Roo/bootstrap/NavHeaderbar.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets, function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })(); /*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass(['hidden','d-none']);
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass(['hidden','d-none']);
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587  * @cfg {String} size ( lg | sm | xs)
588  * @cfg {String} tag ( a | input | submit)
589  * @cfg {String} href empty or href
590  * @cfg {Boolean} disabled default false;
591  * @cfg {Boolean} isClose default false;
592  * @cfg {String} glyphicon depricated - use fa
593  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
594  * @cfg {String} badge text for badge
595  * @cfg {String} theme (default|glow)  
596  * @cfg {Boolean} inverse dark themed version
597  * @cfg {Boolean} toggle is it a slidy toggle button
598  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
599  * @cfg {String} ontext text for on slidy toggle state
600  * @cfg {String} offtext text for off slidy toggle state
601  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
602  * @cfg {Boolean} removeClass remove the standard class..
603  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
604  * 
605  * @constructor
606  * Create a new button
607  * @param {Object} config The config object
608  */
609
610
611 Roo.bootstrap.Button = function(config){
612     Roo.bootstrap.Button.superclass.constructor.call(this, config);
613     this.weightClass = ["btn-default btn-outline-secondary", 
614                        "btn-primary", 
615                        "btn-success", 
616                        "btn-info", 
617                        "btn-warning",
618                        "btn-danger",
619                        "btn-link"
620                       ],  
621     this.addEvents({
622         // raw events
623         /**
624          * @event click
625          * When a butotn is pressed
626          * @param {Roo.bootstrap.Button} btn
627          * @param {Roo.EventObject} e
628          */
629         "click" : true,
630          /**
631          * @event toggle
632          * After the button has been toggles
633          * @param {Roo.bootstrap.Button} btn
634          * @param {Roo.EventObject} e
635          * @param {boolean} pressed (also available as button.pressed)
636          */
637         "toggle" : true
638     });
639 };
640
641 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
642     html: false,
643     active: false,
644     weight: '',
645     badge_weight: '',
646     outline : false,
647     size: '',
648     tag: 'button',
649     href: '',
650     disabled: false,
651     isClose: false,
652     glyphicon: '',
653     fa: '',
654     badge: '',
655     theme: 'default',
656     inverse: false,
657     
658     toggle: false,
659     ontext: 'ON',
660     offtext: 'OFF',
661     defaulton: true,
662     preventDefault: true,
663     removeClass: false,
664     name: false,
665     target: false,
666      
667     pressed : null,
668      
669     
670     getAutoCreate : function(){
671         
672         var cfg = {
673             tag : 'button',
674             cls : 'roo-button',
675             html: ''
676         };
677         
678         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
679             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
680             this.tag = 'button';
681         } else {
682             cfg.tag = this.tag;
683         }
684         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
685         
686         if (this.toggle == true) {
687             cfg={
688                 tag: 'div',
689                 cls: 'slider-frame roo-button',
690                 cn: [
691                     {
692                         tag: 'span',
693                         'data-on-text':'ON',
694                         'data-off-text':'OFF',
695                         cls: 'slider-button',
696                         html: this.offtext
697                     }
698                 ]
699             };
700             
701             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
702                 cfg.cls += ' '+this.weight;
703             }
704             
705             return cfg;
706         }
707         
708         if (this.isClose) {
709             cfg.cls += ' close';
710             
711             cfg["aria-hidden"] = true;
712             
713             cfg.html = "&times;";
714             
715             return cfg;
716         }
717         
718          
719         if (this.theme==='default') {
720             cfg.cls = 'btn roo-button';
721             
722             //if (this.parentType != 'Navbar') {
723             this.weight = this.weight.length ?  this.weight : 'default';
724             //}
725             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
726                 
727                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
728                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
729                 cfg.cls += ' btn-' + outline + weight;
730                 if (this.weight == 'default') {
731                     // BC
732                     cfg.cls += ' btn-' + this.weight;
733                 }
734             }
735         } else if (this.theme==='glow') {
736             
737             cfg.tag = 'a';
738             cfg.cls = 'btn-glow roo-button';
739             
740             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
741                 
742                 cfg.cls += ' ' + this.weight;
743             }
744         }
745    
746         
747         if (this.inverse) {
748             this.cls += ' inverse';
749         }
750         
751         
752         if (this.active || this.pressed === true) {
753             cfg.cls += ' active';
754         }
755         
756         if (this.disabled) {
757             cfg.disabled = 'disabled';
758         }
759         
760         if (this.items) {
761             Roo.log('changing to ul' );
762             cfg.tag = 'ul';
763             this.glyphicon = 'caret';
764             if (Roo.bootstrap.version == 4) {
765                 this.fa = 'caret-down';
766             }
767             
768         }
769         
770         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
771          
772         //gsRoo.log(this.parentType);
773         if (this.parentType === 'Navbar' && !this.parent().bar) {
774             Roo.log('changing to li?');
775             
776             cfg.tag = 'li';
777             
778             cfg.cls = '';
779             cfg.cn =  [{
780                 tag : 'a',
781                 cls : 'roo-button',
782                 html : this.html,
783                 href : this.href || '#'
784             }];
785             if (this.menu) {
786                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
787                 cfg.cls += ' dropdown';
788             }   
789             
790             delete cfg.html;
791             
792         }
793         
794        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
795         
796         if (this.glyphicon) {
797             cfg.html = ' ' + cfg.html;
798             
799             cfg.cn = [
800                 {
801                     tag: 'span',
802                     cls: 'glyphicon glyphicon-' + this.glyphicon
803                 }
804             ];
805         }
806         if (this.fa) {
807             cfg.html = ' ' + cfg.html;
808             
809             cfg.cn = [
810                 {
811                     tag: 'i',
812                     cls: 'fa fas fa-' + this.fa
813                 }
814             ];
815         }
816         
817         if (this.badge) {
818             cfg.html += ' ';
819             
820             cfg.tag = 'a';
821             
822 //            cfg.cls='btn roo-button';
823             
824             cfg.href=this.href;
825             
826             var value = cfg.html;
827             
828             if(this.glyphicon){
829                 value = {
830                     tag: 'span',
831                     cls: 'glyphicon glyphicon-' + this.glyphicon,
832                     html: this.html
833                 };
834             }
835             if(this.fa){
836                 value = {
837                     tag: 'i',
838                     cls: 'fa fas fa-' + this.fa,
839                     html: this.html
840                 };
841             }
842             
843             var bw = this.badge_weight.length ? this.badge_weight :
844                 (this.weight.length ? this.weight : 'secondary');
845             bw = bw == 'default' ? 'secondary' : bw;
846             
847             cfg.cn = [
848                 value,
849                 {
850                     tag: 'span',
851                     cls: 'badge badge-' + bw,
852                     html: this.badge
853                 }
854             ];
855             
856             cfg.html='';
857         }
858         
859         if (this.menu) {
860             cfg.cls += ' dropdown';
861             cfg.html = typeof(cfg.html) != 'undefined' ?
862                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
863         }
864         
865         if (cfg.tag !== 'a' && this.href !== '') {
866             throw "Tag must be a to set href.";
867         } else if (this.href.length > 0) {
868             cfg.href = this.href;
869         }
870         
871         if(this.removeClass){
872             cfg.cls = '';
873         }
874         
875         if(this.target){
876             cfg.target = this.target;
877         }
878         
879         return cfg;
880     },
881     initEvents: function() {
882        // Roo.log('init events?');
883 //        Roo.log(this.el.dom);
884         // add the menu...
885         
886         if (typeof (this.menu) != 'undefined') {
887             this.menu.parentType = this.xtype;
888             this.menu.triggerEl = this.el;
889             this.addxtype(Roo.apply({}, this.menu));
890         }
891
892
893        if (this.el.hasClass('roo-button')) {
894             this.el.on('click', this.onClick, this);
895        } else {
896             this.el.select('.roo-button').on('click', this.onClick, this);
897        }
898        
899        if(this.removeClass){
900            this.el.on('click', this.onClick, this);
901        }
902        
903        this.el.enableDisplayMode();
904         
905     },
906     onClick : function(e)
907     {
908         if (this.disabled) {
909             return;
910         }
911         
912         Roo.log('button on click ');
913         if(this.preventDefault){
914             e.preventDefault();
915         }
916         
917         if (this.pressed === true || this.pressed === false) {
918             this.toggleActive(e);
919         }
920         
921         
922         this.fireEvent('click', this, e);
923     },
924     
925     /**
926      * Enables this button
927      */
928     enable : function()
929     {
930         this.disabled = false;
931         this.el.removeClass('disabled');
932     },
933     
934     /**
935      * Disable this button
936      */
937     disable : function()
938     {
939         this.disabled = true;
940         this.el.addClass('disabled');
941     },
942      /**
943      * sets the active state on/off, 
944      * @param {Boolean} state (optional) Force a particular state
945      */
946     setActive : function(v) {
947         
948         this.el[v ? 'addClass' : 'removeClass']('active');
949         this.pressed = v;
950     },
951      /**
952      * toggles the current active state 
953      */
954     toggleActive : function(e)
955     {
956         this.setActive(!this.pressed);
957         this.fireEvent('toggle', this, e, !this.pressed);
958     },
959      /**
960      * get the current active state
961      * @return {boolean} true if it's active
962      */
963     isActive : function()
964     {
965         return this.el.hasClass('active');
966     },
967     /**
968      * set the text of the first selected button
969      */
970     setText : function(str)
971     {
972         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
973     },
974     /**
975      * get the text of the first selected button
976      */
977     getText : function()
978     {
979         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
980     },
981     
982     setWeight : function(str)
983     {
984         this.el.removeClass(this.weightClass);
985         this.weight = str;
986         var outline = this.outline ? 'outline-' : '';
987         if (str == 'default') {
988             this.el.addClass('btn-default btn-outline-secondary');        
989             return;
990         }
991         this.el.addClass('btn-' + outline + str);        
992     }
993     
994     
995 });
996
997  /*
998  * - LGPL
999  *
1000  * column
1001  * 
1002  */
1003
1004 /**
1005  * @class Roo.bootstrap.Column
1006  * @extends Roo.bootstrap.Component
1007  * Bootstrap Column class
1008  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1009  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1010  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1011  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1012  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1013  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1014  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1015  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1016  *
1017  * 
1018  * @cfg {Boolean} hidden (true|false) hide the element
1019  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1020  * @cfg {String} fa (ban|check|...) font awesome icon
1021  * @cfg {Number} fasize (1|2|....) font awsome size
1022
1023  * @cfg {String} icon (info-sign|check|...) glyphicon name
1024
1025  * @cfg {String} html content of column.
1026  * 
1027  * @constructor
1028  * Create a new Column
1029  * @param {Object} config The config object
1030  */
1031
1032 Roo.bootstrap.Column = function(config){
1033     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1034 };
1035
1036 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1037     
1038     xs: false,
1039     sm: false,
1040     md: false,
1041     lg: false,
1042     xsoff: false,
1043     smoff: false,
1044     mdoff: false,
1045     lgoff: false,
1046     html: '',
1047     offset: 0,
1048     alert: false,
1049     fa: false,
1050     icon : false,
1051     hidden : false,
1052     fasize : 1,
1053     
1054     getAutoCreate : function(){
1055         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1056         
1057         cfg = {
1058             tag: 'div',
1059             cls: 'column'
1060         };
1061         
1062         var settings=this;
1063         ['xs','sm','md','lg'].map(function(size){
1064             //Roo.log( size + ':' + settings[size]);
1065             
1066             if (settings[size+'off'] !== false) {
1067                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1068             }
1069             
1070             if (settings[size] === false) {
1071                 return;
1072             }
1073             
1074             if (!settings[size]) { // 0 = hidden
1075                 cfg.cls += ' hidden-' + size + ' hidden' + size + '-down';;
1076                 return;
1077             }
1078             cfg.cls += ' col-' + size + '-' + settings[size] + (
1079                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1080             );
1081             
1082         });
1083         
1084         if (this.hidden) {
1085             cfg.cls += ' hidden';
1086         }
1087         
1088         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1089             cfg.cls +=' alert alert-' + this.alert;
1090         }
1091         
1092         
1093         if (this.html.length) {
1094             cfg.html = this.html;
1095         }
1096         if (this.fa) {
1097             var fasize = '';
1098             if (this.fasize > 1) {
1099                 fasize = ' fa-' + this.fasize + 'x';
1100             }
1101             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1102             
1103             
1104         }
1105         if (this.icon) {
1106             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1107         }
1108         
1109         return cfg;
1110     }
1111    
1112 });
1113
1114  
1115
1116  /*
1117  * - LGPL
1118  *
1119  * page container.
1120  * 
1121  */
1122
1123
1124 /**
1125  * @class Roo.bootstrap.Container
1126  * @extends Roo.bootstrap.Component
1127  * Bootstrap Container class
1128  * @cfg {Boolean} jumbotron is it a jumbotron element
1129  * @cfg {String} html content of element
1130  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1131  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1132  * @cfg {String} header content of header (for panel)
1133  * @cfg {String} footer content of footer (for panel)
1134  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1135  * @cfg {String} tag (header|aside|section) type of HTML tag.
1136  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1137  * @cfg {String} fa font awesome icon
1138  * @cfg {String} icon (info-sign|check|...) glyphicon name
1139  * @cfg {Boolean} hidden (true|false) hide the element
1140  * @cfg {Boolean} expandable (true|false) default false
1141  * @cfg {Boolean} expanded (true|false) default true
1142  * @cfg {String} rheader contet on the right of header
1143  * @cfg {Boolean} clickable (true|false) default false
1144
1145  *     
1146  * @constructor
1147  * Create a new Container
1148  * @param {Object} config The config object
1149  */
1150
1151 Roo.bootstrap.Container = function(config){
1152     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1153     
1154     this.addEvents({
1155         // raw events
1156          /**
1157          * @event expand
1158          * After the panel has been expand
1159          * 
1160          * @param {Roo.bootstrap.Container} this
1161          */
1162         "expand" : true,
1163         /**
1164          * @event collapse
1165          * After the panel has been collapsed
1166          * 
1167          * @param {Roo.bootstrap.Container} this
1168          */
1169         "collapse" : true,
1170         /**
1171          * @event click
1172          * When a element is chick
1173          * @param {Roo.bootstrap.Container} this
1174          * @param {Roo.EventObject} e
1175          */
1176         "click" : true
1177     });
1178 };
1179
1180 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1181     
1182     jumbotron : false,
1183     well: '',
1184     panel : '',
1185     header: '',
1186     footer : '',
1187     sticky: '',
1188     tag : false,
1189     alert : false,
1190     fa: false,
1191     icon : false,
1192     expandable : false,
1193     rheader : '',
1194     expanded : true,
1195     clickable: false,
1196   
1197      
1198     getChildContainer : function() {
1199         
1200         if(!this.el){
1201             return false;
1202         }
1203         
1204         if (this.panel.length) {
1205             return this.el.select('.panel-body',true).first();
1206         }
1207         
1208         return this.el;
1209     },
1210     
1211     
1212     getAutoCreate : function(){
1213         
1214         var cfg = {
1215             tag : this.tag || 'div',
1216             html : '',
1217             cls : ''
1218         };
1219         if (this.jumbotron) {
1220             cfg.cls = 'jumbotron';
1221         }
1222         
1223         
1224         
1225         // - this is applied by the parent..
1226         //if (this.cls) {
1227         //    cfg.cls = this.cls + '';
1228         //}
1229         
1230         if (this.sticky.length) {
1231             
1232             var bd = Roo.get(document.body);
1233             if (!bd.hasClass('bootstrap-sticky')) {
1234                 bd.addClass('bootstrap-sticky');
1235                 Roo.select('html',true).setStyle('height', '100%');
1236             }
1237              
1238             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1239         }
1240         
1241         
1242         if (this.well.length) {
1243             switch (this.well) {
1244                 case 'lg':
1245                 case 'sm':
1246                     cfg.cls +=' well well-' +this.well;
1247                     break;
1248                 default:
1249                     cfg.cls +=' well';
1250                     break;
1251             }
1252         }
1253         
1254         if (this.hidden) {
1255             cfg.cls += ' hidden';
1256         }
1257         
1258         
1259         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1260             cfg.cls +=' alert alert-' + this.alert;
1261         }
1262         
1263         var body = cfg;
1264         
1265         if (this.panel.length) {
1266             cfg.cls += ' panel panel-' + this.panel;
1267             cfg.cn = [];
1268             if (this.header.length) {
1269                 
1270                 var h = [];
1271                 
1272                 if(this.expandable){
1273                     
1274                     cfg.cls = cfg.cls + ' expandable';
1275                     
1276                     h.push({
1277                         tag: 'i',
1278                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1279                     });
1280                     
1281                 }
1282                 
1283                 h.push(
1284                     {
1285                         tag: 'span',
1286                         cls : 'panel-title',
1287                         html : (this.expandable ? '&nbsp;' : '') + this.header
1288                     },
1289                     {
1290                         tag: 'span',
1291                         cls: 'panel-header-right',
1292                         html: this.rheader
1293                     }
1294                 );
1295                 
1296                 cfg.cn.push({
1297                     cls : 'panel-heading',
1298                     style : this.expandable ? 'cursor: pointer' : '',
1299                     cn : h
1300                 });
1301                 
1302             }
1303             
1304             body = false;
1305             cfg.cn.push({
1306                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1307                 html : this.html
1308             });
1309             
1310             
1311             if (this.footer.length) {
1312                 cfg.cn.push({
1313                     cls : 'panel-footer',
1314                     html : this.footer
1315                     
1316                 });
1317             }
1318             
1319         }
1320         
1321         if (body) {
1322             body.html = this.html || cfg.html;
1323             // prefix with the icons..
1324             if (this.fa) {
1325                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1326             }
1327             if (this.icon) {
1328                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1329             }
1330             
1331             
1332         }
1333         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1334             cfg.cls =  'container';
1335         }
1336         
1337         return cfg;
1338     },
1339     
1340     initEvents: function() 
1341     {
1342         if(this.expandable){
1343             var headerEl = this.headerEl();
1344         
1345             if(headerEl){
1346                 headerEl.on('click', this.onToggleClick, this);
1347             }
1348         }
1349         
1350         if(this.clickable){
1351             this.el.on('click', this.onClick, this);
1352         }
1353         
1354     },
1355     
1356     onToggleClick : function()
1357     {
1358         var headerEl = this.headerEl();
1359         
1360         if(!headerEl){
1361             return;
1362         }
1363         
1364         if(this.expanded){
1365             this.collapse();
1366             return;
1367         }
1368         
1369         this.expand();
1370     },
1371     
1372     expand : function()
1373     {
1374         if(this.fireEvent('expand', this)) {
1375             
1376             this.expanded = true;
1377             
1378             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1379             
1380             this.el.select('.panel-body',true).first().removeClass('hide');
1381             
1382             var toggleEl = this.toggleEl();
1383
1384             if(!toggleEl){
1385                 return;
1386             }
1387
1388             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1389         }
1390         
1391     },
1392     
1393     collapse : function()
1394     {
1395         if(this.fireEvent('collapse', this)) {
1396             
1397             this.expanded = false;
1398             
1399             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1400             this.el.select('.panel-body',true).first().addClass('hide');
1401         
1402             var toggleEl = this.toggleEl();
1403
1404             if(!toggleEl){
1405                 return;
1406             }
1407
1408             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1409         }
1410     },
1411     
1412     toggleEl : function()
1413     {
1414         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1415             return;
1416         }
1417         
1418         return this.el.select('.panel-heading .fa',true).first();
1419     },
1420     
1421     headerEl : function()
1422     {
1423         if(!this.el || !this.panel.length || !this.header.length){
1424             return;
1425         }
1426         
1427         return this.el.select('.panel-heading',true).first()
1428     },
1429     
1430     bodyEl : function()
1431     {
1432         if(!this.el || !this.panel.length){
1433             return;
1434         }
1435         
1436         return this.el.select('.panel-body',true).first()
1437     },
1438     
1439     titleEl : function()
1440     {
1441         if(!this.el || !this.panel.length || !this.header.length){
1442             return;
1443         }
1444         
1445         return this.el.select('.panel-title',true).first();
1446     },
1447     
1448     setTitle : function(v)
1449     {
1450         var titleEl = this.titleEl();
1451         
1452         if(!titleEl){
1453             return;
1454         }
1455         
1456         titleEl.dom.innerHTML = v;
1457     },
1458     
1459     getTitle : function()
1460     {
1461         
1462         var titleEl = this.titleEl();
1463         
1464         if(!titleEl){
1465             return '';
1466         }
1467         
1468         return titleEl.dom.innerHTML;
1469     },
1470     
1471     setRightTitle : function(v)
1472     {
1473         var t = this.el.select('.panel-header-right',true).first();
1474         
1475         if(!t){
1476             return;
1477         }
1478         
1479         t.dom.innerHTML = v;
1480     },
1481     
1482     onClick : function(e)
1483     {
1484         e.preventDefault();
1485         
1486         this.fireEvent('click', this, e);
1487     }
1488 });
1489
1490  /*
1491  * - LGPL
1492  *
1493  * image
1494  * 
1495  */
1496
1497
1498 /**
1499  * @class Roo.bootstrap.Img
1500  * @extends Roo.bootstrap.Component
1501  * Bootstrap Img class
1502  * @cfg {Boolean} imgResponsive false | true
1503  * @cfg {String} border rounded | circle | thumbnail
1504  * @cfg {String} src image source
1505  * @cfg {String} alt image alternative text
1506  * @cfg {String} href a tag href
1507  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1508  * @cfg {String} xsUrl xs image source
1509  * @cfg {String} smUrl sm image source
1510  * @cfg {String} mdUrl md image source
1511  * @cfg {String} lgUrl lg image source
1512  * 
1513  * @constructor
1514  * Create a new Input
1515  * @param {Object} config The config object
1516  */
1517
1518 Roo.bootstrap.Img = function(config){
1519     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1520     
1521     this.addEvents({
1522         // img events
1523         /**
1524          * @event click
1525          * The img click event for the img.
1526          * @param {Roo.EventObject} e
1527          */
1528         "click" : true
1529     });
1530 };
1531
1532 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1533     
1534     imgResponsive: true,
1535     border: '',
1536     src: 'about:blank',
1537     href: false,
1538     target: false,
1539     xsUrl: '',
1540     smUrl: '',
1541     mdUrl: '',
1542     lgUrl: '',
1543
1544     getAutoCreate : function()
1545     {   
1546         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1547             return this.createSingleImg();
1548         }
1549         
1550         var cfg = {
1551             tag: 'div',
1552             cls: 'roo-image-responsive-group',
1553             cn: []
1554         };
1555         var _this = this;
1556         
1557         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1558             
1559             if(!_this[size + 'Url']){
1560                 return;
1561             }
1562             
1563             var img = {
1564                 tag: 'img',
1565                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1566                 html: _this.html || cfg.html,
1567                 src: _this[size + 'Url']
1568             };
1569             
1570             img.cls += ' roo-image-responsive-' + size;
1571             
1572             var s = ['xs', 'sm', 'md', 'lg'];
1573             
1574             s.splice(s.indexOf(size), 1);
1575             
1576             Roo.each(s, function(ss){
1577                 img.cls += ' hidden-' + ss;
1578             });
1579             
1580             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1581                 cfg.cls += ' img-' + _this.border;
1582             }
1583             
1584             if(_this.alt){
1585                 cfg.alt = _this.alt;
1586             }
1587             
1588             if(_this.href){
1589                 var a = {
1590                     tag: 'a',
1591                     href: _this.href,
1592                     cn: [
1593                         img
1594                     ]
1595                 };
1596
1597                 if(this.target){
1598                     a.target = _this.target;
1599                 }
1600             }
1601             
1602             cfg.cn.push((_this.href) ? a : img);
1603             
1604         });
1605         
1606         return cfg;
1607     },
1608     
1609     createSingleImg : function()
1610     {
1611         var cfg = {
1612             tag: 'img',
1613             cls: (this.imgResponsive) ? 'img-responsive' : '',
1614             html : null,
1615             src : 'about:blank'  // just incase src get's set to undefined?!?
1616         };
1617         
1618         cfg.html = this.html || cfg.html;
1619         
1620         cfg.src = this.src || cfg.src;
1621         
1622         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1623             cfg.cls += ' img-' + this.border;
1624         }
1625         
1626         if(this.alt){
1627             cfg.alt = this.alt;
1628         }
1629         
1630         if(this.href){
1631             var a = {
1632                 tag: 'a',
1633                 href: this.href,
1634                 cn: [
1635                     cfg
1636                 ]
1637             };
1638             
1639             if(this.target){
1640                 a.target = this.target;
1641             }
1642             
1643         }
1644         
1645         return (this.href) ? a : cfg;
1646     },
1647     
1648     initEvents: function() 
1649     {
1650         if(!this.href){
1651             this.el.on('click', this.onClick, this);
1652         }
1653         
1654     },
1655     
1656     onClick : function(e)
1657     {
1658         Roo.log('img onclick');
1659         this.fireEvent('click', this, e);
1660     },
1661     /**
1662      * Sets the url of the image - used to update it
1663      * @param {String} url the url of the image
1664      */
1665     
1666     setSrc : function(url)
1667     {
1668         this.src =  url;
1669         
1670         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1671             this.el.dom.src =  url;
1672             return;
1673         }
1674         
1675         this.el.select('img', true).first().dom.src =  url;
1676     }
1677     
1678     
1679    
1680 });
1681
1682  /*
1683  * - LGPL
1684  *
1685  * image
1686  * 
1687  */
1688
1689
1690 /**
1691  * @class Roo.bootstrap.Link
1692  * @extends Roo.bootstrap.Component
1693  * Bootstrap Link Class
1694  * @cfg {String} alt image alternative text
1695  * @cfg {String} href a tag href
1696  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1697  * @cfg {String} html the content of the link.
1698  * @cfg {String} anchor name for the anchor link
1699  * @cfg {String} fa - favicon
1700
1701  * @cfg {Boolean} preventDefault (true | false) default false
1702
1703  * 
1704  * @constructor
1705  * Create a new Input
1706  * @param {Object} config The config object
1707  */
1708
1709 Roo.bootstrap.Link = function(config){
1710     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1711     
1712     this.addEvents({
1713         // img events
1714         /**
1715          * @event click
1716          * The img click event for the img.
1717          * @param {Roo.EventObject} e
1718          */
1719         "click" : true
1720     });
1721 };
1722
1723 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1724     
1725     href: false,
1726     target: false,
1727     preventDefault: false,
1728     anchor : false,
1729     alt : false,
1730     fa: false,
1731
1732
1733     getAutoCreate : function()
1734     {
1735         var html = this.html || '';
1736         
1737         if (this.fa !== false) {
1738             html = '<i class="fa fa-' + this.fa + '"></i>';
1739         }
1740         var cfg = {
1741             tag: 'a'
1742         };
1743         // anchor's do not require html/href...
1744         if (this.anchor === false) {
1745             cfg.html = html;
1746             cfg.href = this.href || '#';
1747         } else {
1748             cfg.name = this.anchor;
1749             if (this.html !== false || this.fa !== false) {
1750                 cfg.html = html;
1751             }
1752             if (this.href !== false) {
1753                 cfg.href = this.href;
1754             }
1755         }
1756         
1757         if(this.alt !== false){
1758             cfg.alt = this.alt;
1759         }
1760         
1761         
1762         if(this.target !== false) {
1763             cfg.target = this.target;
1764         }
1765         
1766         return cfg;
1767     },
1768     
1769     initEvents: function() {
1770         
1771         if(!this.href || this.preventDefault){
1772             this.el.on('click', this.onClick, this);
1773         }
1774     },
1775     
1776     onClick : function(e)
1777     {
1778         if(this.preventDefault){
1779             e.preventDefault();
1780         }
1781         //Roo.log('img onclick');
1782         this.fireEvent('click', this, e);
1783     }
1784    
1785 });
1786
1787  /*
1788  * - LGPL
1789  *
1790  * header
1791  * 
1792  */
1793
1794 /**
1795  * @class Roo.bootstrap.Header
1796  * @extends Roo.bootstrap.Component
1797  * Bootstrap Header class
1798  * @cfg {String} html content of header
1799  * @cfg {Number} level (1|2|3|4|5|6) default 1
1800  * 
1801  * @constructor
1802  * Create a new Header
1803  * @param {Object} config The config object
1804  */
1805
1806
1807 Roo.bootstrap.Header  = function(config){
1808     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1809 };
1810
1811 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1812     
1813     //href : false,
1814     html : false,
1815     level : 1,
1816     
1817     
1818     
1819     getAutoCreate : function(){
1820         
1821         
1822         
1823         var cfg = {
1824             tag: 'h' + (1 *this.level),
1825             html: this.html || ''
1826         } ;
1827         
1828         return cfg;
1829     }
1830    
1831 });
1832
1833  
1834
1835  /*
1836  * Based on:
1837  * Ext JS Library 1.1.1
1838  * Copyright(c) 2006-2007, Ext JS, LLC.
1839  *
1840  * Originally Released Under LGPL - original licence link has changed is not relivant.
1841  *
1842  * Fork - LGPL
1843  * <script type="text/javascript">
1844  */
1845  
1846 /**
1847  * @class Roo.bootstrap.MenuMgr
1848  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1849  * @singleton
1850  */
1851 Roo.bootstrap.MenuMgr = function(){
1852    var menus, active, groups = {}, attached = false, lastShow = new Date();
1853
1854    // private - called when first menu is created
1855    function init(){
1856        menus = {};
1857        active = new Roo.util.MixedCollection();
1858        Roo.get(document).addKeyListener(27, function(){
1859            if(active.length > 0){
1860                hideAll();
1861            }
1862        });
1863    }
1864
1865    // private
1866    function hideAll(){
1867        if(active && active.length > 0){
1868            var c = active.clone();
1869            c.each(function(m){
1870                m.hide();
1871            });
1872        }
1873    }
1874
1875    // private
1876    function onHide(m){
1877        active.remove(m);
1878        if(active.length < 1){
1879            Roo.get(document).un("mouseup", onMouseDown);
1880             
1881            attached = false;
1882        }
1883    }
1884
1885    // private
1886    function onShow(m){
1887        var last = active.last();
1888        lastShow = new Date();
1889        active.add(m);
1890        if(!attached){
1891           Roo.get(document).on("mouseup", onMouseDown);
1892            
1893            attached = true;
1894        }
1895        if(m.parentMenu){
1896           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1897           m.parentMenu.activeChild = m;
1898        }else if(last && last.isVisible()){
1899           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1900        }
1901    }
1902
1903    // private
1904    function onBeforeHide(m){
1905        if(m.activeChild){
1906            m.activeChild.hide();
1907        }
1908        if(m.autoHideTimer){
1909            clearTimeout(m.autoHideTimer);
1910            delete m.autoHideTimer;
1911        }
1912    }
1913
1914    // private
1915    function onBeforeShow(m){
1916        var pm = m.parentMenu;
1917        if(!pm && !m.allowOtherMenus){
1918            hideAll();
1919        }else if(pm && pm.activeChild && active != m){
1920            pm.activeChild.hide();
1921        }
1922    }
1923
1924    // private this should really trigger on mouseup..
1925    function onMouseDown(e){
1926         Roo.log("on Mouse Up");
1927         
1928         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1929             Roo.log("MenuManager hideAll");
1930             hideAll();
1931             e.stopEvent();
1932         }
1933         
1934         
1935    }
1936
1937    // private
1938    function onBeforeCheck(mi, state){
1939        if(state){
1940            var g = groups[mi.group];
1941            for(var i = 0, l = g.length; i < l; i++){
1942                if(g[i] != mi){
1943                    g[i].setChecked(false);
1944                }
1945            }
1946        }
1947    }
1948
1949    return {
1950
1951        /**
1952         * Hides all menus that are currently visible
1953         */
1954        hideAll : function(){
1955             hideAll();  
1956        },
1957
1958        // private
1959        register : function(menu){
1960            if(!menus){
1961                init();
1962            }
1963            menus[menu.id] = menu;
1964            menu.on("beforehide", onBeforeHide);
1965            menu.on("hide", onHide);
1966            menu.on("beforeshow", onBeforeShow);
1967            menu.on("show", onShow);
1968            var g = menu.group;
1969            if(g && menu.events["checkchange"]){
1970                if(!groups[g]){
1971                    groups[g] = [];
1972                }
1973                groups[g].push(menu);
1974                menu.on("checkchange", onCheck);
1975            }
1976        },
1977
1978         /**
1979          * Returns a {@link Roo.menu.Menu} object
1980          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1981          * be used to generate and return a new Menu instance.
1982          */
1983        get : function(menu){
1984            if(typeof menu == "string"){ // menu id
1985                return menus[menu];
1986            }else if(menu.events){  // menu instance
1987                return menu;
1988            }
1989            /*else if(typeof menu.length == 'number'){ // array of menu items?
1990                return new Roo.bootstrap.Menu({items:menu});
1991            }else{ // otherwise, must be a config
1992                return new Roo.bootstrap.Menu(menu);
1993            }
1994            */
1995            return false;
1996        },
1997
1998        // private
1999        unregister : function(menu){
2000            delete menus[menu.id];
2001            menu.un("beforehide", onBeforeHide);
2002            menu.un("hide", onHide);
2003            menu.un("beforeshow", onBeforeShow);
2004            menu.un("show", onShow);
2005            var g = menu.group;
2006            if(g && menu.events["checkchange"]){
2007                groups[g].remove(menu);
2008                menu.un("checkchange", onCheck);
2009            }
2010        },
2011
2012        // private
2013        registerCheckable : function(menuItem){
2014            var g = menuItem.group;
2015            if(g){
2016                if(!groups[g]){
2017                    groups[g] = [];
2018                }
2019                groups[g].push(menuItem);
2020                menuItem.on("beforecheckchange", onBeforeCheck);
2021            }
2022        },
2023
2024        // private
2025        unregisterCheckable : function(menuItem){
2026            var g = menuItem.group;
2027            if(g){
2028                groups[g].remove(menuItem);
2029                menuItem.un("beforecheckchange", onBeforeCheck);
2030            }
2031        }
2032    };
2033 }();/*
2034  * - LGPL
2035  *
2036  * menu
2037  * 
2038  */
2039
2040 /**
2041  * @class Roo.bootstrap.Menu
2042  * @extends Roo.bootstrap.Component
2043  * Bootstrap Menu class - container for MenuItems
2044  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2045  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2046  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2047  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2048  * 
2049  * @constructor
2050  * Create a new Menu
2051  * @param {Object} config The config object
2052  */
2053
2054
2055 Roo.bootstrap.Menu = function(config){
2056     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2057     if (this.registerMenu && this.type != 'treeview')  {
2058         Roo.bootstrap.MenuMgr.register(this);
2059     }
2060     
2061     
2062     this.addEvents({
2063         /**
2064          * @event beforeshow
2065          * Fires before this menu is displayed (return false to block)
2066          * @param {Roo.menu.Menu} this
2067          */
2068         beforeshow : true,
2069         /**
2070          * @event beforehide
2071          * Fires before this menu is hidden (return false to block)
2072          * @param {Roo.menu.Menu} this
2073          */
2074         beforehide : true,
2075         /**
2076          * @event show
2077          * Fires after this menu is displayed
2078          * @param {Roo.menu.Menu} this
2079          */
2080         show : true,
2081         /**
2082          * @event hide
2083          * Fires after this menu is hidden
2084          * @param {Roo.menu.Menu} this
2085          */
2086         hide : true,
2087         /**
2088          * @event click
2089          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2090          * @param {Roo.menu.Menu} this
2091          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2092          * @param {Roo.EventObject} e
2093          */
2094         click : true,
2095         /**
2096          * @event mouseover
2097          * Fires when the mouse is hovering over this menu
2098          * @param {Roo.menu.Menu} this
2099          * @param {Roo.EventObject} e
2100          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2101          */
2102         mouseover : true,
2103         /**
2104          * @event mouseout
2105          * Fires when the mouse exits this menu
2106          * @param {Roo.menu.Menu} this
2107          * @param {Roo.EventObject} e
2108          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2109          */
2110         mouseout : true,
2111         /**
2112          * @event itemclick
2113          * Fires when a menu item contained in this menu is clicked
2114          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2115          * @param {Roo.EventObject} e
2116          */
2117         itemclick: true
2118     });
2119     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2120 };
2121
2122 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2123     
2124    /// html : false,
2125     //align : '',
2126     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2127     type: false,
2128     /**
2129      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2130      */
2131     registerMenu : true,
2132     
2133     menuItems :false, // stores the menu items..
2134     
2135     hidden:true,
2136         
2137     parentMenu : false,
2138     
2139     stopEvent : true,
2140     
2141     isLink : false,
2142     
2143     getChildContainer : function() {
2144         return this.el;  
2145     },
2146     
2147     getAutoCreate : function(){
2148          
2149         //if (['right'].indexOf(this.align)!==-1) {
2150         //    cfg.cn[1].cls += ' pull-right'
2151         //}
2152         
2153         
2154         var cfg = {
2155             tag : 'ul',
2156             cls : 'dropdown-menu' ,
2157             style : 'z-index:1000'
2158             
2159         };
2160         
2161         if (this.type === 'submenu') {
2162             cfg.cls = 'submenu active';
2163         }
2164         if (this.type === 'treeview') {
2165             cfg.cls = 'treeview-menu';
2166         }
2167         
2168         return cfg;
2169     },
2170     initEvents : function() {
2171         
2172        // Roo.log("ADD event");
2173        // Roo.log(this.triggerEl.dom);
2174         
2175         this.triggerEl.on('click', this.onTriggerClick, this);
2176         
2177         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2178         
2179         
2180         if (this.triggerEl.hasClass('nav-item')) {
2181             // dropdown toggle on the 'a' in BS4?
2182             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2183         } else {
2184             this.triggerEl.addClass('dropdown-toggle');
2185         }
2186         if (Roo.isTouch) {
2187             this.el.on('touchstart'  , this.onTouch, this);
2188         }
2189         this.el.on('click' , this.onClick, this);
2190
2191         this.el.on("mouseover", this.onMouseOver, this);
2192         this.el.on("mouseout", this.onMouseOut, this);
2193         
2194     },
2195     
2196     findTargetItem : function(e)
2197     {
2198         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2199         if(!t){
2200             return false;
2201         }
2202         //Roo.log(t);         Roo.log(t.id);
2203         if(t && t.id){
2204             //Roo.log(this.menuitems);
2205             return this.menuitems.get(t.id);
2206             
2207             //return this.items.get(t.menuItemId);
2208         }
2209         
2210         return false;
2211     },
2212     
2213     onTouch : function(e) 
2214     {
2215         Roo.log("menu.onTouch");
2216         //e.stopEvent(); this make the user popdown broken
2217         this.onClick(e);
2218     },
2219     
2220     onClick : function(e)
2221     {
2222         Roo.log("menu.onClick");
2223         
2224         var t = this.findTargetItem(e);
2225         if(!t || t.isContainer){
2226             return;
2227         }
2228         Roo.log(e);
2229         /*
2230         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2231             if(t == this.activeItem && t.shouldDeactivate(e)){
2232                 this.activeItem.deactivate();
2233                 delete this.activeItem;
2234                 return;
2235             }
2236             if(t.canActivate){
2237                 this.setActiveItem(t, true);
2238             }
2239             return;
2240             
2241             
2242         }
2243         */
2244        
2245         Roo.log('pass click event');
2246         
2247         t.onClick(e);
2248         
2249         this.fireEvent("click", this, t, e);
2250         
2251         var _this = this;
2252         
2253         if(!t.href.length || t.href == '#'){
2254             (function() { _this.hide(); }).defer(100);
2255         }
2256         
2257     },
2258     
2259     onMouseOver : function(e){
2260         var t  = this.findTargetItem(e);
2261         //Roo.log(t);
2262         //if(t){
2263         //    if(t.canActivate && !t.disabled){
2264         //        this.setActiveItem(t, true);
2265         //    }
2266         //}
2267         
2268         this.fireEvent("mouseover", this, e, t);
2269     },
2270     isVisible : function(){
2271         return !this.hidden;
2272     },
2273     onMouseOut : function(e){
2274         var t  = this.findTargetItem(e);
2275         
2276         //if(t ){
2277         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2278         //        this.activeItem.deactivate();
2279         //        delete this.activeItem;
2280         //    }
2281         //}
2282         this.fireEvent("mouseout", this, e, t);
2283     },
2284     
2285     
2286     /**
2287      * Displays this menu relative to another element
2288      * @param {String/HTMLElement/Roo.Element} element The element to align to
2289      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2290      * the element (defaults to this.defaultAlign)
2291      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2292      */
2293     show : function(el, pos, parentMenu)
2294     {
2295         if (false === this.fireEvent("beforeshow", this)) {
2296             Roo.log("show canceled");
2297             return;
2298         }
2299         this.parentMenu = parentMenu;
2300         if(!this.el){
2301             this.render();
2302         }
2303         
2304         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2305     },
2306      /**
2307      * Displays this menu at a specific xy position
2308      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2309      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2310      */
2311     showAt : function(xy, parentMenu, /* private: */_e){
2312         this.parentMenu = parentMenu;
2313         if(!this.el){
2314             this.render();
2315         }
2316         if(_e !== false){
2317             this.fireEvent("beforeshow", this);
2318             //xy = this.el.adjustForConstraints(xy);
2319         }
2320         
2321         //this.el.show();
2322         this.hideMenuItems();
2323         this.hidden = false;
2324         this.triggerEl.addClass('open');
2325         this.el.addClass('show');
2326         
2327         // reassign x when hitting right
2328         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2329             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2330         }
2331         
2332         // reassign y when hitting bottom
2333         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2334             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2335         }
2336         
2337         // but the list may align on trigger left or trigger top... should it be a properity?
2338         
2339         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2340             this.el.setXY(xy);
2341         }
2342         
2343         this.focus();
2344         this.fireEvent("show", this);
2345     },
2346     
2347     focus : function(){
2348         return;
2349         if(!this.hidden){
2350             this.doFocus.defer(50, this);
2351         }
2352     },
2353
2354     doFocus : function(){
2355         if(!this.hidden){
2356             this.focusEl.focus();
2357         }
2358     },
2359
2360     /**
2361      * Hides this menu and optionally all parent menus
2362      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2363      */
2364     hide : function(deep)
2365     {
2366         if (false === this.fireEvent("beforehide", this)) {
2367             Roo.log("hide canceled");
2368             return;
2369         }
2370         this.hideMenuItems();
2371         if(this.el && this.isVisible()){
2372            
2373             if(this.activeItem){
2374                 this.activeItem.deactivate();
2375                 this.activeItem = null;
2376             }
2377             this.triggerEl.removeClass('open');;
2378             this.el.removeClass('show');
2379             this.hidden = true;
2380             this.fireEvent("hide", this);
2381         }
2382         if(deep === true && this.parentMenu){
2383             this.parentMenu.hide(true);
2384         }
2385     },
2386     
2387     onTriggerClick : function(e)
2388     {
2389         Roo.log('trigger click');
2390         
2391         var target = e.getTarget();
2392         
2393         Roo.log(target.nodeName.toLowerCase());
2394         
2395         if(target.nodeName.toLowerCase() === 'i'){
2396             e.preventDefault();
2397         }
2398         
2399     },
2400     
2401     onTriggerPress  : function(e)
2402     {
2403         Roo.log('trigger press');
2404         //Roo.log(e.getTarget());
2405        // Roo.log(this.triggerEl.dom);
2406        
2407         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2408         var pel = Roo.get(e.getTarget());
2409         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2410             Roo.log('is treeview or dropdown?');
2411             return;
2412         }
2413         
2414         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2415             return;
2416         }
2417         
2418         if (this.isVisible()) {
2419             Roo.log('hide');
2420             this.hide();
2421         } else {
2422             Roo.log('show');
2423             this.show(this.triggerEl, '?', false);
2424         }
2425         
2426         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2427             e.stopEvent();
2428         }
2429         
2430     },
2431        
2432     
2433     hideMenuItems : function()
2434     {
2435         Roo.log("hide Menu Items");
2436         if (!this.el) { 
2437             return;
2438         }
2439         
2440         this.el.select('.open',true).each(function(aa) {
2441             
2442             aa.removeClass('open');
2443          
2444         });
2445     },
2446     addxtypeChild : function (tree, cntr) {
2447         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2448           
2449         this.menuitems.add(comp);
2450         return comp;
2451
2452     },
2453     getEl : function()
2454     {
2455         Roo.log(this.el);
2456         return this.el;
2457     },
2458     
2459     clear : function()
2460     {
2461         this.getEl().dom.innerHTML = '';
2462         this.menuitems.clear();
2463     }
2464 });
2465
2466  
2467  /*
2468  * - LGPL
2469  *
2470  * menu item
2471  * 
2472  */
2473
2474
2475 /**
2476  * @class Roo.bootstrap.MenuItem
2477  * @extends Roo.bootstrap.Component
2478  * Bootstrap MenuItem class
2479  * @cfg {String} html the menu label
2480  * @cfg {String} href the link
2481  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2482  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2483  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2484  * @cfg {String} fa favicon to show on left of menu item.
2485  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2486  * 
2487  * 
2488  * @constructor
2489  * Create a new MenuItem
2490  * @param {Object} config The config object
2491  */
2492
2493
2494 Roo.bootstrap.MenuItem = function(config){
2495     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2496     this.addEvents({
2497         // raw events
2498         /**
2499          * @event click
2500          * The raw click event for the entire grid.
2501          * @param {Roo.bootstrap.MenuItem} this
2502          * @param {Roo.EventObject} e
2503          */
2504         "click" : true
2505     });
2506 };
2507
2508 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2509     
2510     href : false,
2511     html : false,
2512     preventDefault: false,
2513     isContainer : false,
2514     active : false,
2515     fa: false,
2516     
2517     getAutoCreate : function(){
2518         
2519         if(this.isContainer){
2520             return {
2521                 tag: 'li',
2522                 cls: 'dropdown-menu-item '
2523             };
2524         }
2525         var ctag = {
2526             tag: 'span',
2527             html: 'Link'
2528         };
2529         
2530         var anc = {
2531             tag : 'a',
2532             cls : 'dropdown-item',
2533             href : '#',
2534             cn : [  ]
2535         };
2536         
2537         if (this.fa !== false) {
2538             anc.cn.push({
2539                 tag : 'i',
2540                 cls : 'fa fa-' + this.fa
2541             });
2542         }
2543         
2544         anc.cn.push(ctag);
2545         
2546         
2547         var cfg= {
2548             tag: 'li',
2549             cls: 'dropdown-menu-item',
2550             cn: [ anc ]
2551         };
2552         if (this.parent().type == 'treeview') {
2553             cfg.cls = 'treeview-menu';
2554         }
2555         if (this.active) {
2556             cfg.cls += ' active';
2557         }
2558         
2559         
2560         
2561         anc.href = this.href || cfg.cn[0].href ;
2562         ctag.html = this.html || cfg.cn[0].html ;
2563         return cfg;
2564     },
2565     
2566     initEvents: function()
2567     {
2568         if (this.parent().type == 'treeview') {
2569             this.el.select('a').on('click', this.onClick, this);
2570         }
2571         
2572         if (this.menu) {
2573             this.menu.parentType = this.xtype;
2574             this.menu.triggerEl = this.el;
2575             this.menu = this.addxtype(Roo.apply({}, this.menu));
2576         }
2577         
2578     },
2579     onClick : function(e)
2580     {
2581         Roo.log('item on click ');
2582         
2583         if(this.preventDefault){
2584             e.preventDefault();
2585         }
2586         //this.parent().hideMenuItems();
2587         
2588         this.fireEvent('click', this, e);
2589     },
2590     getEl : function()
2591     {
2592         return this.el;
2593     } 
2594 });
2595
2596  
2597
2598  /*
2599  * - LGPL
2600  *
2601  * menu separator
2602  * 
2603  */
2604
2605
2606 /**
2607  * @class Roo.bootstrap.MenuSeparator
2608  * @extends Roo.bootstrap.Component
2609  * Bootstrap MenuSeparator class
2610  * 
2611  * @constructor
2612  * Create a new MenuItem
2613  * @param {Object} config The config object
2614  */
2615
2616
2617 Roo.bootstrap.MenuSeparator = function(config){
2618     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2619 };
2620
2621 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2622     
2623     getAutoCreate : function(){
2624         var cfg = {
2625             cls: 'divider',
2626             tag : 'li'
2627         };
2628         
2629         return cfg;
2630     }
2631    
2632 });
2633
2634  
2635
2636  
2637 /*
2638 * Licence: LGPL
2639 */
2640
2641 /**
2642  * @class Roo.bootstrap.Modal
2643  * @extends Roo.bootstrap.Component
2644  * Bootstrap Modal class
2645  * @cfg {String} title Title of dialog
2646  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2647  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2648  * @cfg {Boolean} specificTitle default false
2649  * @cfg {Array} buttons Array of buttons or standard button set..
2650  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2651  * @cfg {Boolean} animate default true
2652  * @cfg {Boolean} allow_close default true
2653  * @cfg {Boolean} fitwindow default false
2654  * @cfg {String} size (sm|lg) default empty
2655  * @cfg {Number} max_width set the max width of modal
2656  *
2657  *
2658  * @constructor
2659  * Create a new Modal Dialog
2660  * @param {Object} config The config object
2661  */
2662
2663 Roo.bootstrap.Modal = function(config){
2664     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2665     this.addEvents({
2666         // raw events
2667         /**
2668          * @event btnclick
2669          * The raw btnclick event for the button
2670          * @param {Roo.EventObject} e
2671          */
2672         "btnclick" : true,
2673         /**
2674          * @event resize
2675          * Fire when dialog resize
2676          * @param {Roo.bootstrap.Modal} this
2677          * @param {Roo.EventObject} e
2678          */
2679         "resize" : true
2680     });
2681     this.buttons = this.buttons || [];
2682
2683     if (this.tmpl) {
2684         this.tmpl = Roo.factory(this.tmpl);
2685     }
2686
2687 };
2688
2689 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2690
2691     title : 'test dialog',
2692
2693     buttons : false,
2694
2695     // set on load...
2696
2697     html: false,
2698
2699     tmp: false,
2700
2701     specificTitle: false,
2702
2703     buttonPosition: 'right',
2704
2705     allow_close : true,
2706
2707     animate : true,
2708
2709     fitwindow: false,
2710     
2711      // private
2712     dialogEl: false,
2713     bodyEl:  false,
2714     footerEl:  false,
2715     titleEl:  false,
2716     closeEl:  false,
2717
2718     size: '',
2719     
2720     max_width: 0,
2721     
2722     max_height: 0,
2723     
2724     fit_content: false,
2725
2726     onRender : function(ct, position)
2727     {
2728         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2729
2730         if(!this.el){
2731             var cfg = Roo.apply({},  this.getAutoCreate());
2732             cfg.id = Roo.id();
2733             //if(!cfg.name){
2734             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2735             //}
2736             //if (!cfg.name.length) {
2737             //    delete cfg.name;
2738            // }
2739             if (this.cls) {
2740                 cfg.cls += ' ' + this.cls;
2741             }
2742             if (this.style) {
2743                 cfg.style = this.style;
2744             }
2745             this.el = Roo.get(document.body).createChild(cfg, position);
2746         }
2747         //var type = this.el.dom.type;
2748
2749
2750         if(this.tabIndex !== undefined){
2751             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2752         }
2753
2754         this.dialogEl = this.el.select('.modal-dialog',true).first();
2755         this.bodyEl = this.el.select('.modal-body',true).first();
2756         this.closeEl = this.el.select('.modal-header .close', true).first();
2757         this.headerEl = this.el.select('.modal-header',true).first();
2758         this.titleEl = this.el.select('.modal-title',true).first();
2759         this.footerEl = this.el.select('.modal-footer',true).first();
2760
2761         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2762         
2763         //this.el.addClass("x-dlg-modal");
2764
2765         if (this.buttons.length) {
2766             Roo.each(this.buttons, function(bb) {
2767                 var b = Roo.apply({}, bb);
2768                 b.xns = b.xns || Roo.bootstrap;
2769                 b.xtype = b.xtype || 'Button';
2770                 if (typeof(b.listeners) == 'undefined') {
2771                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2772                 }
2773
2774                 var btn = Roo.factory(b);
2775
2776                 btn.render(this.getButtonContainer());
2777
2778             },this);
2779         }
2780         // render the children.
2781         var nitems = [];
2782
2783         if(typeof(this.items) != 'undefined'){
2784             var items = this.items;
2785             delete this.items;
2786
2787             for(var i =0;i < items.length;i++) {
2788                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2789             }
2790         }
2791
2792         this.items = nitems;
2793
2794         // where are these used - they used to be body/close/footer
2795
2796
2797         this.initEvents();
2798         //this.el.addClass([this.fieldClass, this.cls]);
2799
2800     },
2801
2802     getAutoCreate : function()
2803     {
2804         var bdy = {
2805                 cls : 'modal-body',
2806                 html : this.html || ''
2807         };
2808
2809         var title = {
2810             tag: 'h4',
2811             cls : 'modal-title',
2812             html : this.title
2813         };
2814
2815         if(this.specificTitle){
2816             title = this.title;
2817
2818         }
2819
2820         var header = [];
2821         if (this.allow_close && Roo.bootstrap.version == 3) {
2822             header.push({
2823                 tag: 'button',
2824                 cls : 'close',
2825                 html : '&times'
2826             });
2827         }
2828
2829         header.push(title);
2830
2831         if (this.allow_close && Roo.bootstrap.version == 4) {
2832             header.push({
2833                 tag: 'button',
2834                 cls : 'close',
2835                 html : '&times'
2836             });
2837         }
2838         
2839         var size = '';
2840
2841         if(this.size.length){
2842             size = 'modal-' + this.size;
2843         }
2844         
2845         var footer = Roo.bootstrap.version == 3 ?
2846             {
2847                 cls : 'modal-footer',
2848                 cn : [
2849                     {
2850                         tag: 'div',
2851                         cls: 'btn-' + this.buttonPosition
2852                     }
2853                 ]
2854
2855             } :
2856             {  // BS4 uses mr-auto on left buttons....
2857                 cls : 'modal-footer'
2858             };
2859
2860             
2861
2862         
2863         
2864         var modal = {
2865             cls: "modal",
2866              cn : [
2867                 {
2868                     cls: "modal-dialog " + size,
2869                     cn : [
2870                         {
2871                             cls : "modal-content",
2872                             cn : [
2873                                 {
2874                                     cls : 'modal-header',
2875                                     cn : header
2876                                 },
2877                                 bdy,
2878                                 footer
2879                             ]
2880
2881                         }
2882                     ]
2883
2884                 }
2885             ]
2886         };
2887
2888         if(this.animate){
2889             modal.cls += ' fade';
2890         }
2891
2892         return modal;
2893
2894     },
2895     getChildContainer : function() {
2896
2897          return this.bodyEl;
2898
2899     },
2900     getButtonContainer : function() {
2901         
2902          return Roo.bootstrap.version == 4 ?
2903             this.el.select('.modal-footer',true).first()
2904             : this.el.select('.modal-footer div',true).first();
2905
2906     },
2907     initEvents : function()
2908     {
2909         if (this.allow_close) {
2910             this.closeEl.on('click', this.hide, this);
2911         }
2912         Roo.EventManager.onWindowResize(this.resize, this, true);
2913
2914
2915     },
2916   
2917
2918     resize : function()
2919     {
2920         this.maskEl.setSize(
2921             Roo.lib.Dom.getViewWidth(true),
2922             Roo.lib.Dom.getViewHeight(true)
2923         );
2924         
2925         if (this.fitwindow) {
2926             
2927            
2928             this.setSize(
2929                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2930                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
2931             );
2932             return;
2933         }
2934         
2935         if(this.max_width !== 0) {
2936             
2937             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2938             
2939             if(this.height) {
2940                 this.setSize(w, this.height);
2941                 return;
2942             }
2943             
2944             if(this.max_height) {
2945                 this.setSize(w,Math.min(
2946                     this.max_height,
2947                     Roo.lib.Dom.getViewportHeight(true) - 60
2948                 ));
2949                 
2950                 return;
2951             }
2952             
2953             if(!this.fit_content) {
2954                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2955                 return;
2956             }
2957             
2958             this.setSize(w, Math.min(
2959                 60 +
2960                 this.headerEl.getHeight() + 
2961                 this.footerEl.getHeight() + 
2962                 this.getChildHeight(this.bodyEl.dom.childNodes),
2963                 Roo.lib.Dom.getViewportHeight(true) - 60)
2964             );
2965         }
2966         
2967     },
2968
2969     setSize : function(w,h)
2970     {
2971         if (!w && !h) {
2972             return;
2973         }
2974         
2975         this.resizeTo(w,h);
2976     },
2977
2978     show : function() {
2979
2980         if (!this.rendered) {
2981             this.render();
2982         }
2983
2984         //this.el.setStyle('display', 'block');
2985         this.el.removeClass('hideing');
2986         this.el.dom.style.display='block';
2987         
2988         Roo.get(document.body).addClass('modal-open');
2989  
2990         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2991             
2992             (function(){
2993                 this.el.addClass('show');
2994                 this.el.addClass('in');
2995             }).defer(50, this);
2996         }else{
2997             this.el.addClass('show');
2998             this.el.addClass('in');
2999         }
3000
3001         // not sure how we can show data in here..
3002         //if (this.tmpl) {
3003         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3004         //}
3005
3006         Roo.get(document.body).addClass("x-body-masked");
3007         
3008         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3009         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3010         this.maskEl.dom.style.display = 'block';
3011         this.maskEl.addClass('show');
3012         
3013         
3014         this.resize();
3015         
3016         this.fireEvent('show', this);
3017
3018         // set zindex here - otherwise it appears to be ignored...
3019         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3020
3021         (function () {
3022             this.items.forEach( function(e) {
3023                 e.layout ? e.layout() : false;
3024
3025             });
3026         }).defer(100,this);
3027
3028     },
3029     hide : function()
3030     {
3031         if(this.fireEvent("beforehide", this) !== false){
3032             
3033             this.maskEl.removeClass('show');
3034             
3035             this.maskEl.dom.style.display = '';
3036             Roo.get(document.body).removeClass("x-body-masked");
3037             this.el.removeClass('in');
3038             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3039
3040             if(this.animate){ // why
3041                 this.el.addClass('hideing');
3042                 this.el.removeClass('show');
3043                 (function(){
3044                     if (!this.el.hasClass('hideing')) {
3045                         return; // it's been shown again...
3046                     }
3047                     
3048                     this.el.dom.style.display='';
3049
3050                     Roo.get(document.body).removeClass('modal-open');
3051                     this.el.removeClass('hideing');
3052                 }).defer(150,this);
3053                 
3054             }else{
3055                 this.el.removeClass('show');
3056                 this.el.dom.style.display='';
3057                 Roo.get(document.body).removeClass('modal-open');
3058
3059             }
3060             this.fireEvent('hide', this);
3061         }
3062     },
3063     isVisible : function()
3064     {
3065         
3066         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3067         
3068     },
3069
3070     addButton : function(str, cb)
3071     {
3072
3073
3074         var b = Roo.apply({}, { html : str } );
3075         b.xns = b.xns || Roo.bootstrap;
3076         b.xtype = b.xtype || 'Button';
3077         if (typeof(b.listeners) == 'undefined') {
3078             b.listeners = { click : cb.createDelegate(this)  };
3079         }
3080
3081         var btn = Roo.factory(b);
3082
3083         btn.render(this.getButtonContainer());
3084
3085         return btn;
3086
3087     },
3088
3089     setDefaultButton : function(btn)
3090     {
3091         //this.el.select('.modal-footer').()
3092     },
3093
3094     resizeTo: function(w,h)
3095     {
3096         this.dialogEl.setWidth(w);
3097         
3098         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3099
3100         this.bodyEl.setHeight(h - diff);
3101         
3102         this.fireEvent('resize', this);
3103     },
3104     
3105     setContentSize  : function(w, h)
3106     {
3107
3108     },
3109     onButtonClick: function(btn,e)
3110     {
3111         //Roo.log([a,b,c]);
3112         this.fireEvent('btnclick', btn.name, e);
3113     },
3114      /**
3115      * Set the title of the Dialog
3116      * @param {String} str new Title
3117      */
3118     setTitle: function(str) {
3119         this.titleEl.dom.innerHTML = str;
3120     },
3121     /**
3122      * Set the body of the Dialog
3123      * @param {String} str new Title
3124      */
3125     setBody: function(str) {
3126         this.bodyEl.dom.innerHTML = str;
3127     },
3128     /**
3129      * Set the body of the Dialog using the template
3130      * @param {Obj} data - apply this data to the template and replace the body contents.
3131      */
3132     applyBody: function(obj)
3133     {
3134         if (!this.tmpl) {
3135             Roo.log("Error - using apply Body without a template");
3136             //code
3137         }
3138         this.tmpl.overwrite(this.bodyEl, obj);
3139     },
3140     
3141     getChildHeight : function(child_nodes)
3142     {
3143         if(
3144             !child_nodes ||
3145             child_nodes.length == 0
3146         ) {
3147             return;
3148         }
3149         
3150         var child_height = 0;
3151         
3152         for(var i = 0; i < child_nodes.length; i++) {
3153             
3154             /*
3155             * for modal with tabs...
3156             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3157                 
3158                 var layout_childs = child_nodes[i].childNodes;
3159                 
3160                 for(var j = 0; j < layout_childs.length; j++) {
3161                     
3162                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3163                         
3164                         var layout_body_childs = layout_childs[j].childNodes;
3165                         
3166                         for(var k = 0; k < layout_body_childs.length; k++) {
3167                             
3168                             if(layout_body_childs[k].classList.contains('navbar')) {
3169                                 child_height += layout_body_childs[k].offsetHeight;
3170                                 continue;
3171                             }
3172                             
3173                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3174                                 
3175                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3176                                 
3177                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3178                                     
3179                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3180                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3181                                         continue;
3182                                     }
3183                                     
3184                                 }
3185                                 
3186                             }
3187                             
3188                         }
3189                     }
3190                 }
3191                 continue;
3192             }
3193             */
3194             
3195             child_height += child_nodes[i].offsetHeight;
3196             // Roo.log(child_nodes[i].offsetHeight);
3197         }
3198         
3199         return child_height;
3200     }
3201
3202 });
3203
3204
3205 Roo.apply(Roo.bootstrap.Modal,  {
3206     /**
3207          * Button config that displays a single OK button
3208          * @type Object
3209          */
3210         OK :  [{
3211             name : 'ok',
3212             weight : 'primary',
3213             html : 'OK'
3214         }],
3215         /**
3216          * Button config that displays Yes and No buttons
3217          * @type Object
3218          */
3219         YESNO : [
3220             {
3221                 name  : 'no',
3222                 html : 'No'
3223             },
3224             {
3225                 name  :'yes',
3226                 weight : 'primary',
3227                 html : 'Yes'
3228             }
3229         ],
3230
3231         /**
3232          * Button config that displays OK and Cancel buttons
3233          * @type Object
3234          */
3235         OKCANCEL : [
3236             {
3237                name : 'cancel',
3238                 html : 'Cancel'
3239             },
3240             {
3241                 name : 'ok',
3242                 weight : 'primary',
3243                 html : 'OK'
3244             }
3245         ],
3246         /**
3247          * Button config that displays Yes, No and Cancel buttons
3248          * @type Object
3249          */
3250         YESNOCANCEL : [
3251             {
3252                 name : 'yes',
3253                 weight : 'primary',
3254                 html : 'Yes'
3255             },
3256             {
3257                 name : 'no',
3258                 html : 'No'
3259             },
3260             {
3261                 name : 'cancel',
3262                 html : 'Cancel'
3263             }
3264         ],
3265         
3266         zIndex : 10001
3267 });
3268 /*
3269  * - LGPL
3270  *
3271  * messagebox - can be used as a replace
3272  * 
3273  */
3274 /**
3275  * @class Roo.MessageBox
3276  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3277  * Example usage:
3278  *<pre><code>
3279 // Basic alert:
3280 Roo.Msg.alert('Status', 'Changes saved successfully.');
3281
3282 // Prompt for user data:
3283 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3284     if (btn == 'ok'){
3285         // process text value...
3286     }
3287 });
3288
3289 // Show a dialog using config options:
3290 Roo.Msg.show({
3291    title:'Save Changes?',
3292    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3293    buttons: Roo.Msg.YESNOCANCEL,
3294    fn: processResult,
3295    animEl: 'elId'
3296 });
3297 </code></pre>
3298  * @singleton
3299  */
3300 Roo.bootstrap.MessageBox = function(){
3301     var dlg, opt, mask, waitTimer;
3302     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3303     var buttons, activeTextEl, bwidth;
3304
3305     
3306     // private
3307     var handleButton = function(button){
3308         dlg.hide();
3309         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3310     };
3311
3312     // private
3313     var handleHide = function(){
3314         if(opt && opt.cls){
3315             dlg.el.removeClass(opt.cls);
3316         }
3317         //if(waitTimer){
3318         //    Roo.TaskMgr.stop(waitTimer);
3319         //    waitTimer = null;
3320         //}
3321     };
3322
3323     // private
3324     var updateButtons = function(b){
3325         var width = 0;
3326         if(!b){
3327             buttons["ok"].hide();
3328             buttons["cancel"].hide();
3329             buttons["yes"].hide();
3330             buttons["no"].hide();
3331             dlg.footerEl.hide();
3332             
3333             return width;
3334         }
3335         dlg.footerEl.show();
3336         for(var k in buttons){
3337             if(typeof buttons[k] != "function"){
3338                 if(b[k]){
3339                     buttons[k].show();
3340                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3341                     width += buttons[k].el.getWidth()+15;
3342                 }else{
3343                     buttons[k].hide();
3344                 }
3345             }
3346         }
3347         return width;
3348     };
3349
3350     // private
3351     var handleEsc = function(d, k, e){
3352         if(opt && opt.closable !== false){
3353             dlg.hide();
3354         }
3355         if(e){
3356             e.stopEvent();
3357         }
3358     };
3359
3360     return {
3361         /**
3362          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3363          * @return {Roo.BasicDialog} The BasicDialog element
3364          */
3365         getDialog : function(){
3366            if(!dlg){
3367                 dlg = new Roo.bootstrap.Modal( {
3368                     //draggable: true,
3369                     //resizable:false,
3370                     //constraintoviewport:false,
3371                     //fixedcenter:true,
3372                     //collapsible : false,
3373                     //shim:true,
3374                     //modal: true,
3375                 //    width: 'auto',
3376                   //  height:100,
3377                     //buttonAlign:"center",
3378                     closeClick : function(){
3379                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3380                             handleButton("no");
3381                         }else{
3382                             handleButton("cancel");
3383                         }
3384                     }
3385                 });
3386                 dlg.render();
3387                 dlg.on("hide", handleHide);
3388                 mask = dlg.mask;
3389                 //dlg.addKeyListener(27, handleEsc);
3390                 buttons = {};
3391                 this.buttons = buttons;
3392                 var bt = this.buttonText;
3393                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3394                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3395                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3396                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3397                 //Roo.log(buttons);
3398                 bodyEl = dlg.bodyEl.createChild({
3399
3400                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3401                         '<textarea class="roo-mb-textarea"></textarea>' +
3402                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3403                 });
3404                 msgEl = bodyEl.dom.firstChild;
3405                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3406                 textboxEl.enableDisplayMode();
3407                 textboxEl.addKeyListener([10,13], function(){
3408                     if(dlg.isVisible() && opt && opt.buttons){
3409                         if(opt.buttons.ok){
3410                             handleButton("ok");
3411                         }else if(opt.buttons.yes){
3412                             handleButton("yes");
3413                         }
3414                     }
3415                 });
3416                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3417                 textareaEl.enableDisplayMode();
3418                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3419                 progressEl.enableDisplayMode();
3420                 
3421                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3422                 var pf = progressEl.dom.firstChild;
3423                 if (pf) {
3424                     pp = Roo.get(pf.firstChild);
3425                     pp.setHeight(pf.offsetHeight);
3426                 }
3427                 
3428             }
3429             return dlg;
3430         },
3431
3432         /**
3433          * Updates the message box body text
3434          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3435          * the XHTML-compliant non-breaking space character '&amp;#160;')
3436          * @return {Roo.MessageBox} This message box
3437          */
3438         updateText : function(text)
3439         {
3440             if(!dlg.isVisible() && !opt.width){
3441                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3442                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3443             }
3444             msgEl.innerHTML = text || '&#160;';
3445       
3446             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3447             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3448             var w = Math.max(
3449                     Math.min(opt.width || cw , this.maxWidth), 
3450                     Math.max(opt.minWidth || this.minWidth, bwidth)
3451             );
3452             if(opt.prompt){
3453                 activeTextEl.setWidth(w);
3454             }
3455             if(dlg.isVisible()){
3456                 dlg.fixedcenter = false;
3457             }
3458             // to big, make it scroll. = But as usual stupid IE does not support
3459             // !important..
3460             
3461             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3462                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3463                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3464             } else {
3465                 bodyEl.dom.style.height = '';
3466                 bodyEl.dom.style.overflowY = '';
3467             }
3468             if (cw > w) {
3469                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3470             } else {
3471                 bodyEl.dom.style.overflowX = '';
3472             }
3473             
3474             dlg.setContentSize(w, bodyEl.getHeight());
3475             if(dlg.isVisible()){
3476                 dlg.fixedcenter = true;
3477             }
3478             return this;
3479         },
3480
3481         /**
3482          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3483          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3484          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3485          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3486          * @return {Roo.MessageBox} This message box
3487          */
3488         updateProgress : function(value, text){
3489             if(text){
3490                 this.updateText(text);
3491             }
3492             
3493             if (pp) { // weird bug on my firefox - for some reason this is not defined
3494                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3495                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3496             }
3497             return this;
3498         },        
3499
3500         /**
3501          * Returns true if the message box is currently displayed
3502          * @return {Boolean} True if the message box is visible, else false
3503          */
3504         isVisible : function(){
3505             return dlg && dlg.isVisible();  
3506         },
3507
3508         /**
3509          * Hides the message box if it is displayed
3510          */
3511         hide : function(){
3512             if(this.isVisible()){
3513                 dlg.hide();
3514             }  
3515         },
3516
3517         /**
3518          * Displays a new message box, or reinitializes an existing message box, based on the config options
3519          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3520          * The following config object properties are supported:
3521          * <pre>
3522 Property    Type             Description
3523 ----------  ---------------  ------------------------------------------------------------------------------------
3524 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3525                                    closes (defaults to undefined)
3526 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3527                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3528 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3529                                    progress and wait dialogs will ignore this property and always hide the
3530                                    close button as they can only be closed programmatically.
3531 cls               String           A custom CSS class to apply to the message box element
3532 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3533                                    displayed (defaults to 75)
3534 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3535                                    function will be btn (the name of the button that was clicked, if applicable,
3536                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3537                                    Progress and wait dialogs will ignore this option since they do not respond to
3538                                    user actions and can only be closed programmatically, so any required function
3539                                    should be called by the same code after it closes the dialog.
3540 icon              String           A CSS class that provides a background image to be used as an icon for
3541                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3542 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3543 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3544 modal             Boolean          False to allow user interaction with the page while the message box is
3545                                    displayed (defaults to true)
3546 msg               String           A string that will replace the existing message box body text (defaults
3547                                    to the XHTML-compliant non-breaking space character '&#160;')
3548 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3549 progress          Boolean          True to display a progress bar (defaults to false)
3550 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3551 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3552 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3553 title             String           The title text
3554 value             String           The string value to set into the active textbox element if displayed
3555 wait              Boolean          True to display a progress bar (defaults to false)
3556 width             Number           The width of the dialog in pixels
3557 </pre>
3558          *
3559          * Example usage:
3560          * <pre><code>
3561 Roo.Msg.show({
3562    title: 'Address',
3563    msg: 'Please enter your address:',
3564    width: 300,
3565    buttons: Roo.MessageBox.OKCANCEL,
3566    multiline: true,
3567    fn: saveAddress,
3568    animEl: 'addAddressBtn'
3569 });
3570 </code></pre>
3571          * @param {Object} config Configuration options
3572          * @return {Roo.MessageBox} This message box
3573          */
3574         show : function(options)
3575         {
3576             
3577             // this causes nightmares if you show one dialog after another
3578             // especially on callbacks..
3579              
3580             if(this.isVisible()){
3581                 
3582                 this.hide();
3583                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3584                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3585                 Roo.log("New Dialog Message:" +  options.msg )
3586                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3587                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3588                 
3589             }
3590             var d = this.getDialog();
3591             opt = options;
3592             d.setTitle(opt.title || "&#160;");
3593             d.closeEl.setDisplayed(opt.closable !== false);
3594             activeTextEl = textboxEl;
3595             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3596             if(opt.prompt){
3597                 if(opt.multiline){
3598                     textboxEl.hide();
3599                     textareaEl.show();
3600                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3601                         opt.multiline : this.defaultTextHeight);
3602                     activeTextEl = textareaEl;
3603                 }else{
3604                     textboxEl.show();
3605                     textareaEl.hide();
3606                 }
3607             }else{
3608                 textboxEl.hide();
3609                 textareaEl.hide();
3610             }
3611             progressEl.setDisplayed(opt.progress === true);
3612             if (opt.progress) {
3613                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3614             }
3615             this.updateProgress(0);
3616             activeTextEl.dom.value = opt.value || "";
3617             if(opt.prompt){
3618                 dlg.setDefaultButton(activeTextEl);
3619             }else{
3620                 var bs = opt.buttons;
3621                 var db = null;
3622                 if(bs && bs.ok){
3623                     db = buttons["ok"];
3624                 }else if(bs && bs.yes){
3625                     db = buttons["yes"];
3626                 }
3627                 dlg.setDefaultButton(db);
3628             }
3629             bwidth = updateButtons(opt.buttons);
3630             this.updateText(opt.msg);
3631             if(opt.cls){
3632                 d.el.addClass(opt.cls);
3633             }
3634             d.proxyDrag = opt.proxyDrag === true;
3635             d.modal = opt.modal !== false;
3636             d.mask = opt.modal !== false ? mask : false;
3637             if(!d.isVisible()){
3638                 // force it to the end of the z-index stack so it gets a cursor in FF
3639                 document.body.appendChild(dlg.el.dom);
3640                 d.animateTarget = null;
3641                 d.show(options.animEl);
3642             }
3643             return this;
3644         },
3645
3646         /**
3647          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3648          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3649          * and closing the message box when the process is complete.
3650          * @param {String} title The title bar text
3651          * @param {String} msg The message box body text
3652          * @return {Roo.MessageBox} This message box
3653          */
3654         progress : function(title, msg){
3655             this.show({
3656                 title : title,
3657                 msg : msg,
3658                 buttons: false,
3659                 progress:true,
3660                 closable:false,
3661                 minWidth: this.minProgressWidth,
3662                 modal : true
3663             });
3664             return this;
3665         },
3666
3667         /**
3668          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3669          * If a callback function is passed it will be called after the user clicks the button, and the
3670          * id of the button that was clicked will be passed as the only parameter to the callback
3671          * (could also be the top-right close button).
3672          * @param {String} title The title bar text
3673          * @param {String} msg The message box body text
3674          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3675          * @param {Object} scope (optional) The scope of the callback function
3676          * @return {Roo.MessageBox} This message box
3677          */
3678         alert : function(title, msg, fn, scope)
3679         {
3680             this.show({
3681                 title : title,
3682                 msg : msg,
3683                 buttons: this.OK,
3684                 fn: fn,
3685                 closable : false,
3686                 scope : scope,
3687                 modal : true
3688             });
3689             return this;
3690         },
3691
3692         /**
3693          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3694          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3695          * You are responsible for closing the message box when the process is complete.
3696          * @param {String} msg The message box body text
3697          * @param {String} title (optional) The title bar text
3698          * @return {Roo.MessageBox} This message box
3699          */
3700         wait : function(msg, title){
3701             this.show({
3702                 title : title,
3703                 msg : msg,
3704                 buttons: false,
3705                 closable:false,
3706                 progress:true,
3707                 modal:true,
3708                 width:300,
3709                 wait:true
3710             });
3711             waitTimer = Roo.TaskMgr.start({
3712                 run: function(i){
3713                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3714                 },
3715                 interval: 1000
3716             });
3717             return this;
3718         },
3719
3720         /**
3721          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3722          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3723          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3724          * @param {String} title The title bar text
3725          * @param {String} msg The message box body text
3726          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3727          * @param {Object} scope (optional) The scope of the callback function
3728          * @return {Roo.MessageBox} This message box
3729          */
3730         confirm : function(title, msg, fn, scope){
3731             this.show({
3732                 title : title,
3733                 msg : msg,
3734                 buttons: this.YESNO,
3735                 fn: fn,
3736                 scope : scope,
3737                 modal : true
3738             });
3739             return this;
3740         },
3741
3742         /**
3743          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3744          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3745          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3746          * (could also be the top-right close button) and the text that was entered will be passed as the two
3747          * parameters to the callback.
3748          * @param {String} title The title bar text
3749          * @param {String} msg The message box body text
3750          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3751          * @param {Object} scope (optional) The scope of the callback function
3752          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3753          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3754          * @return {Roo.MessageBox} This message box
3755          */
3756         prompt : function(title, msg, fn, scope, multiline){
3757             this.show({
3758                 title : title,
3759                 msg : msg,
3760                 buttons: this.OKCANCEL,
3761                 fn: fn,
3762                 minWidth:250,
3763                 scope : scope,
3764                 prompt:true,
3765                 multiline: multiline,
3766                 modal : true
3767             });
3768             return this;
3769         },
3770
3771         /**
3772          * Button config that displays a single OK button
3773          * @type Object
3774          */
3775         OK : {ok:true},
3776         /**
3777          * Button config that displays Yes and No buttons
3778          * @type Object
3779          */
3780         YESNO : {yes:true, no:true},
3781         /**
3782          * Button config that displays OK and Cancel buttons
3783          * @type Object
3784          */
3785         OKCANCEL : {ok:true, cancel:true},
3786         /**
3787          * Button config that displays Yes, No and Cancel buttons
3788          * @type Object
3789          */
3790         YESNOCANCEL : {yes:true, no:true, cancel:true},
3791
3792         /**
3793          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3794          * @type Number
3795          */
3796         defaultTextHeight : 75,
3797         /**
3798          * The maximum width in pixels of the message box (defaults to 600)
3799          * @type Number
3800          */
3801         maxWidth : 600,
3802         /**
3803          * The minimum width in pixels of the message box (defaults to 100)
3804          * @type Number
3805          */
3806         minWidth : 100,
3807         /**
3808          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3809          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3810          * @type Number
3811          */
3812         minProgressWidth : 250,
3813         /**
3814          * An object containing the default button text strings that can be overriden for localized language support.
3815          * Supported properties are: ok, cancel, yes and no.
3816          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3817          * @type Object
3818          */
3819         buttonText : {
3820             ok : "OK",
3821             cancel : "Cancel",
3822             yes : "Yes",
3823             no : "No"
3824         }
3825     };
3826 }();
3827
3828 /**
3829  * Shorthand for {@link Roo.MessageBox}
3830  */
3831 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3832 Roo.Msg = Roo.Msg || Roo.MessageBox;
3833 /*
3834  * - LGPL
3835  *
3836  * navbar
3837  * 
3838  */
3839
3840 /**
3841  * @class Roo.bootstrap.Navbar
3842  * @extends Roo.bootstrap.Component
3843  * Bootstrap Navbar class
3844
3845  * @constructor
3846  * Create a new Navbar
3847  * @param {Object} config The config object
3848  */
3849
3850
3851 Roo.bootstrap.Navbar = function(config){
3852     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3853     this.addEvents({
3854         // raw events
3855         /**
3856          * @event beforetoggle
3857          * Fire before toggle the menu
3858          * @param {Roo.EventObject} e
3859          */
3860         "beforetoggle" : true
3861     });
3862 };
3863
3864 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3865     
3866     
3867    
3868     // private
3869     navItems : false,
3870     loadMask : false,
3871     
3872     
3873     getAutoCreate : function(){
3874         
3875         
3876         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3877         
3878     },
3879     
3880     initEvents :function ()
3881     {
3882         //Roo.log(this.el.select('.navbar-toggle',true));
3883         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
3884         
3885         var mark = {
3886             tag: "div",
3887             cls:"x-dlg-mask"
3888         };
3889         
3890         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3891         
3892         var size = this.el.getSize();
3893         this.maskEl.setSize(size.width, size.height);
3894         this.maskEl.enableDisplayMode("block");
3895         this.maskEl.hide();
3896         
3897         if(this.loadMask){
3898             this.maskEl.show();
3899         }
3900     },
3901     
3902     
3903     getChildContainer : function()
3904     {
3905         if (this.el && this.el.select('.collapse').getCount()) {
3906             return this.el.select('.collapse',true).first();
3907         }
3908         
3909         return this.el;
3910     },
3911     
3912     mask : function()
3913     {
3914         this.maskEl.show();
3915     },
3916     
3917     unmask : function()
3918     {
3919         this.maskEl.hide();
3920     },
3921     onToggle : function()
3922     {
3923         
3924         if(this.fireEvent('beforetoggle', this) === false){
3925             return;
3926         }
3927         var ce = this.el.select('.navbar-collapse',true).first();
3928       
3929         if (!ce.hasClass('show')) {
3930            this.expand();
3931         } else {
3932             this.collapse();
3933         }
3934         
3935         
3936     
3937     },
3938     /**
3939      * Expand the navbar pulldown 
3940      */
3941     expand : function ()
3942     {
3943        
3944         var ce = this.el.select('.navbar-collapse',true).first();
3945         if (ce.hasClass('collapsing')) {
3946             return;
3947         }
3948         ce.dom.style.height = '';
3949                // show it...
3950         ce.addClass('in'); // old...
3951         ce.removeClass('collapse');
3952         ce.addClass('show');
3953         var h = ce.getHeight();
3954         Roo.log(h);
3955         ce.removeClass('show');
3956         // at this point we should be able to see it..
3957         ce.addClass('collapsing');
3958         
3959         ce.setHeight(0); // resize it ...
3960         ce.on('transitionend', function() {
3961             //Roo.log('done transition');
3962             ce.removeClass('collapsing');
3963             ce.addClass('show');
3964             ce.removeClass('collapse');
3965
3966             ce.dom.style.height = '';
3967         }, this, { single: true} );
3968         ce.setHeight(h);
3969         ce.dom.scrollTop = 0;
3970     },
3971     /**
3972      * Collapse the navbar pulldown 
3973      */
3974     collapse : function()
3975     {
3976          var ce = this.el.select('.navbar-collapse',true).first();
3977        
3978         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
3979             // it's collapsed or collapsing..
3980             return;
3981         }
3982         ce.removeClass('in'); // old...
3983         ce.setHeight(ce.getHeight());
3984         ce.removeClass('show');
3985         ce.addClass('collapsing');
3986         
3987         ce.on('transitionend', function() {
3988             ce.dom.style.height = '';
3989             ce.removeClass('collapsing');
3990             ce.addClass('collapse');
3991         }, this, { single: true} );
3992         ce.setHeight(0);
3993     }
3994     
3995     
3996     
3997 });
3998
3999
4000
4001  
4002
4003  /*
4004  * - LGPL
4005  *
4006  * navbar
4007  * 
4008  */
4009
4010 /**
4011  * @class Roo.bootstrap.NavSimplebar
4012  * @extends Roo.bootstrap.Navbar
4013  * Bootstrap Sidebar class
4014  *
4015  * @cfg {Boolean} inverse is inverted color
4016  * 
4017  * @cfg {String} type (nav | pills | tabs)
4018  * @cfg {Boolean} arrangement stacked | justified
4019  * @cfg {String} align (left | right) alignment
4020  * 
4021  * @cfg {Boolean} main (true|false) main nav bar? default false
4022  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4023  * 
4024  * @cfg {String} tag (header|footer|nav|div) default is nav 
4025
4026  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4027  * 
4028  * 
4029  * @constructor
4030  * Create a new Sidebar
4031  * @param {Object} config The config object
4032  */
4033
4034
4035 Roo.bootstrap.NavSimplebar = function(config){
4036     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4037 };
4038
4039 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4040     
4041     inverse: false,
4042     
4043     type: false,
4044     arrangement: '',
4045     align : false,
4046     
4047     weight : 'light',
4048     
4049     main : false,
4050     
4051     
4052     tag : false,
4053     
4054     
4055     getAutoCreate : function(){
4056         
4057         
4058         var cfg = {
4059             tag : this.tag || 'div',
4060             cls : 'navbar navbar-expand-lg roo-navbar-simple'
4061         };
4062         if (['light','white'].indexOf(this.weight) > -1) {
4063             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4064         }
4065         cfg.cls += ' bg-' + this.weight;
4066         
4067         if (this.inverse) {
4068             cfg.cls += ' navbar-inverse';
4069             
4070         }
4071         
4072         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4073         
4074         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4075             return cfg;
4076         }
4077         
4078         
4079     
4080         
4081         cfg.cn = [
4082             {
4083                 cls: 'nav nav-' + this.xtype,
4084                 tag : 'ul'
4085             }
4086         ];
4087         
4088          
4089         this.type = this.type || 'nav';
4090         if (['tabs','pills'].indexOf(this.type) != -1) {
4091             cfg.cn[0].cls += ' nav-' + this.type
4092         
4093         
4094         } else {
4095             if (this.type!=='nav') {
4096                 Roo.log('nav type must be nav/tabs/pills')
4097             }
4098             cfg.cn[0].cls += ' navbar-nav'
4099         }
4100         
4101         
4102         
4103         
4104         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4105             cfg.cn[0].cls += ' nav-' + this.arrangement;
4106         }
4107         
4108         
4109         if (this.align === 'right') {
4110             cfg.cn[0].cls += ' navbar-right';
4111         }
4112         
4113         
4114         
4115         
4116         return cfg;
4117     
4118         
4119     }
4120     
4121     
4122     
4123 });
4124
4125
4126
4127  
4128
4129  
4130        /*
4131  * - LGPL
4132  *
4133  * navbar
4134  * navbar-fixed-top
4135  * navbar-expand-md  fixed-top 
4136  */
4137
4138 /**
4139  * @class Roo.bootstrap.NavHeaderbar
4140  * @extends Roo.bootstrap.NavSimplebar
4141  * Bootstrap Sidebar class
4142  *
4143  * @cfg {String} brand what is brand
4144  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4145  * @cfg {String} brand_href href of the brand
4146  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4147  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4148  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4149  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4150  * 
4151  * @constructor
4152  * Create a new Sidebar
4153  * @param {Object} config The config object
4154  */
4155
4156
4157 Roo.bootstrap.NavHeaderbar = function(config){
4158     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4159       
4160 };
4161
4162 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4163     
4164     position: '',
4165     brand: '',
4166     brand_href: false,
4167     srButton : true,
4168     autohide : false,
4169     desktopCenter : false,
4170    
4171     
4172     getAutoCreate : function(){
4173         
4174         var   cfg = {
4175             tag: this.nav || 'nav',
4176             cls: 'navbar navbar-expand-md',
4177             role: 'navigation',
4178             cn: []
4179         };
4180         
4181         var cn = cfg.cn;
4182         if (this.desktopCenter) {
4183             cn.push({cls : 'container', cn : []});
4184             cn = cn[0].cn;
4185         }
4186         
4187         if(this.srButton){
4188             var btn = {
4189                 tag: 'button',
4190                 type: 'button',
4191                 cls: 'navbar-toggle navbar-toggler',
4192                 'data-toggle': 'collapse',
4193                 cn: [
4194                     {
4195                         tag: 'span',
4196                         cls: 'sr-only',
4197                         html: 'Toggle navigation'
4198                     },
4199                     {
4200                         tag: 'span',
4201                         cls: 'icon-bar navbar-toggler-icon'
4202                     },
4203                     {
4204                         tag: 'span',
4205                         cls: 'icon-bar'
4206                     },
4207                     {
4208                         tag: 'span',
4209                         cls: 'icon-bar'
4210                     }
4211                 ]
4212             };
4213             
4214             cn.push( Roo.bootstrap.version == 4 ? btn : {
4215                 tag: 'div',
4216                 cls: 'navbar-header',
4217                 cn: [
4218                     btn
4219                 ]
4220             });
4221         }
4222         
4223         cn.push({
4224             tag: 'div',
4225             cls: 'collapse navbar-collapse',
4226             cn : []
4227         });
4228         
4229         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4230         
4231         if (['light','white'].indexOf(this.weight) > -1) {
4232             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4233         }
4234         cfg.cls += ' bg-' + this.weight;
4235         
4236         
4237         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4238             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4239             
4240             // tag can override this..
4241             
4242             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4243         }
4244         
4245         if (this.brand !== '') {
4246             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4247             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4248                 tag: 'a',
4249                 href: this.brand_href ? this.brand_href : '#',
4250                 cls: 'navbar-brand',
4251                 cn: [
4252                 this.brand
4253                 ]
4254             });
4255         }
4256         
4257         if(this.main){
4258             cfg.cls += ' main-nav';
4259         }
4260         
4261         
4262         return cfg;
4263
4264         
4265     },
4266     getHeaderChildContainer : function()
4267     {
4268         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4269             return this.el.select('.navbar-header',true).first();
4270         }
4271         
4272         return this.getChildContainer();
4273     },
4274     
4275     
4276     initEvents : function()
4277     {
4278         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4279         
4280         if (this.autohide) {
4281             
4282             var prevScroll = 0;
4283             var ft = this.el;
4284             
4285             Roo.get(document).on('scroll',function(e) {
4286                 var ns = Roo.get(document).getScroll().top;
4287                 var os = prevScroll;
4288                 prevScroll = ns;
4289                 
4290                 if(ns > os){
4291                     ft.removeClass('slideDown');
4292                     ft.addClass('slideUp');
4293                     return;
4294                 }
4295                 ft.removeClass('slideUp');
4296                 ft.addClass('slideDown');
4297                  
4298               
4299           },this);
4300         }
4301     }    
4302     
4303 });
4304
4305
4306
4307  
4308
4309  /*
4310  * - LGPL
4311  *
4312  * navbar
4313  * 
4314  */
4315
4316 /**
4317  * @class Roo.bootstrap.NavSidebar
4318  * @extends Roo.bootstrap.Navbar
4319  * Bootstrap Sidebar class
4320  * 
4321  * @constructor
4322  * Create a new Sidebar
4323  * @param {Object} config The config object
4324  */
4325
4326
4327 Roo.bootstrap.NavSidebar = function(config){
4328     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4329 };
4330
4331 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4332     
4333     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4334     
4335     getAutoCreate : function(){
4336         
4337         
4338         return  {
4339             tag: 'div',
4340             cls: 'sidebar sidebar-nav'
4341         };
4342     
4343         
4344     }
4345     
4346     
4347     
4348 });
4349
4350
4351
4352  
4353
4354  /*
4355  * - LGPL
4356  *
4357  * nav group
4358  * 
4359  */
4360
4361 /**
4362  * @class Roo.bootstrap.NavGroup
4363  * @extends Roo.bootstrap.Component
4364  * Bootstrap NavGroup class
4365  * @cfg {String} align (left|right)
4366  * @cfg {Boolean} inverse
4367  * @cfg {String} type (nav|pills|tab) default nav
4368  * @cfg {String} navId - reference Id for navbar.
4369
4370  * 
4371  * @constructor
4372  * Create a new nav group
4373  * @param {Object} config The config object
4374  */
4375
4376 Roo.bootstrap.NavGroup = function(config){
4377     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4378     this.navItems = [];
4379    
4380     Roo.bootstrap.NavGroup.register(this);
4381      this.addEvents({
4382         /**
4383              * @event changed
4384              * Fires when the active item changes
4385              * @param {Roo.bootstrap.NavGroup} this
4386              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4387              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4388          */
4389         'changed': true
4390      });
4391     
4392 };
4393
4394 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4395     
4396     align: '',
4397     inverse: false,
4398     form: false,
4399     type: 'nav',
4400     navId : '',
4401     // private
4402     
4403     navItems : false, 
4404     
4405     getAutoCreate : function()
4406     {
4407         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4408         
4409         cfg = {
4410             tag : 'ul',
4411             cls: 'nav' 
4412         };
4413         if (Roo.bootstrap.version == 4) {
4414             if (['tabs','pills'].indexOf(this.type) != -1) {
4415                 cfg.cls += ' nav-' + this.type; 
4416             } else {
4417                 cfg.cls += ' navbar-nav';
4418             }
4419         } else {
4420             if (['tabs','pills'].indexOf(this.type) != -1) {
4421                 cfg.cls += ' nav-' + this.type
4422             } else {
4423                 if (this.type !== 'nav') {
4424                     Roo.log('nav type must be nav/tabs/pills')
4425                 }
4426                 cfg.cls += ' navbar-nav'
4427             }
4428         }
4429         
4430         if (this.parent() && this.parent().sidebar) {
4431             cfg = {
4432                 tag: 'ul',
4433                 cls: 'dashboard-menu sidebar-menu'
4434             };
4435             
4436             return cfg;
4437         }
4438         
4439         if (this.form === true) {
4440             cfg = {
4441                 tag: 'form',
4442                 cls: 'navbar-form form-inline'
4443             };
4444             
4445             if (this.align === 'right') {
4446                 cfg.cls += ' navbar-right ml-md-auto';
4447             } else {
4448                 cfg.cls += ' navbar-left';
4449             }
4450         }
4451         
4452         if (this.align === 'right') {
4453             cfg.cls += ' navbar-right ml-md-auto';
4454         } else {
4455             cfg.cls += ' mr-auto';
4456         }
4457         
4458         if (this.inverse) {
4459             cfg.cls += ' navbar-inverse';
4460             
4461         }
4462         
4463         
4464         return cfg;
4465     },
4466     /**
4467     * sets the active Navigation item
4468     * @param {Roo.bootstrap.NavItem} the new current navitem
4469     */
4470     setActiveItem : function(item)
4471     {
4472         var prev = false;
4473         Roo.each(this.navItems, function(v){
4474             if (v == item) {
4475                 return ;
4476             }
4477             if (v.isActive()) {
4478                 v.setActive(false, true);
4479                 prev = v;
4480                 
4481             }
4482             
4483         });
4484
4485         item.setActive(true, true);
4486         this.fireEvent('changed', this, item, prev);
4487         
4488         
4489     },
4490     /**
4491     * gets the active Navigation item
4492     * @return {Roo.bootstrap.NavItem} the current navitem
4493     */
4494     getActive : function()
4495     {
4496         
4497         var prev = false;
4498         Roo.each(this.navItems, function(v){
4499             
4500             if (v.isActive()) {
4501                 prev = v;
4502                 
4503             }
4504             
4505         });
4506         return prev;
4507     },
4508     
4509     indexOfNav : function()
4510     {
4511         
4512         var prev = false;
4513         Roo.each(this.navItems, function(v,i){
4514             
4515             if (v.isActive()) {
4516                 prev = i;
4517                 
4518             }
4519             
4520         });
4521         return prev;
4522     },
4523     /**
4524     * adds a Navigation item
4525     * @param {Roo.bootstrap.NavItem} the navitem to add
4526     */
4527     addItem : function(cfg)
4528     {
4529         if (this.form && Roo.bootstrap.version == 4) {
4530             cfg.tag = 'div';
4531         }
4532         var cn = new Roo.bootstrap.NavItem(cfg);
4533         this.register(cn);
4534         cn.parentId = this.id;
4535         cn.onRender(this.el, null);
4536         return cn;
4537     },
4538     /**
4539     * register a Navigation item
4540     * @param {Roo.bootstrap.NavItem} the navitem to add
4541     */
4542     register : function(item)
4543     {
4544         this.navItems.push( item);
4545         item.navId = this.navId;
4546     
4547     },
4548     
4549     /**
4550     * clear all the Navigation item
4551     */
4552    
4553     clearAll : function()
4554     {
4555         this.navItems = [];
4556         this.el.dom.innerHTML = '';
4557     },
4558     
4559     getNavItem: function(tabId)
4560     {
4561         var ret = false;
4562         Roo.each(this.navItems, function(e) {
4563             if (e.tabId == tabId) {
4564                ret =  e;
4565                return false;
4566             }
4567             return true;
4568             
4569         });
4570         return ret;
4571     },
4572     
4573     setActiveNext : function()
4574     {
4575         var i = this.indexOfNav(this.getActive());
4576         if (i > this.navItems.length) {
4577             return;
4578         }
4579         this.setActiveItem(this.navItems[i+1]);
4580     },
4581     setActivePrev : function()
4582     {
4583         var i = this.indexOfNav(this.getActive());
4584         if (i  < 1) {
4585             return;
4586         }
4587         this.setActiveItem(this.navItems[i-1]);
4588     },
4589     clearWasActive : function(except) {
4590         Roo.each(this.navItems, function(e) {
4591             if (e.tabId != except.tabId && e.was_active) {
4592                e.was_active = false;
4593                return false;
4594             }
4595             return true;
4596             
4597         });
4598     },
4599     getWasActive : function ()
4600     {
4601         var r = false;
4602         Roo.each(this.navItems, function(e) {
4603             if (e.was_active) {
4604                r = e;
4605                return false;
4606             }
4607             return true;
4608             
4609         });
4610         return r;
4611     }
4612     
4613     
4614 });
4615
4616  
4617 Roo.apply(Roo.bootstrap.NavGroup, {
4618     
4619     groups: {},
4620      /**
4621     * register a Navigation Group
4622     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4623     */
4624     register : function(navgrp)
4625     {
4626         this.groups[navgrp.navId] = navgrp;
4627         
4628     },
4629     /**
4630     * fetch a Navigation Group based on the navigation ID
4631     * @param {string} the navgroup to add
4632     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4633     */
4634     get: function(navId) {
4635         if (typeof(this.groups[navId]) == 'undefined') {
4636             return false;
4637             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4638         }
4639         return this.groups[navId] ;
4640     }
4641     
4642     
4643     
4644 });
4645
4646  /*
4647  * - LGPL
4648  *
4649  * row
4650  * 
4651  */
4652
4653 /**
4654  * @class Roo.bootstrap.NavItem
4655  * @extends Roo.bootstrap.Component
4656  * Bootstrap Navbar.NavItem class
4657  * @cfg {String} href  link to
4658  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4659
4660  * @cfg {String} html content of button
4661  * @cfg {String} badge text inside badge
4662  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4663  * @cfg {String} glyphicon DEPRICATED - use fa
4664  * @cfg {String} icon DEPRICATED - use fa
4665  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4666  * @cfg {Boolean} active Is item active
4667  * @cfg {Boolean} disabled Is item disabled
4668  
4669  * @cfg {Boolean} preventDefault (true | false) default false
4670  * @cfg {String} tabId the tab that this item activates.
4671  * @cfg {String} tagtype (a|span) render as a href or span?
4672  * @cfg {Boolean} animateRef (true|false) link to element default false  
4673   
4674  * @constructor
4675  * Create a new Navbar Item
4676  * @param {Object} config The config object
4677  */
4678 Roo.bootstrap.NavItem = function(config){
4679     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4680     this.addEvents({
4681         // raw events
4682         /**
4683          * @event click
4684          * The raw click event for the entire grid.
4685          * @param {Roo.EventObject} e
4686          */
4687         "click" : true,
4688          /**
4689             * @event changed
4690             * Fires when the active item active state changes
4691             * @param {Roo.bootstrap.NavItem} this
4692             * @param {boolean} state the new state
4693              
4694          */
4695         'changed': true,
4696         /**
4697             * @event scrollto
4698             * Fires when scroll to element
4699             * @param {Roo.bootstrap.NavItem} this
4700             * @param {Object} options
4701             * @param {Roo.EventObject} e
4702              
4703          */
4704         'scrollto': true
4705     });
4706    
4707 };
4708
4709 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4710     
4711     href: false,
4712     html: '',
4713     badge: '',
4714     icon: false,
4715     fa : false,
4716     glyphicon: false,
4717     active: false,
4718     preventDefault : false,
4719     tabId : false,
4720     tagtype : 'a',
4721     tag: 'li',
4722     disabled : false,
4723     animateRef : false,
4724     was_active : false,
4725     button_weight : '',
4726     button_outline : false,
4727     
4728     navLink: false,
4729     
4730     getAutoCreate : function(){
4731          
4732         var cfg = {
4733             tag: this.tag,
4734             cls: 'nav-item'
4735         };
4736         
4737         if (this.active) {
4738             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4739         }
4740         if (this.disabled) {
4741             cfg.cls += ' disabled';
4742         }
4743         
4744         // BS4 only?
4745         if (this.button_weight.length) {
4746             cfg.tag = this.href ? 'a' : 'button';
4747             cfg.html = this.html || '';
4748             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4749             if (this.href) {
4750                 cfg.href = this.href;
4751             }
4752             if (this.fa) {
4753                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4754             }
4755             
4756             // menu .. should add dropdown-menu class - so no need for carat..
4757             
4758             if (this.badge !== '') {
4759                  
4760                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4761             }
4762             return cfg;
4763         }
4764         
4765         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4766             cfg.cn = [
4767                 {
4768                     tag: this.tagtype,
4769                     href : this.href || "#",
4770                     html: this.html || ''
4771                 }
4772             ];
4773             if (this.tagtype == 'a') {
4774                 cfg.cn[0].cls = 'nav-link';
4775             }
4776             if (this.icon) {
4777                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4778             }
4779             if (this.fa) {
4780                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4781             }
4782             if(this.glyphicon) {
4783                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4784             }
4785             
4786             if (this.menu) {
4787                 
4788                 cfg.cn[0].html += " <span class='caret'></span>";
4789              
4790             }
4791             
4792             if (this.badge !== '') {
4793                  
4794                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4795             }
4796         }
4797         
4798         
4799         
4800         return cfg;
4801     },
4802     onRender : function(ct, position)
4803     {
4804        // Roo.log("Call onRender: " + this.xtype);
4805         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4806             this.tag = 'div';
4807         }
4808         
4809         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4810         this.navLink = this.el.select('.nav-link',true).first();
4811         return ret;
4812     },
4813       
4814     
4815     initEvents: function() 
4816     {
4817         if (typeof (this.menu) != 'undefined') {
4818             this.menu.parentType = this.xtype;
4819             this.menu.triggerEl = this.el;
4820             this.menu = this.addxtype(Roo.apply({}, this.menu));
4821         }
4822         
4823         this.el.select('a',true).on('click', this.onClick, this);
4824         
4825         if(this.tagtype == 'span'){
4826             this.el.select('span',true).on('click', this.onClick, this);
4827         }
4828        
4829         // at this point parent should be available..
4830         this.parent().register(this);
4831     },
4832     
4833     onClick : function(e)
4834     {
4835         if (e.getTarget('.dropdown-menu-item')) {
4836             // did you click on a menu itemm.... - then don't trigger onclick..
4837             return;
4838         }
4839         
4840         if(
4841                 this.preventDefault || 
4842                 this.href == '#' 
4843         ){
4844             Roo.log("NavItem - prevent Default?");
4845             e.preventDefault();
4846         }
4847         
4848         if (this.disabled) {
4849             return;
4850         }
4851         
4852         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4853         if (tg && tg.transition) {
4854             Roo.log("waiting for the transitionend");
4855             return;
4856         }
4857         
4858         
4859         
4860         //Roo.log("fire event clicked");
4861         if(this.fireEvent('click', this, e) === false){
4862             return;
4863         };
4864         
4865         if(this.tagtype == 'span'){
4866             return;
4867         }
4868         
4869         //Roo.log(this.href);
4870         var ael = this.el.select('a',true).first();
4871         //Roo.log(ael);
4872         
4873         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4874             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4875             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4876                 return; // ignore... - it's a 'hash' to another page.
4877             }
4878             Roo.log("NavItem - prevent Default?");
4879             e.preventDefault();
4880             this.scrollToElement(e);
4881         }
4882         
4883         
4884         var p =  this.parent();
4885    
4886         if (['tabs','pills'].indexOf(p.type)!==-1) {
4887             if (typeof(p.setActiveItem) !== 'undefined') {
4888                 p.setActiveItem(this);
4889             }
4890         }
4891         
4892         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4893         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4894             // remove the collapsed menu expand...
4895             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4896         }
4897     },
4898     
4899     isActive: function () {
4900         return this.active
4901     },
4902     setActive : function(state, fire, is_was_active)
4903     {
4904         if (this.active && !state && this.navId) {
4905             this.was_active = true;
4906             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4907             if (nv) {
4908                 nv.clearWasActive(this);
4909             }
4910             
4911         }
4912         this.active = state;
4913         
4914         if (!state ) {
4915             this.el.removeClass('active');
4916             this.navLink ? this.navLink.removeClass('active') : false;
4917         } else if (!this.el.hasClass('active')) {
4918             
4919             this.el.addClass('active');
4920             if (Roo.bootstrap.version == 4 && this.navLink ) {
4921                 this.navLink.addClass('active');
4922             }
4923             
4924         }
4925         if (fire) {
4926             this.fireEvent('changed', this, state);
4927         }
4928         
4929         // show a panel if it's registered and related..
4930         
4931         if (!this.navId || !this.tabId || !state || is_was_active) {
4932             return;
4933         }
4934         
4935         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4936         if (!tg) {
4937             return;
4938         }
4939         var pan = tg.getPanelByName(this.tabId);
4940         if (!pan) {
4941             return;
4942         }
4943         // if we can not flip to new panel - go back to old nav highlight..
4944         if (false == tg.showPanel(pan)) {
4945             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4946             if (nv) {
4947                 var onav = nv.getWasActive();
4948                 if (onav) {
4949                     onav.setActive(true, false, true);
4950                 }
4951             }
4952             
4953         }
4954         
4955         
4956         
4957     },
4958      // this should not be here...
4959     setDisabled : function(state)
4960     {
4961         this.disabled = state;
4962         if (!state ) {
4963             this.el.removeClass('disabled');
4964         } else if (!this.el.hasClass('disabled')) {
4965             this.el.addClass('disabled');
4966         }
4967         
4968     },
4969     
4970     /**
4971      * Fetch the element to display the tooltip on.
4972      * @return {Roo.Element} defaults to this.el
4973      */
4974     tooltipEl : function()
4975     {
4976         return this.el.select('' + this.tagtype + '', true).first();
4977     },
4978     
4979     scrollToElement : function(e)
4980     {
4981         var c = document.body;
4982         
4983         /*
4984          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4985          */
4986         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4987             c = document.documentElement;
4988         }
4989         
4990         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4991         
4992         if(!target){
4993             return;
4994         }
4995
4996         var o = target.calcOffsetsTo(c);
4997         
4998         var options = {
4999             target : target,
5000             value : o[1]
5001         };
5002         
5003         this.fireEvent('scrollto', this, options, e);
5004         
5005         Roo.get(c).scrollTo('top', options.value, true);
5006         
5007         return;
5008     }
5009 });
5010  
5011
5012  /*
5013  * - LGPL
5014  *
5015  * sidebar item
5016  *
5017  *  li
5018  *    <span> icon </span>
5019  *    <span> text </span>
5020  *    <span>badge </span>
5021  */
5022
5023 /**
5024  * @class Roo.bootstrap.NavSidebarItem
5025  * @extends Roo.bootstrap.NavItem
5026  * Bootstrap Navbar.NavSidebarItem class
5027  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5028  * {Boolean} open is the menu open
5029  * {Boolean} buttonView use button as the tigger el rather that a (default false)
5030  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5031  * {String} buttonSize (sm|md|lg)the extra classes for the button
5032  * {Boolean} showArrow show arrow next to the text (default true)
5033  * @constructor
5034  * Create a new Navbar Button
5035  * @param {Object} config The config object
5036  */
5037 Roo.bootstrap.NavSidebarItem = function(config){
5038     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5039     this.addEvents({
5040         // raw events
5041         /**
5042          * @event click
5043          * The raw click event for the entire grid.
5044          * @param {Roo.EventObject} e
5045          */
5046         "click" : true,
5047          /**
5048             * @event changed
5049             * Fires when the active item active state changes
5050             * @param {Roo.bootstrap.NavSidebarItem} this
5051             * @param {boolean} state the new state
5052              
5053          */
5054         'changed': true
5055     });
5056    
5057 };
5058
5059 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5060     
5061     badgeWeight : 'default',
5062     
5063     open: false,
5064     
5065     buttonView : false,
5066     
5067     buttonWeight : 'default',
5068     
5069     buttonSize : 'md',
5070     
5071     showArrow : true,
5072     
5073     getAutoCreate : function(){
5074         
5075         
5076         var a = {
5077                 tag: 'a',
5078                 href : this.href || '#',
5079                 cls: '',
5080                 html : '',
5081                 cn : []
5082         };
5083         
5084         if(this.buttonView){
5085             a = {
5086                 tag: 'button',
5087                 href : this.href || '#',
5088                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5089                 html : this.html,
5090                 cn : []
5091             };
5092         }
5093         
5094         var cfg = {
5095             tag: 'li',
5096             cls: '',
5097             cn: [ a ]
5098         };
5099         
5100         if (this.active) {
5101             cfg.cls += ' active';
5102         }
5103         
5104         if (this.disabled) {
5105             cfg.cls += ' disabled';
5106         }
5107         if (this.open) {
5108             cfg.cls += ' open x-open';
5109         }
5110         // left icon..
5111         if (this.glyphicon || this.icon) {
5112             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5113             a.cn.push({ tag : 'i', cls : c }) ;
5114         }
5115         
5116         if(!this.buttonView){
5117             var span = {
5118                 tag: 'span',
5119                 html : this.html || ''
5120             };
5121
5122             a.cn.push(span);
5123             
5124         }
5125         
5126         if (this.badge !== '') {
5127             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5128         }
5129         
5130         if (this.menu) {
5131             
5132             if(this.showArrow){
5133                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5134             }
5135             
5136             a.cls += ' dropdown-toggle treeview' ;
5137         }
5138         
5139         return cfg;
5140     },
5141     
5142     initEvents : function()
5143     { 
5144         if (typeof (this.menu) != 'undefined') {
5145             this.menu.parentType = this.xtype;
5146             this.menu.triggerEl = this.el;
5147             this.menu = this.addxtype(Roo.apply({}, this.menu));
5148         }
5149         
5150         this.el.on('click', this.onClick, this);
5151         
5152         if(this.badge !== ''){
5153             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5154         }
5155         
5156     },
5157     
5158     onClick : function(e)
5159     {
5160         if(this.disabled){
5161             e.preventDefault();
5162             return;
5163         }
5164         
5165         if(this.preventDefault){
5166             e.preventDefault();
5167         }
5168         
5169         this.fireEvent('click', this, e);
5170     },
5171     
5172     disable : function()
5173     {
5174         this.setDisabled(true);
5175     },
5176     
5177     enable : function()
5178     {
5179         this.setDisabled(false);
5180     },
5181     
5182     setDisabled : function(state)
5183     {
5184         if(this.disabled == state){
5185             return;
5186         }
5187         
5188         this.disabled = state;
5189         
5190         if (state) {
5191             this.el.addClass('disabled');
5192             return;
5193         }
5194         
5195         this.el.removeClass('disabled');
5196         
5197         return;
5198     },
5199     
5200     setActive : function(state)
5201     {
5202         if(this.active == state){
5203             return;
5204         }
5205         
5206         this.active = state;
5207         
5208         if (state) {
5209             this.el.addClass('active');
5210             return;
5211         }
5212         
5213         this.el.removeClass('active');
5214         
5215         return;
5216     },
5217     
5218     isActive: function () 
5219     {
5220         return this.active;
5221     },
5222     
5223     setBadge : function(str)
5224     {
5225         if(!this.badgeEl){
5226             return;
5227         }
5228         
5229         this.badgeEl.dom.innerHTML = str;
5230     }
5231     
5232    
5233      
5234  
5235 });
5236  
5237
5238  /*
5239  * - LGPL
5240  *
5241  * row
5242  * 
5243  */
5244
5245 /**
5246  * @class Roo.bootstrap.Row
5247  * @extends Roo.bootstrap.Component
5248  * Bootstrap Row class (contains columns...)
5249  * 
5250  * @constructor
5251  * Create a new Row
5252  * @param {Object} config The config object
5253  */
5254
5255 Roo.bootstrap.Row = function(config){
5256     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5257 };
5258
5259 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5260     
5261     getAutoCreate : function(){
5262        return {
5263             cls: 'row clearfix'
5264        };
5265     }
5266     
5267     
5268 });
5269
5270  
5271
5272  /*
5273  * - LGPL
5274  *
5275  * element
5276  * 
5277  */
5278
5279 /**
5280  * @class Roo.bootstrap.Element
5281  * @extends Roo.bootstrap.Component
5282  * Bootstrap Element class
5283  * @cfg {String} html contents of the element
5284  * @cfg {String} tag tag of the element
5285  * @cfg {String} cls class of the element
5286  * @cfg {Boolean} preventDefault (true|false) default false
5287  * @cfg {Boolean} clickable (true|false) default false
5288  * 
5289  * @constructor
5290  * Create a new Element
5291  * @param {Object} config The config object
5292  */
5293
5294 Roo.bootstrap.Element = function(config){
5295     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5296     
5297     this.addEvents({
5298         // raw events
5299         /**
5300          * @event click
5301          * When a element is chick
5302          * @param {Roo.bootstrap.Element} this
5303          * @param {Roo.EventObject} e
5304          */
5305         "click" : true
5306     });
5307 };
5308
5309 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5310     
5311     tag: 'div',
5312     cls: '',
5313     html: '',
5314     preventDefault: false, 
5315     clickable: false,
5316     
5317     getAutoCreate : function(){
5318         
5319         var cfg = {
5320             tag: this.tag,
5321             // cls: this.cls, double assign in parent class Component.js :: onRender
5322             html: this.html
5323         };
5324         
5325         return cfg;
5326     },
5327     
5328     initEvents: function() 
5329     {
5330         Roo.bootstrap.Element.superclass.initEvents.call(this);
5331         
5332         if(this.clickable){
5333             this.el.on('click', this.onClick, this);
5334         }
5335         
5336     },
5337     
5338     onClick : function(e)
5339     {
5340         if(this.preventDefault){
5341             e.preventDefault();
5342         }
5343         
5344         this.fireEvent('click', this, e);
5345     },
5346     
5347     getValue : function()
5348     {
5349         return this.el.dom.innerHTML;
5350     },
5351     
5352     setValue : function(value)
5353     {
5354         this.el.dom.innerHTML = value;
5355     }
5356    
5357 });
5358
5359  
5360
5361  /*
5362  * - LGPL
5363  *
5364  * pagination
5365  * 
5366  */
5367
5368 /**
5369  * @class Roo.bootstrap.Pagination
5370  * @extends Roo.bootstrap.Component
5371  * Bootstrap Pagination class
5372  * @cfg {String} size xs | sm | md | lg
5373  * @cfg {Boolean} inverse false | true
5374  * 
5375  * @constructor
5376  * Create a new Pagination
5377  * @param {Object} config The config object
5378  */
5379
5380 Roo.bootstrap.Pagination = function(config){
5381     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5382 };
5383
5384 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5385     
5386     cls: false,
5387     size: false,
5388     inverse: false,
5389     
5390     getAutoCreate : function(){
5391         var cfg = {
5392             tag: 'ul',
5393                 cls: 'pagination'
5394         };
5395         if (this.inverse) {
5396             cfg.cls += ' inverse';
5397         }
5398         if (this.html) {
5399             cfg.html=this.html;
5400         }
5401         if (this.cls) {
5402             cfg.cls += " " + this.cls;
5403         }
5404         return cfg;
5405     }
5406    
5407 });
5408
5409  
5410
5411  /*
5412  * - LGPL
5413  *
5414  * Pagination item
5415  * 
5416  */
5417
5418
5419 /**
5420  * @class Roo.bootstrap.PaginationItem
5421  * @extends Roo.bootstrap.Component
5422  * Bootstrap PaginationItem class
5423  * @cfg {String} html text
5424  * @cfg {String} href the link
5425  * @cfg {Boolean} preventDefault (true | false) default true
5426  * @cfg {Boolean} active (true | false) default false
5427  * @cfg {Boolean} disabled default false
5428  * 
5429  * 
5430  * @constructor
5431  * Create a new PaginationItem
5432  * @param {Object} config The config object
5433  */
5434
5435
5436 Roo.bootstrap.PaginationItem = function(config){
5437     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5438     this.addEvents({
5439         // raw events
5440         /**
5441          * @event click
5442          * The raw click event for the entire grid.
5443          * @param {Roo.EventObject} e
5444          */
5445         "click" : true
5446     });
5447 };
5448
5449 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5450     
5451     href : false,
5452     html : false,
5453     preventDefault: true,
5454     active : false,
5455     cls : false,
5456     disabled: false,
5457     
5458     getAutoCreate : function(){
5459         var cfg= {
5460             tag: 'li',
5461             cn: [
5462                 {
5463                     tag : 'a',
5464                     href : this.href ? this.href : '#',
5465                     html : this.html ? this.html : ''
5466                 }
5467             ]
5468         };
5469         
5470         if(this.cls){
5471             cfg.cls = this.cls;
5472         }
5473         
5474         if(this.disabled){
5475             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5476         }
5477         
5478         if(this.active){
5479             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5480         }
5481         
5482         return cfg;
5483     },
5484     
5485     initEvents: function() {
5486         
5487         this.el.on('click', this.onClick, this);
5488         
5489     },
5490     onClick : function(e)
5491     {
5492         Roo.log('PaginationItem on click ');
5493         if(this.preventDefault){
5494             e.preventDefault();
5495         }
5496         
5497         if(this.disabled){
5498             return;
5499         }
5500         
5501         this.fireEvent('click', this, e);
5502     }
5503    
5504 });
5505
5506  
5507
5508  /*
5509  * - LGPL
5510  *
5511  * slider
5512  * 
5513  */
5514
5515
5516 /**
5517  * @class Roo.bootstrap.Slider
5518  * @extends Roo.bootstrap.Component
5519  * Bootstrap Slider class
5520  *    
5521  * @constructor
5522  * Create a new Slider
5523  * @param {Object} config The config object
5524  */
5525
5526 Roo.bootstrap.Slider = function(config){
5527     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5528 };
5529
5530 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5531     
5532     getAutoCreate : function(){
5533         
5534         var cfg = {
5535             tag: 'div',
5536             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5537             cn: [
5538                 {
5539                     tag: 'a',
5540                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5541                 }
5542             ]
5543         };
5544         
5545         return cfg;
5546     }
5547    
5548 });
5549
5550  /*
5551  * Based on:
5552  * Ext JS Library 1.1.1
5553  * Copyright(c) 2006-2007, Ext JS, LLC.
5554  *
5555  * Originally Released Under LGPL - original licence link has changed is not relivant.
5556  *
5557  * Fork - LGPL
5558  * <script type="text/javascript">
5559  */
5560  
5561
5562 /**
5563  * @class Roo.grid.ColumnModel
5564  * @extends Roo.util.Observable
5565  * This is the default implementation of a ColumnModel used by the Grid. It defines
5566  * the columns in the grid.
5567  * <br>Usage:<br>
5568  <pre><code>
5569  var colModel = new Roo.grid.ColumnModel([
5570         {header: "Ticker", width: 60, sortable: true, locked: true},
5571         {header: "Company Name", width: 150, sortable: true},
5572         {header: "Market Cap.", width: 100, sortable: true},
5573         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5574         {header: "Employees", width: 100, sortable: true, resizable: false}
5575  ]);
5576  </code></pre>
5577  * <p>
5578  
5579  * The config options listed for this class are options which may appear in each
5580  * individual column definition.
5581  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5582  * @constructor
5583  * @param {Object} config An Array of column config objects. See this class's
5584  * config objects for details.
5585 */
5586 Roo.grid.ColumnModel = function(config){
5587         /**
5588      * The config passed into the constructor
5589      */
5590     this.config = config;
5591     this.lookup = {};
5592
5593     // if no id, create one
5594     // if the column does not have a dataIndex mapping,
5595     // map it to the order it is in the config
5596     for(var i = 0, len = config.length; i < len; i++){
5597         var c = config[i];
5598         if(typeof c.dataIndex == "undefined"){
5599             c.dataIndex = i;
5600         }
5601         if(typeof c.renderer == "string"){
5602             c.renderer = Roo.util.Format[c.renderer];
5603         }
5604         if(typeof c.id == "undefined"){
5605             c.id = Roo.id();
5606         }
5607         if(c.editor && c.editor.xtype){
5608             c.editor  = Roo.factory(c.editor, Roo.grid);
5609         }
5610         if(c.editor && c.editor.isFormField){
5611             c.editor = new Roo.grid.GridEditor(c.editor);
5612         }
5613         this.lookup[c.id] = c;
5614     }
5615
5616     /**
5617      * The width of columns which have no width specified (defaults to 100)
5618      * @type Number
5619      */
5620     this.defaultWidth = 100;
5621
5622     /**
5623      * Default sortable of columns which have no sortable specified (defaults to false)
5624      * @type Boolean
5625      */
5626     this.defaultSortable = false;
5627
5628     this.addEvents({
5629         /**
5630              * @event widthchange
5631              * Fires when the width of a column changes.
5632              * @param {ColumnModel} this
5633              * @param {Number} columnIndex The column index
5634              * @param {Number} newWidth The new width
5635              */
5636             "widthchange": true,
5637         /**
5638              * @event headerchange
5639              * Fires when the text of a header changes.
5640              * @param {ColumnModel} this
5641              * @param {Number} columnIndex The column index
5642              * @param {Number} newText The new header text
5643              */
5644             "headerchange": true,
5645         /**
5646              * @event hiddenchange
5647              * Fires when a column is hidden or "unhidden".
5648              * @param {ColumnModel} this
5649              * @param {Number} columnIndex The column index
5650              * @param {Boolean} hidden true if hidden, false otherwise
5651              */
5652             "hiddenchange": true,
5653             /**
5654          * @event columnmoved
5655          * Fires when a column is moved.
5656          * @param {ColumnModel} this
5657          * @param {Number} oldIndex
5658          * @param {Number} newIndex
5659          */
5660         "columnmoved" : true,
5661         /**
5662          * @event columlockchange
5663          * Fires when a column's locked state is changed
5664          * @param {ColumnModel} this
5665          * @param {Number} colIndex
5666          * @param {Boolean} locked true if locked
5667          */
5668         "columnlockchange" : true
5669     });
5670     Roo.grid.ColumnModel.superclass.constructor.call(this);
5671 };
5672 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5673     /**
5674      * @cfg {String} header The header text to display in the Grid view.
5675      */
5676     /**
5677      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5678      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5679      * specified, the column's index is used as an index into the Record's data Array.
5680      */
5681     /**
5682      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5683      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5684      */
5685     /**
5686      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5687      * Defaults to the value of the {@link #defaultSortable} property.
5688      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5689      */
5690     /**
5691      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5692      */
5693     /**
5694      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5695      */
5696     /**
5697      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5698      */
5699     /**
5700      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5701      */
5702     /**
5703      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5704      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5705      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5706      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5707      */
5708        /**
5709      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5710      */
5711     /**
5712      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5713      */
5714     /**
5715      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5716      */
5717     /**
5718      * @cfg {String} cursor (Optional)
5719      */
5720     /**
5721      * @cfg {String} tooltip (Optional)
5722      */
5723     /**
5724      * @cfg {Number} xs (Optional)
5725      */
5726     /**
5727      * @cfg {Number} sm (Optional)
5728      */
5729     /**
5730      * @cfg {Number} md (Optional)
5731      */
5732     /**
5733      * @cfg {Number} lg (Optional)
5734      */
5735     /**
5736      * Returns the id of the column at the specified index.
5737      * @param {Number} index The column index
5738      * @return {String} the id
5739      */
5740     getColumnId : function(index){
5741         return this.config[index].id;
5742     },
5743
5744     /**
5745      * Returns the column for a specified id.
5746      * @param {String} id The column id
5747      * @return {Object} the column
5748      */
5749     getColumnById : function(id){
5750         return this.lookup[id];
5751     },
5752
5753     
5754     /**
5755      * Returns the column for a specified dataIndex.
5756      * @param {String} dataIndex The column dataIndex
5757      * @return {Object|Boolean} the column or false if not found
5758      */
5759     getColumnByDataIndex: function(dataIndex){
5760         var index = this.findColumnIndex(dataIndex);
5761         return index > -1 ? this.config[index] : false;
5762     },
5763     
5764     /**
5765      * Returns the index for a specified column id.
5766      * @param {String} id The column id
5767      * @return {Number} the index, or -1 if not found
5768      */
5769     getIndexById : function(id){
5770         for(var i = 0, len = this.config.length; i < len; i++){
5771             if(this.config[i].id == id){
5772                 return i;
5773             }
5774         }
5775         return -1;
5776     },
5777     
5778     /**
5779      * Returns the index for a specified column dataIndex.
5780      * @param {String} dataIndex The column dataIndex
5781      * @return {Number} the index, or -1 if not found
5782      */
5783     
5784     findColumnIndex : function(dataIndex){
5785         for(var i = 0, len = this.config.length; i < len; i++){
5786             if(this.config[i].dataIndex == dataIndex){
5787                 return i;
5788             }
5789         }
5790         return -1;
5791     },
5792     
5793     
5794     moveColumn : function(oldIndex, newIndex){
5795         var c = this.config[oldIndex];
5796         this.config.splice(oldIndex, 1);
5797         this.config.splice(newIndex, 0, c);
5798         this.dataMap = null;
5799         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5800     },
5801
5802     isLocked : function(colIndex){
5803         return this.config[colIndex].locked === true;
5804     },
5805
5806     setLocked : function(colIndex, value, suppressEvent){
5807         if(this.isLocked(colIndex) == value){
5808             return;
5809         }
5810         this.config[colIndex].locked = value;
5811         if(!suppressEvent){
5812             this.fireEvent("columnlockchange", this, colIndex, value);
5813         }
5814     },
5815
5816     getTotalLockedWidth : function(){
5817         var totalWidth = 0;
5818         for(var i = 0; i < this.config.length; i++){
5819             if(this.isLocked(i) && !this.isHidden(i)){
5820                 this.totalWidth += this.getColumnWidth(i);
5821             }
5822         }
5823         return totalWidth;
5824     },
5825
5826     getLockedCount : function(){
5827         for(var i = 0, len = this.config.length; i < len; i++){
5828             if(!this.isLocked(i)){
5829                 return i;
5830             }
5831         }
5832         
5833         return this.config.length;
5834     },
5835
5836     /**
5837      * Returns the number of columns.
5838      * @return {Number}
5839      */
5840     getColumnCount : function(visibleOnly){
5841         if(visibleOnly === true){
5842             var c = 0;
5843             for(var i = 0, len = this.config.length; i < len; i++){
5844                 if(!this.isHidden(i)){
5845                     c++;
5846                 }
5847             }
5848             return c;
5849         }
5850         return this.config.length;
5851     },
5852
5853     /**
5854      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5855      * @param {Function} fn
5856      * @param {Object} scope (optional)
5857      * @return {Array} result
5858      */
5859     getColumnsBy : function(fn, scope){
5860         var r = [];
5861         for(var i = 0, len = this.config.length; i < len; i++){
5862             var c = this.config[i];
5863             if(fn.call(scope||this, c, i) === true){
5864                 r[r.length] = c;
5865             }
5866         }
5867         return r;
5868     },
5869
5870     /**
5871      * Returns true if the specified column is sortable.
5872      * @param {Number} col The column index
5873      * @return {Boolean}
5874      */
5875     isSortable : function(col){
5876         if(typeof this.config[col].sortable == "undefined"){
5877             return this.defaultSortable;
5878         }
5879         return this.config[col].sortable;
5880     },
5881
5882     /**
5883      * Returns the rendering (formatting) function defined for the column.
5884      * @param {Number} col The column index.
5885      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5886      */
5887     getRenderer : function(col){
5888         if(!this.config[col].renderer){
5889             return Roo.grid.ColumnModel.defaultRenderer;
5890         }
5891         return this.config[col].renderer;
5892     },
5893
5894     /**
5895      * Sets the rendering (formatting) function for a column.
5896      * @param {Number} col The column index
5897      * @param {Function} fn The function to use to process the cell's raw data
5898      * to return HTML markup for the grid view. The render function is called with
5899      * the following parameters:<ul>
5900      * <li>Data value.</li>
5901      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5902      * <li>css A CSS style string to apply to the table cell.</li>
5903      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5904      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5905      * <li>Row index</li>
5906      * <li>Column index</li>
5907      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5908      */
5909     setRenderer : function(col, fn){
5910         this.config[col].renderer = fn;
5911     },
5912
5913     /**
5914      * Returns the width for the specified column.
5915      * @param {Number} col The column index
5916      * @return {Number}
5917      */
5918     getColumnWidth : function(col){
5919         return this.config[col].width * 1 || this.defaultWidth;
5920     },
5921
5922     /**
5923      * Sets the width for a column.
5924      * @param {Number} col The column index
5925      * @param {Number} width The new width
5926      */
5927     setColumnWidth : function(col, width, suppressEvent){
5928         this.config[col].width = width;
5929         this.totalWidth = null;
5930         if(!suppressEvent){
5931              this.fireEvent("widthchange", this, col, width);
5932         }
5933     },
5934
5935     /**
5936      * Returns the total width of all columns.
5937      * @param {Boolean} includeHidden True to include hidden column widths
5938      * @return {Number}
5939      */
5940     getTotalWidth : function(includeHidden){
5941         if(!this.totalWidth){
5942             this.totalWidth = 0;
5943             for(var i = 0, len = this.config.length; i < len; i++){
5944                 if(includeHidden || !this.isHidden(i)){
5945                     this.totalWidth += this.getColumnWidth(i);
5946                 }
5947             }
5948         }
5949         return this.totalWidth;
5950     },
5951
5952     /**
5953      * Returns the header for the specified column.
5954      * @param {Number} col The column index
5955      * @return {String}
5956      */
5957     getColumnHeader : function(col){
5958         return this.config[col].header;
5959     },
5960
5961     /**
5962      * Sets the header for a column.
5963      * @param {Number} col The column index
5964      * @param {String} header The new header
5965      */
5966     setColumnHeader : function(col, header){
5967         this.config[col].header = header;
5968         this.fireEvent("headerchange", this, col, header);
5969     },
5970
5971     /**
5972      * Returns the tooltip for the specified column.
5973      * @param {Number} col The column index
5974      * @return {String}
5975      */
5976     getColumnTooltip : function(col){
5977             return this.config[col].tooltip;
5978     },
5979     /**
5980      * Sets the tooltip for a column.
5981      * @param {Number} col The column index
5982      * @param {String} tooltip The new tooltip
5983      */
5984     setColumnTooltip : function(col, tooltip){
5985             this.config[col].tooltip = tooltip;
5986     },
5987
5988     /**
5989      * Returns the dataIndex for the specified column.
5990      * @param {Number} col The column index
5991      * @return {Number}
5992      */
5993     getDataIndex : function(col){
5994         return this.config[col].dataIndex;
5995     },
5996
5997     /**
5998      * Sets the dataIndex for a column.
5999      * @param {Number} col The column index
6000      * @param {Number} dataIndex The new dataIndex
6001      */
6002     setDataIndex : function(col, dataIndex){
6003         this.config[col].dataIndex = dataIndex;
6004     },
6005
6006     
6007     
6008     /**
6009      * Returns true if the cell is editable.
6010      * @param {Number} colIndex The column index
6011      * @param {Number} rowIndex The row index - this is nto actually used..?
6012      * @return {Boolean}
6013      */
6014     isCellEditable : function(colIndex, rowIndex){
6015         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6016     },
6017
6018     /**
6019      * Returns the editor defined for the cell/column.
6020      * return false or null to disable editing.
6021      * @param {Number} colIndex The column index
6022      * @param {Number} rowIndex The row index
6023      * @return {Object}
6024      */
6025     getCellEditor : function(colIndex, rowIndex){
6026         return this.config[colIndex].editor;
6027     },
6028
6029     /**
6030      * Sets if a column is editable.
6031      * @param {Number} col The column index
6032      * @param {Boolean} editable True if the column is editable
6033      */
6034     setEditable : function(col, editable){
6035         this.config[col].editable = editable;
6036     },
6037
6038
6039     /**
6040      * Returns true if the column is hidden.
6041      * @param {Number} colIndex The column index
6042      * @return {Boolean}
6043      */
6044     isHidden : function(colIndex){
6045         return this.config[colIndex].hidden;
6046     },
6047
6048
6049     /**
6050      * Returns true if the column width cannot be changed
6051      */
6052     isFixed : function(colIndex){
6053         return this.config[colIndex].fixed;
6054     },
6055
6056     /**
6057      * Returns true if the column can be resized
6058      * @return {Boolean}
6059      */
6060     isResizable : function(colIndex){
6061         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6062     },
6063     /**
6064      * Sets if a column is hidden.
6065      * @param {Number} colIndex The column index
6066      * @param {Boolean} hidden True if the column is hidden
6067      */
6068     setHidden : function(colIndex, hidden){
6069         this.config[colIndex].hidden = hidden;
6070         this.totalWidth = null;
6071         this.fireEvent("hiddenchange", this, colIndex, hidden);
6072     },
6073
6074     /**
6075      * Sets the editor for a column.
6076      * @param {Number} col The column index
6077      * @param {Object} editor The editor object
6078      */
6079     setEditor : function(col, editor){
6080         this.config[col].editor = editor;
6081     }
6082 });
6083
6084 Roo.grid.ColumnModel.defaultRenderer = function(value)
6085 {
6086     if(typeof value == "object") {
6087         return value;
6088     }
6089         if(typeof value == "string" && value.length < 1){
6090             return "&#160;";
6091         }
6092     
6093         return String.format("{0}", value);
6094 };
6095
6096 // Alias for backwards compatibility
6097 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6098 /*
6099  * Based on:
6100  * Ext JS Library 1.1.1
6101  * Copyright(c) 2006-2007, Ext JS, LLC.
6102  *
6103  * Originally Released Under LGPL - original licence link has changed is not relivant.
6104  *
6105  * Fork - LGPL
6106  * <script type="text/javascript">
6107  */
6108  
6109 /**
6110  * @class Roo.LoadMask
6111  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6112  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6113  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6114  * element's UpdateManager load indicator and will be destroyed after the initial load.
6115  * @constructor
6116  * Create a new LoadMask
6117  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6118  * @param {Object} config The config object
6119  */
6120 Roo.LoadMask = function(el, config){
6121     this.el = Roo.get(el);
6122     Roo.apply(this, config);
6123     if(this.store){
6124         this.store.on('beforeload', this.onBeforeLoad, this);
6125         this.store.on('load', this.onLoad, this);
6126         this.store.on('loadexception', this.onLoadException, this);
6127         this.removeMask = false;
6128     }else{
6129         var um = this.el.getUpdateManager();
6130         um.showLoadIndicator = false; // disable the default indicator
6131         um.on('beforeupdate', this.onBeforeLoad, this);
6132         um.on('update', this.onLoad, this);
6133         um.on('failure', this.onLoad, this);
6134         this.removeMask = true;
6135     }
6136 };
6137
6138 Roo.LoadMask.prototype = {
6139     /**
6140      * @cfg {Boolean} removeMask
6141      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6142      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6143      */
6144     /**
6145      * @cfg {String} msg
6146      * The text to display in a centered loading message box (defaults to 'Loading...')
6147      */
6148     msg : 'Loading...',
6149     /**
6150      * @cfg {String} msgCls
6151      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6152      */
6153     msgCls : 'x-mask-loading',
6154
6155     /**
6156      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6157      * @type Boolean
6158      */
6159     disabled: false,
6160
6161     /**
6162      * Disables the mask to prevent it from being displayed
6163      */
6164     disable : function(){
6165        this.disabled = true;
6166     },
6167
6168     /**
6169      * Enables the mask so that it can be displayed
6170      */
6171     enable : function(){
6172         this.disabled = false;
6173     },
6174     
6175     onLoadException : function()
6176     {
6177         Roo.log(arguments);
6178         
6179         if (typeof(arguments[3]) != 'undefined') {
6180             Roo.MessageBox.alert("Error loading",arguments[3]);
6181         } 
6182         /*
6183         try {
6184             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6185                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6186             }   
6187         } catch(e) {
6188             
6189         }
6190         */
6191     
6192         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6193     },
6194     // private
6195     onLoad : function()
6196     {
6197         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6198     },
6199
6200     // private
6201     onBeforeLoad : function(){
6202         if(!this.disabled){
6203             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6204         }
6205     },
6206
6207     // private
6208     destroy : function(){
6209         if(this.store){
6210             this.store.un('beforeload', this.onBeforeLoad, this);
6211             this.store.un('load', this.onLoad, this);
6212             this.store.un('loadexception', this.onLoadException, this);
6213         }else{
6214             var um = this.el.getUpdateManager();
6215             um.un('beforeupdate', this.onBeforeLoad, this);
6216             um.un('update', this.onLoad, this);
6217             um.un('failure', this.onLoad, this);
6218         }
6219     }
6220 };/*
6221  * - LGPL
6222  *
6223  * table
6224  * 
6225  */
6226
6227 /**
6228  * @class Roo.bootstrap.Table
6229  * @extends Roo.bootstrap.Component
6230  * Bootstrap Table class
6231  * @cfg {String} cls table class
6232  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6233  * @cfg {String} bgcolor Specifies the background color for a table
6234  * @cfg {Number} border Specifies whether the table cells should have borders or not
6235  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6236  * @cfg {Number} cellspacing Specifies the space between cells
6237  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6238  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6239  * @cfg {String} sortable Specifies that the table should be sortable
6240  * @cfg {String} summary Specifies a summary of the content of a table
6241  * @cfg {Number} width Specifies the width of a table
6242  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6243  * 
6244  * @cfg {boolean} striped Should the rows be alternative striped
6245  * @cfg {boolean} bordered Add borders to the table
6246  * @cfg {boolean} hover Add hover highlighting
6247  * @cfg {boolean} condensed Format condensed
6248  * @cfg {boolean} responsive Format condensed
6249  * @cfg {Boolean} loadMask (true|false) default false
6250  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6251  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6252  * @cfg {Boolean} rowSelection (true|false) default false
6253  * @cfg {Boolean} cellSelection (true|false) default false
6254  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6255  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6256  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6257  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6258  
6259  * 
6260  * @constructor
6261  * Create a new Table
6262  * @param {Object} config The config object
6263  */
6264
6265 Roo.bootstrap.Table = function(config){
6266     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6267     
6268   
6269     
6270     // BC...
6271     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6272     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6273     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6274     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6275     
6276     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6277     if (this.sm) {
6278         this.sm.grid = this;
6279         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6280         this.sm = this.selModel;
6281         this.sm.xmodule = this.xmodule || false;
6282     }
6283     
6284     if (this.cm && typeof(this.cm.config) == 'undefined') {
6285         this.colModel = new Roo.grid.ColumnModel(this.cm);
6286         this.cm = this.colModel;
6287         this.cm.xmodule = this.xmodule || false;
6288     }
6289     if (this.store) {
6290         this.store= Roo.factory(this.store, Roo.data);
6291         this.ds = this.store;
6292         this.ds.xmodule = this.xmodule || false;
6293          
6294     }
6295     if (this.footer && this.store) {
6296         this.footer.dataSource = this.ds;
6297         this.footer = Roo.factory(this.footer);
6298     }
6299     
6300     /** @private */
6301     this.addEvents({
6302         /**
6303          * @event cellclick
6304          * Fires when a cell is clicked
6305          * @param {Roo.bootstrap.Table} this
6306          * @param {Roo.Element} el
6307          * @param {Number} rowIndex
6308          * @param {Number} columnIndex
6309          * @param {Roo.EventObject} e
6310          */
6311         "cellclick" : true,
6312         /**
6313          * @event celldblclick
6314          * Fires when a cell is double clicked
6315          * @param {Roo.bootstrap.Table} this
6316          * @param {Roo.Element} el
6317          * @param {Number} rowIndex
6318          * @param {Number} columnIndex
6319          * @param {Roo.EventObject} e
6320          */
6321         "celldblclick" : true,
6322         /**
6323          * @event rowclick
6324          * Fires when a row is clicked
6325          * @param {Roo.bootstrap.Table} this
6326          * @param {Roo.Element} el
6327          * @param {Number} rowIndex
6328          * @param {Roo.EventObject} e
6329          */
6330         "rowclick" : true,
6331         /**
6332          * @event rowdblclick
6333          * Fires when a row is double clicked
6334          * @param {Roo.bootstrap.Table} this
6335          * @param {Roo.Element} el
6336          * @param {Number} rowIndex
6337          * @param {Roo.EventObject} e
6338          */
6339         "rowdblclick" : true,
6340         /**
6341          * @event mouseover
6342          * Fires when a mouseover occur
6343          * @param {Roo.bootstrap.Table} this
6344          * @param {Roo.Element} el
6345          * @param {Number} rowIndex
6346          * @param {Number} columnIndex
6347          * @param {Roo.EventObject} e
6348          */
6349         "mouseover" : true,
6350         /**
6351          * @event mouseout
6352          * Fires when a mouseout occur
6353          * @param {Roo.bootstrap.Table} this
6354          * @param {Roo.Element} el
6355          * @param {Number} rowIndex
6356          * @param {Number} columnIndex
6357          * @param {Roo.EventObject} e
6358          */
6359         "mouseout" : true,
6360         /**
6361          * @event rowclass
6362          * Fires when a row is rendered, so you can change add a style to it.
6363          * @param {Roo.bootstrap.Table} this
6364          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6365          */
6366         'rowclass' : true,
6367           /**
6368          * @event rowsrendered
6369          * Fires when all the  rows have been rendered
6370          * @param {Roo.bootstrap.Table} this
6371          */
6372         'rowsrendered' : true,
6373         /**
6374          * @event contextmenu
6375          * The raw contextmenu event for the entire grid.
6376          * @param {Roo.EventObject} e
6377          */
6378         "contextmenu" : true,
6379         /**
6380          * @event rowcontextmenu
6381          * Fires when a row is right clicked
6382          * @param {Roo.bootstrap.Table} this
6383          * @param {Number} rowIndex
6384          * @param {Roo.EventObject} e
6385          */
6386         "rowcontextmenu" : true,
6387         /**
6388          * @event cellcontextmenu
6389          * Fires when a cell is right clicked
6390          * @param {Roo.bootstrap.Table} this
6391          * @param {Number} rowIndex
6392          * @param {Number} cellIndex
6393          * @param {Roo.EventObject} e
6394          */
6395          "cellcontextmenu" : true,
6396          /**
6397          * @event headercontextmenu
6398          * Fires when a header is right clicked
6399          * @param {Roo.bootstrap.Table} this
6400          * @param {Number} columnIndex
6401          * @param {Roo.EventObject} e
6402          */
6403         "headercontextmenu" : true
6404     });
6405 };
6406
6407 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6408     
6409     cls: false,
6410     align: false,
6411     bgcolor: false,
6412     border: false,
6413     cellpadding: false,
6414     cellspacing: false,
6415     frame: false,
6416     rules: false,
6417     sortable: false,
6418     summary: false,
6419     width: false,
6420     striped : false,
6421     scrollBody : false,
6422     bordered: false,
6423     hover:  false,
6424     condensed : false,
6425     responsive : false,
6426     sm : false,
6427     cm : false,
6428     store : false,
6429     loadMask : false,
6430     footerShow : true,
6431     headerShow : true,
6432   
6433     rowSelection : false,
6434     cellSelection : false,
6435     layout : false,
6436     
6437     // Roo.Element - the tbody
6438     mainBody: false,
6439     // Roo.Element - thead element
6440     mainHead: false,
6441     
6442     container: false, // used by gridpanel...
6443     
6444     lazyLoad : false,
6445     
6446     CSS : Roo.util.CSS,
6447     
6448     auto_hide_footer : false,
6449     
6450     getAutoCreate : function()
6451     {
6452         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6453         
6454         cfg = {
6455             tag: 'table',
6456             cls : 'table',
6457             cn : []
6458         };
6459         if (this.scrollBody) {
6460             cfg.cls += ' table-body-fixed';
6461         }    
6462         if (this.striped) {
6463             cfg.cls += ' table-striped';
6464         }
6465         
6466         if (this.hover) {
6467             cfg.cls += ' table-hover';
6468         }
6469         if (this.bordered) {
6470             cfg.cls += ' table-bordered';
6471         }
6472         if (this.condensed) {
6473             cfg.cls += ' table-condensed';
6474         }
6475         if (this.responsive) {
6476             cfg.cls += ' table-responsive';
6477         }
6478         
6479         if (this.cls) {
6480             cfg.cls+=  ' ' +this.cls;
6481         }
6482         
6483         // this lot should be simplifed...
6484         var _t = this;
6485         var cp = [
6486             'align',
6487             'bgcolor',
6488             'border',
6489             'cellpadding',
6490             'cellspacing',
6491             'frame',
6492             'rules',
6493             'sortable',
6494             'summary',
6495             'width'
6496         ].forEach(function(k) {
6497             if (_t[k]) {
6498                 cfg[k] = _t[k];
6499             }
6500         });
6501         
6502         
6503         if (this.layout) {
6504             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6505         }
6506         
6507         if(this.store || this.cm){
6508             if(this.headerShow){
6509                 cfg.cn.push(this.renderHeader());
6510             }
6511             
6512             cfg.cn.push(this.renderBody());
6513             
6514             if(this.footerShow){
6515                 cfg.cn.push(this.renderFooter());
6516             }
6517             // where does this come from?
6518             //cfg.cls+=  ' TableGrid';
6519         }
6520         
6521         return { cn : [ cfg ] };
6522     },
6523     
6524     initEvents : function()
6525     {   
6526         if(!this.store || !this.cm){
6527             return;
6528         }
6529         if (this.selModel) {
6530             this.selModel.initEvents();
6531         }
6532         
6533         
6534         //Roo.log('initEvents with ds!!!!');
6535         
6536         this.mainBody = this.el.select('tbody', true).first();
6537         this.mainHead = this.el.select('thead', true).first();
6538         this.mainFoot = this.el.select('tfoot', true).first();
6539         
6540         
6541         
6542         var _this = this;
6543         
6544         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6545             e.on('click', _this.sort, _this);
6546         });
6547         
6548         this.mainBody.on("click", this.onClick, this);
6549         this.mainBody.on("dblclick", this.onDblClick, this);
6550         
6551         // why is this done????? = it breaks dialogs??
6552         //this.parent().el.setStyle('position', 'relative');
6553         
6554         
6555         if (this.footer) {
6556             this.footer.parentId = this.id;
6557             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6558             
6559             if(this.lazyLoad){
6560                 this.el.select('tfoot tr td').first().addClass('hide');
6561             }
6562         } 
6563         
6564         if(this.loadMask) {
6565             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6566         }
6567         
6568         this.store.on('load', this.onLoad, this);
6569         this.store.on('beforeload', this.onBeforeLoad, this);
6570         this.store.on('update', this.onUpdate, this);
6571         this.store.on('add', this.onAdd, this);
6572         this.store.on("clear", this.clear, this);
6573         
6574         this.el.on("contextmenu", this.onContextMenu, this);
6575         
6576         this.mainBody.on('scroll', this.onBodyScroll, this);
6577         
6578         this.cm.on("headerchange", this.onHeaderChange, this);
6579         
6580         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6581         
6582     },
6583     
6584     onContextMenu : function(e, t)
6585     {
6586         this.processEvent("contextmenu", e);
6587     },
6588     
6589     processEvent : function(name, e)
6590     {
6591         if (name != 'touchstart' ) {
6592             this.fireEvent(name, e);    
6593         }
6594         
6595         var t = e.getTarget();
6596         
6597         var cell = Roo.get(t);
6598         
6599         if(!cell){
6600             return;
6601         }
6602         
6603         if(cell.findParent('tfoot', false, true)){
6604             return;
6605         }
6606         
6607         if(cell.findParent('thead', false, true)){
6608             
6609             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6610                 cell = Roo.get(t).findParent('th', false, true);
6611                 if (!cell) {
6612                     Roo.log("failed to find th in thead?");
6613                     Roo.log(e.getTarget());
6614                     return;
6615                 }
6616             }
6617             
6618             var cellIndex = cell.dom.cellIndex;
6619             
6620             var ename = name == 'touchstart' ? 'click' : name;
6621             this.fireEvent("header" + ename, this, cellIndex, e);
6622             
6623             return;
6624         }
6625         
6626         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6627             cell = Roo.get(t).findParent('td', false, true);
6628             if (!cell) {
6629                 Roo.log("failed to find th in tbody?");
6630                 Roo.log(e.getTarget());
6631                 return;
6632             }
6633         }
6634         
6635         var row = cell.findParent('tr', false, true);
6636         var cellIndex = cell.dom.cellIndex;
6637         var rowIndex = row.dom.rowIndex - 1;
6638         
6639         if(row !== false){
6640             
6641             this.fireEvent("row" + name, this, rowIndex, e);
6642             
6643             if(cell !== false){
6644             
6645                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6646             }
6647         }
6648         
6649     },
6650     
6651     onMouseover : function(e, el)
6652     {
6653         var cell = Roo.get(el);
6654         
6655         if(!cell){
6656             return;
6657         }
6658         
6659         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6660             cell = cell.findParent('td', false, true);
6661         }
6662         
6663         var row = cell.findParent('tr', false, true);
6664         var cellIndex = cell.dom.cellIndex;
6665         var rowIndex = row.dom.rowIndex - 1; // start from 0
6666         
6667         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6668         
6669     },
6670     
6671     onMouseout : function(e, el)
6672     {
6673         var cell = Roo.get(el);
6674         
6675         if(!cell){
6676             return;
6677         }
6678         
6679         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6680             cell = cell.findParent('td', false, true);
6681         }
6682         
6683         var row = cell.findParent('tr', false, true);
6684         var cellIndex = cell.dom.cellIndex;
6685         var rowIndex = row.dom.rowIndex - 1; // start from 0
6686         
6687         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6688         
6689     },
6690     
6691     onClick : function(e, el)
6692     {
6693         var cell = Roo.get(el);
6694         
6695         if(!cell || (!this.cellSelection && !this.rowSelection)){
6696             return;
6697         }
6698         
6699         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6700             cell = cell.findParent('td', false, true);
6701         }
6702         
6703         if(!cell || typeof(cell) == 'undefined'){
6704             return;
6705         }
6706         
6707         var row = cell.findParent('tr', false, true);
6708         
6709         if(!row || typeof(row) == 'undefined'){
6710             return;
6711         }
6712         
6713         var cellIndex = cell.dom.cellIndex;
6714         var rowIndex = this.getRowIndex(row);
6715         
6716         // why??? - should these not be based on SelectionModel?
6717         if(this.cellSelection){
6718             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6719         }
6720         
6721         if(this.rowSelection){
6722             this.fireEvent('rowclick', this, row, rowIndex, e);
6723         }
6724         
6725         
6726     },
6727         
6728     onDblClick : function(e,el)
6729     {
6730         var cell = Roo.get(el);
6731         
6732         if(!cell || (!this.cellSelection && !this.rowSelection)){
6733             return;
6734         }
6735         
6736         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6737             cell = cell.findParent('td', false, true);
6738         }
6739         
6740         if(!cell || typeof(cell) == 'undefined'){
6741             return;
6742         }
6743         
6744         var row = cell.findParent('tr', false, true);
6745         
6746         if(!row || typeof(row) == 'undefined'){
6747             return;
6748         }
6749         
6750         var cellIndex = cell.dom.cellIndex;
6751         var rowIndex = this.getRowIndex(row);
6752         
6753         if(this.cellSelection){
6754             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6755         }
6756         
6757         if(this.rowSelection){
6758             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6759         }
6760     },
6761     
6762     sort : function(e,el)
6763     {
6764         var col = Roo.get(el);
6765         
6766         if(!col.hasClass('sortable')){
6767             return;
6768         }
6769         
6770         var sort = col.attr('sort');
6771         var dir = 'ASC';
6772         
6773         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6774             dir = 'DESC';
6775         }
6776         
6777         this.store.sortInfo = {field : sort, direction : dir};
6778         
6779         if (this.footer) {
6780             Roo.log("calling footer first");
6781             this.footer.onClick('first');
6782         } else {
6783         
6784             this.store.load({ params : { start : 0 } });
6785         }
6786     },
6787     
6788     renderHeader : function()
6789     {
6790         var header = {
6791             tag: 'thead',
6792             cn : []
6793         };
6794         
6795         var cm = this.cm;
6796         this.totalWidth = 0;
6797         
6798         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6799             
6800             var config = cm.config[i];
6801             
6802             var c = {
6803                 tag: 'th',
6804                 cls : 'x-hcol-' + i,
6805                 style : '',
6806                 html: cm.getColumnHeader(i)
6807             };
6808             
6809             var hh = '';
6810             
6811             if(typeof(config.sortable) != 'undefined' && config.sortable){
6812                 c.cls = 'sortable';
6813                 c.html = '<i class="glyphicon"></i>' + c.html;
6814             }
6815             
6816             // could use BS4 hidden-..-down 
6817             
6818             if(typeof(config.lgHeader) != 'undefined'){
6819                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
6820             }
6821             
6822             if(typeof(config.mdHeader) != 'undefined'){
6823                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6824             }
6825             
6826             if(typeof(config.smHeader) != 'undefined'){
6827                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6828             }
6829             
6830             if(typeof(config.xsHeader) != 'undefined'){
6831                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6832             }
6833             
6834             if(hh.length){
6835                 c.html = hh;
6836             }
6837             
6838             if(typeof(config.tooltip) != 'undefined'){
6839                 c.tooltip = config.tooltip;
6840             }
6841             
6842             if(typeof(config.colspan) != 'undefined'){
6843                 c.colspan = config.colspan;
6844             }
6845             
6846             if(typeof(config.hidden) != 'undefined' && config.hidden){
6847                 c.style += ' display:none;';
6848             }
6849             
6850             if(typeof(config.dataIndex) != 'undefined'){
6851                 c.sort = config.dataIndex;
6852             }
6853             
6854            
6855             
6856             if(typeof(config.align) != 'undefined' && config.align.length){
6857                 c.style += ' text-align:' + config.align + ';';
6858             }
6859             
6860             if(typeof(config.width) != 'undefined'){
6861                 c.style += ' width:' + config.width + 'px;';
6862                 this.totalWidth += config.width;
6863             } else {
6864                 this.totalWidth += 100; // assume minimum of 100 per column?
6865             }
6866             
6867             if(typeof(config.cls) != 'undefined'){
6868                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6869             }
6870             
6871             ['xs','sm','md','lg'].map(function(size){
6872                 
6873                 if(typeof(config[size]) == 'undefined'){
6874                     return;
6875                 }
6876                  
6877                 if (!config[size]) { // 0 = hidden
6878                     // BS 4 '0' is treated as hide that column and below.
6879                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
6880                     return;
6881                 }
6882                 
6883                 c.cls += ' col-' + size + '-' + config[size] + (
6884                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
6885                 );
6886                 
6887                 
6888             });
6889             
6890             header.cn.push(c)
6891         }
6892         
6893         return header;
6894     },
6895     
6896     renderBody : function()
6897     {
6898         var body = {
6899             tag: 'tbody',
6900             cn : [
6901                 {
6902                     tag: 'tr',
6903                     cn : [
6904                         {
6905                             tag : 'td',
6906                             colspan :  this.cm.getColumnCount()
6907                         }
6908                     ]
6909                 }
6910             ]
6911         };
6912         
6913         return body;
6914     },
6915     
6916     renderFooter : function()
6917     {
6918         var footer = {
6919             tag: 'tfoot',
6920             cn : [
6921                 {
6922                     tag: 'tr',
6923                     cn : [
6924                         {
6925                             tag : 'td',
6926                             colspan :  this.cm.getColumnCount()
6927                         }
6928                     ]
6929                 }
6930             ]
6931         };
6932         
6933         return footer;
6934     },
6935     
6936     
6937     
6938     onLoad : function()
6939     {
6940 //        Roo.log('ds onload');
6941         this.clear();
6942         
6943         var _this = this;
6944         var cm = this.cm;
6945         var ds = this.store;
6946         
6947         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6948             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6949             if (_this.store.sortInfo) {
6950                     
6951                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6952                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6953                 }
6954                 
6955                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6956                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6957                 }
6958             }
6959         });
6960         
6961         var tbody =  this.mainBody;
6962               
6963         if(ds.getCount() > 0){
6964             ds.data.each(function(d,rowIndex){
6965                 var row =  this.renderRow(cm, ds, rowIndex);
6966                 
6967                 tbody.createChild(row);
6968                 
6969                 var _this = this;
6970                 
6971                 if(row.cellObjects.length){
6972                     Roo.each(row.cellObjects, function(r){
6973                         _this.renderCellObject(r);
6974                     })
6975                 }
6976                 
6977             }, this);
6978         }
6979         
6980         var tfoot = this.el.select('tfoot', true).first();
6981         
6982         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6983             
6984             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6985             
6986             var total = this.ds.getTotalCount();
6987             
6988             if(this.footer.pageSize < total){
6989                 this.mainFoot.show();
6990             }
6991         }
6992         
6993         Roo.each(this.el.select('tbody td', true).elements, function(e){
6994             e.on('mouseover', _this.onMouseover, _this);
6995         });
6996         
6997         Roo.each(this.el.select('tbody td', true).elements, function(e){
6998             e.on('mouseout', _this.onMouseout, _this);
6999         });
7000         this.fireEvent('rowsrendered', this);
7001         
7002         this.autoSize();
7003     },
7004     
7005     
7006     onUpdate : function(ds,record)
7007     {
7008         this.refreshRow(record);
7009         this.autoSize();
7010     },
7011     
7012     onRemove : function(ds, record, index, isUpdate){
7013         if(isUpdate !== true){
7014             this.fireEvent("beforerowremoved", this, index, record);
7015         }
7016         var bt = this.mainBody.dom;
7017         
7018         var rows = this.el.select('tbody > tr', true).elements;
7019         
7020         if(typeof(rows[index]) != 'undefined'){
7021             bt.removeChild(rows[index].dom);
7022         }
7023         
7024 //        if(bt.rows[index]){
7025 //            bt.removeChild(bt.rows[index]);
7026 //        }
7027         
7028         if(isUpdate !== true){
7029             //this.stripeRows(index);
7030             //this.syncRowHeights(index, index);
7031             //this.layout();
7032             this.fireEvent("rowremoved", this, index, record);
7033         }
7034     },
7035     
7036     onAdd : function(ds, records, rowIndex)
7037     {
7038         //Roo.log('on Add called');
7039         // - note this does not handle multiple adding very well..
7040         var bt = this.mainBody.dom;
7041         for (var i =0 ; i < records.length;i++) {
7042             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7043             //Roo.log(records[i]);
7044             //Roo.log(this.store.getAt(rowIndex+i));
7045             this.insertRow(this.store, rowIndex + i, false);
7046             return;
7047         }
7048         
7049     },
7050     
7051     
7052     refreshRow : function(record){
7053         var ds = this.store, index;
7054         if(typeof record == 'number'){
7055             index = record;
7056             record = ds.getAt(index);
7057         }else{
7058             index = ds.indexOf(record);
7059         }
7060         this.insertRow(ds, index, true);
7061         this.autoSize();
7062         this.onRemove(ds, record, index+1, true);
7063         this.autoSize();
7064         //this.syncRowHeights(index, index);
7065         //this.layout();
7066         this.fireEvent("rowupdated", this, index, record);
7067     },
7068     
7069     insertRow : function(dm, rowIndex, isUpdate){
7070         
7071         if(!isUpdate){
7072             this.fireEvent("beforerowsinserted", this, rowIndex);
7073         }
7074             //var s = this.getScrollState();
7075         var row = this.renderRow(this.cm, this.store, rowIndex);
7076         // insert before rowIndex..
7077         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7078         
7079         var _this = this;
7080                 
7081         if(row.cellObjects.length){
7082             Roo.each(row.cellObjects, function(r){
7083                 _this.renderCellObject(r);
7084             })
7085         }
7086             
7087         if(!isUpdate){
7088             this.fireEvent("rowsinserted", this, rowIndex);
7089             //this.syncRowHeights(firstRow, lastRow);
7090             //this.stripeRows(firstRow);
7091             //this.layout();
7092         }
7093         
7094     },
7095     
7096     
7097     getRowDom : function(rowIndex)
7098     {
7099         var rows = this.el.select('tbody > tr', true).elements;
7100         
7101         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7102         
7103     },
7104     // returns the object tree for a tr..
7105   
7106     
7107     renderRow : function(cm, ds, rowIndex) 
7108     {
7109         var d = ds.getAt(rowIndex);
7110         
7111         var row = {
7112             tag : 'tr',
7113             cls : 'x-row-' + rowIndex,
7114             cn : []
7115         };
7116             
7117         var cellObjects = [];
7118         
7119         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7120             var config = cm.config[i];
7121             
7122             var renderer = cm.getRenderer(i);
7123             var value = '';
7124             var id = false;
7125             
7126             if(typeof(renderer) !== 'undefined'){
7127                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7128             }
7129             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7130             // and are rendered into the cells after the row is rendered - using the id for the element.
7131             
7132             if(typeof(value) === 'object'){
7133                 id = Roo.id();
7134                 cellObjects.push({
7135                     container : id,
7136                     cfg : value 
7137                 })
7138             }
7139             
7140             var rowcfg = {
7141                 record: d,
7142                 rowIndex : rowIndex,
7143                 colIndex : i,
7144                 rowClass : ''
7145             };
7146
7147             this.fireEvent('rowclass', this, rowcfg);
7148             
7149             var td = {
7150                 tag: 'td',
7151                 cls : rowcfg.rowClass + ' x-col-' + i,
7152                 style: '',
7153                 html: (typeof(value) === 'object') ? '' : value
7154             };
7155             
7156             if (id) {
7157                 td.id = id;
7158             }
7159             
7160             if(typeof(config.colspan) != 'undefined'){
7161                 td.colspan = config.colspan;
7162             }
7163             
7164             if(typeof(config.hidden) != 'undefined' && config.hidden){
7165                 td.style += ' display:none;';
7166             }
7167             
7168             if(typeof(config.align) != 'undefined' && config.align.length){
7169                 td.style += ' text-align:' + config.align + ';';
7170             }
7171             if(typeof(config.valign) != 'undefined' && config.valign.length){
7172                 td.style += ' vertical-align:' + config.valign + ';';
7173             }
7174             
7175             if(typeof(config.width) != 'undefined'){
7176                 td.style += ' width:' +  config.width + 'px;';
7177             }
7178             
7179             if(typeof(config.cursor) != 'undefined'){
7180                 td.style += ' cursor:' +  config.cursor + ';';
7181             }
7182             
7183             if(typeof(config.cls) != 'undefined'){
7184                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7185             }
7186             
7187             ['xs','sm','md','lg'].map(function(size){
7188                 
7189                 if(typeof(config[size]) == 'undefined'){
7190                     return;
7191                 }
7192                 
7193                 
7194                   
7195                 if (!config[size]) { // 0 = hidden
7196                     // BS 4 '0' is treated as hide that column and below.
7197                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7198                     return;
7199                 }
7200                 
7201                 td.cls += ' col-' + size + '-' + config[size] + (
7202                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
7203                 );
7204                  
7205
7206             });
7207             
7208             row.cn.push(td);
7209            
7210         }
7211         
7212         row.cellObjects = cellObjects;
7213         
7214         return row;
7215           
7216     },
7217     
7218     
7219     
7220     onBeforeLoad : function()
7221     {
7222         
7223     },
7224      /**
7225      * Remove all rows
7226      */
7227     clear : function()
7228     {
7229         this.el.select('tbody', true).first().dom.innerHTML = '';
7230     },
7231     /**
7232      * Show or hide a row.
7233      * @param {Number} rowIndex to show or hide
7234      * @param {Boolean} state hide
7235      */
7236     setRowVisibility : function(rowIndex, state)
7237     {
7238         var bt = this.mainBody.dom;
7239         
7240         var rows = this.el.select('tbody > tr', true).elements;
7241         
7242         if(typeof(rows[rowIndex]) == 'undefined'){
7243             return;
7244         }
7245         rows[rowIndex].dom.style.display = state ? '' : 'none';
7246     },
7247     
7248     
7249     getSelectionModel : function(){
7250         if(!this.selModel){
7251             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7252         }
7253         return this.selModel;
7254     },
7255     /*
7256      * Render the Roo.bootstrap object from renderder
7257      */
7258     renderCellObject : function(r)
7259     {
7260         var _this = this;
7261         
7262         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7263         
7264         var t = r.cfg.render(r.container);
7265         
7266         if(r.cfg.cn){
7267             Roo.each(r.cfg.cn, function(c){
7268                 var child = {
7269                     container: t.getChildContainer(),
7270                     cfg: c
7271                 };
7272                 _this.renderCellObject(child);
7273             })
7274         }
7275     },
7276     
7277     getRowIndex : function(row)
7278     {
7279         var rowIndex = -1;
7280         
7281         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7282             if(el != row){
7283                 return;
7284             }
7285             
7286             rowIndex = index;
7287         });
7288         
7289         return rowIndex;
7290     },
7291      /**
7292      * Returns the grid's underlying element = used by panel.Grid
7293      * @return {Element} The element
7294      */
7295     getGridEl : function(){
7296         return this.el;
7297     },
7298      /**
7299      * Forces a resize - used by panel.Grid
7300      * @return {Element} The element
7301      */
7302     autoSize : function()
7303     {
7304         //var ctr = Roo.get(this.container.dom.parentElement);
7305         var ctr = Roo.get(this.el.dom);
7306         
7307         var thd = this.getGridEl().select('thead',true).first();
7308         var tbd = this.getGridEl().select('tbody', true).first();
7309         var tfd = this.getGridEl().select('tfoot', true).first();
7310         
7311         var cw = ctr.getWidth();
7312         
7313         if (tbd) {
7314             
7315             tbd.setSize(ctr.getWidth(),
7316                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7317             );
7318             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7319             cw -= barsize;
7320         }
7321         cw = Math.max(cw, this.totalWidth);
7322         this.getGridEl().select('tr',true).setWidth(cw);
7323         // resize 'expandable coloumn?
7324         
7325         return; // we doe not have a view in this design..
7326         
7327     },
7328     onBodyScroll: function()
7329     {
7330         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7331         if(this.mainHead){
7332             this.mainHead.setStyle({
7333                 'position' : 'relative',
7334                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7335             });
7336         }
7337         
7338         if(this.lazyLoad){
7339             
7340             var scrollHeight = this.mainBody.dom.scrollHeight;
7341             
7342             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7343             
7344             var height = this.mainBody.getHeight();
7345             
7346             if(scrollHeight - height == scrollTop) {
7347                 
7348                 var total = this.ds.getTotalCount();
7349                 
7350                 if(this.footer.cursor + this.footer.pageSize < total){
7351                     
7352                     this.footer.ds.load({
7353                         params : {
7354                             start : this.footer.cursor + this.footer.pageSize,
7355                             limit : this.footer.pageSize
7356                         },
7357                         add : true
7358                     });
7359                 }
7360             }
7361             
7362         }
7363     },
7364     
7365     onHeaderChange : function()
7366     {
7367         var header = this.renderHeader();
7368         var table = this.el.select('table', true).first();
7369         
7370         this.mainHead.remove();
7371         this.mainHead = table.createChild(header, this.mainBody, false);
7372     },
7373     
7374     onHiddenChange : function(colModel, colIndex, hidden)
7375     {
7376         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7377         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7378         
7379         this.CSS.updateRule(thSelector, "display", "");
7380         this.CSS.updateRule(tdSelector, "display", "");
7381         
7382         if(hidden){
7383             this.CSS.updateRule(thSelector, "display", "none");
7384             this.CSS.updateRule(tdSelector, "display", "none");
7385         }
7386         
7387         this.onHeaderChange();
7388         this.onLoad();
7389     },
7390     
7391     setColumnWidth: function(col_index, width)
7392     {
7393         // width = "md-2 xs-2..."
7394         if(!this.colModel.config[col_index]) {
7395             return;
7396         }
7397         
7398         var w = width.split(" ");
7399         
7400         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7401         
7402         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7403         
7404         
7405         for(var j = 0; j < w.length; j++) {
7406             
7407             if(!w[j]) {
7408                 continue;
7409             }
7410             
7411             var size_cls = w[j].split("-");
7412             
7413             if(!Number.isInteger(size_cls[1] * 1)) {
7414                 continue;
7415             }
7416             
7417             if(!this.colModel.config[col_index][size_cls[0]]) {
7418                 continue;
7419             }
7420             
7421             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7422                 continue;
7423             }
7424             
7425             h_row[0].classList.replace(
7426                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7427                 "col-"+size_cls[0]+"-"+size_cls[1]
7428             );
7429             
7430             for(var i = 0; i < rows.length; i++) {
7431                 
7432                 var size_cls = w[j].split("-");
7433                 
7434                 if(!Number.isInteger(size_cls[1] * 1)) {
7435                     continue;
7436                 }
7437                 
7438                 if(!this.colModel.config[col_index][size_cls[0]]) {
7439                     continue;
7440                 }
7441                 
7442                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7443                     continue;
7444                 }
7445                 
7446                 rows[i].classList.replace(
7447                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7448                     "col-"+size_cls[0]+"-"+size_cls[1]
7449                 );
7450             }
7451             
7452             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7453         }
7454     }
7455 });
7456
7457  
7458
7459  /*
7460  * - LGPL
7461  *
7462  * table cell
7463  * 
7464  */
7465
7466 /**
7467  * @class Roo.bootstrap.TableCell
7468  * @extends Roo.bootstrap.Component
7469  * Bootstrap TableCell class
7470  * @cfg {String} html cell contain text
7471  * @cfg {String} cls cell class
7472  * @cfg {String} tag cell tag (td|th) default td
7473  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7474  * @cfg {String} align Aligns the content in a cell
7475  * @cfg {String} axis Categorizes cells
7476  * @cfg {String} bgcolor Specifies the background color of a cell
7477  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7478  * @cfg {Number} colspan Specifies the number of columns a cell should span
7479  * @cfg {String} headers Specifies one or more header cells a cell is related to
7480  * @cfg {Number} height Sets the height of a cell
7481  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7482  * @cfg {Number} rowspan Sets the number of rows a cell should span
7483  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7484  * @cfg {String} valign Vertical aligns the content in a cell
7485  * @cfg {Number} width Specifies the width of a cell
7486  * 
7487  * @constructor
7488  * Create a new TableCell
7489  * @param {Object} config The config object
7490  */
7491
7492 Roo.bootstrap.TableCell = function(config){
7493     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7494 };
7495
7496 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7497     
7498     html: false,
7499     cls: false,
7500     tag: false,
7501     abbr: false,
7502     align: false,
7503     axis: false,
7504     bgcolor: false,
7505     charoff: false,
7506     colspan: false,
7507     headers: false,
7508     height: false,
7509     nowrap: false,
7510     rowspan: false,
7511     scope: false,
7512     valign: false,
7513     width: false,
7514     
7515     
7516     getAutoCreate : function(){
7517         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7518         
7519         cfg = {
7520             tag: 'td'
7521         };
7522         
7523         if(this.tag){
7524             cfg.tag = this.tag;
7525         }
7526         
7527         if (this.html) {
7528             cfg.html=this.html
7529         }
7530         if (this.cls) {
7531             cfg.cls=this.cls
7532         }
7533         if (this.abbr) {
7534             cfg.abbr=this.abbr
7535         }
7536         if (this.align) {
7537             cfg.align=this.align
7538         }
7539         if (this.axis) {
7540             cfg.axis=this.axis
7541         }
7542         if (this.bgcolor) {
7543             cfg.bgcolor=this.bgcolor
7544         }
7545         if (this.charoff) {
7546             cfg.charoff=this.charoff
7547         }
7548         if (this.colspan) {
7549             cfg.colspan=this.colspan
7550         }
7551         if (this.headers) {
7552             cfg.headers=this.headers
7553         }
7554         if (this.height) {
7555             cfg.height=this.height
7556         }
7557         if (this.nowrap) {
7558             cfg.nowrap=this.nowrap
7559         }
7560         if (this.rowspan) {
7561             cfg.rowspan=this.rowspan
7562         }
7563         if (this.scope) {
7564             cfg.scope=this.scope
7565         }
7566         if (this.valign) {
7567             cfg.valign=this.valign
7568         }
7569         if (this.width) {
7570             cfg.width=this.width
7571         }
7572         
7573         
7574         return cfg;
7575     }
7576    
7577 });
7578
7579  
7580
7581  /*
7582  * - LGPL
7583  *
7584  * table row
7585  * 
7586  */
7587
7588 /**
7589  * @class Roo.bootstrap.TableRow
7590  * @extends Roo.bootstrap.Component
7591  * Bootstrap TableRow class
7592  * @cfg {String} cls row class
7593  * @cfg {String} align Aligns the content in a table row
7594  * @cfg {String} bgcolor Specifies a background color for a table row
7595  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7596  * @cfg {String} valign Vertical aligns the content in a table row
7597  * 
7598  * @constructor
7599  * Create a new TableRow
7600  * @param {Object} config The config object
7601  */
7602
7603 Roo.bootstrap.TableRow = function(config){
7604     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7605 };
7606
7607 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7608     
7609     cls: false,
7610     align: false,
7611     bgcolor: false,
7612     charoff: false,
7613     valign: false,
7614     
7615     getAutoCreate : function(){
7616         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7617         
7618         cfg = {
7619             tag: 'tr'
7620         };
7621             
7622         if(this.cls){
7623             cfg.cls = this.cls;
7624         }
7625         if(this.align){
7626             cfg.align = this.align;
7627         }
7628         if(this.bgcolor){
7629             cfg.bgcolor = this.bgcolor;
7630         }
7631         if(this.charoff){
7632             cfg.charoff = this.charoff;
7633         }
7634         if(this.valign){
7635             cfg.valign = this.valign;
7636         }
7637         
7638         return cfg;
7639     }
7640    
7641 });
7642
7643  
7644
7645  /*
7646  * - LGPL
7647  *
7648  * table body
7649  * 
7650  */
7651
7652 /**
7653  * @class Roo.bootstrap.TableBody
7654  * @extends Roo.bootstrap.Component
7655  * Bootstrap TableBody class
7656  * @cfg {String} cls element class
7657  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7658  * @cfg {String} align Aligns the content inside the element
7659  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7660  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7661  * 
7662  * @constructor
7663  * Create a new TableBody
7664  * @param {Object} config The config object
7665  */
7666
7667 Roo.bootstrap.TableBody = function(config){
7668     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7669 };
7670
7671 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7672     
7673     cls: false,
7674     tag: false,
7675     align: false,
7676     charoff: false,
7677     valign: false,
7678     
7679     getAutoCreate : function(){
7680         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7681         
7682         cfg = {
7683             tag: 'tbody'
7684         };
7685             
7686         if (this.cls) {
7687             cfg.cls=this.cls
7688         }
7689         if(this.tag){
7690             cfg.tag = this.tag;
7691         }
7692         
7693         if(this.align){
7694             cfg.align = this.align;
7695         }
7696         if(this.charoff){
7697             cfg.charoff = this.charoff;
7698         }
7699         if(this.valign){
7700             cfg.valign = this.valign;
7701         }
7702         
7703         return cfg;
7704     }
7705     
7706     
7707 //    initEvents : function()
7708 //    {
7709 //        
7710 //        if(!this.store){
7711 //            return;
7712 //        }
7713 //        
7714 //        this.store = Roo.factory(this.store, Roo.data);
7715 //        this.store.on('load', this.onLoad, this);
7716 //        
7717 //        this.store.load();
7718 //        
7719 //    },
7720 //    
7721 //    onLoad: function () 
7722 //    {   
7723 //        this.fireEvent('load', this);
7724 //    }
7725 //    
7726 //   
7727 });
7728
7729  
7730
7731  /*
7732  * Based on:
7733  * Ext JS Library 1.1.1
7734  * Copyright(c) 2006-2007, Ext JS, LLC.
7735  *
7736  * Originally Released Under LGPL - original licence link has changed is not relivant.
7737  *
7738  * Fork - LGPL
7739  * <script type="text/javascript">
7740  */
7741
7742 // as we use this in bootstrap.
7743 Roo.namespace('Roo.form');
7744  /**
7745  * @class Roo.form.Action
7746  * Internal Class used to handle form actions
7747  * @constructor
7748  * @param {Roo.form.BasicForm} el The form element or its id
7749  * @param {Object} config Configuration options
7750  */
7751
7752  
7753  
7754 // define the action interface
7755 Roo.form.Action = function(form, options){
7756     this.form = form;
7757     this.options = options || {};
7758 };
7759 /**
7760  * Client Validation Failed
7761  * @const 
7762  */
7763 Roo.form.Action.CLIENT_INVALID = 'client';
7764 /**
7765  * Server Validation Failed
7766  * @const 
7767  */
7768 Roo.form.Action.SERVER_INVALID = 'server';
7769  /**
7770  * Connect to Server Failed
7771  * @const 
7772  */
7773 Roo.form.Action.CONNECT_FAILURE = 'connect';
7774 /**
7775  * Reading Data from Server Failed
7776  * @const 
7777  */
7778 Roo.form.Action.LOAD_FAILURE = 'load';
7779
7780 Roo.form.Action.prototype = {
7781     type : 'default',
7782     failureType : undefined,
7783     response : undefined,
7784     result : undefined,
7785
7786     // interface method
7787     run : function(options){
7788
7789     },
7790
7791     // interface method
7792     success : function(response){
7793
7794     },
7795
7796     // interface method
7797     handleResponse : function(response){
7798
7799     },
7800
7801     // default connection failure
7802     failure : function(response){
7803         
7804         this.response = response;
7805         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7806         this.form.afterAction(this, false);
7807     },
7808
7809     processResponse : function(response){
7810         this.response = response;
7811         if(!response.responseText){
7812             return true;
7813         }
7814         this.result = this.handleResponse(response);
7815         return this.result;
7816     },
7817
7818     // utility functions used internally
7819     getUrl : function(appendParams){
7820         var url = this.options.url || this.form.url || this.form.el.dom.action;
7821         if(appendParams){
7822             var p = this.getParams();
7823             if(p){
7824                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7825             }
7826         }
7827         return url;
7828     },
7829
7830     getMethod : function(){
7831         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7832     },
7833
7834     getParams : function(){
7835         var bp = this.form.baseParams;
7836         var p = this.options.params;
7837         if(p){
7838             if(typeof p == "object"){
7839                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7840             }else if(typeof p == 'string' && bp){
7841                 p += '&' + Roo.urlEncode(bp);
7842             }
7843         }else if(bp){
7844             p = Roo.urlEncode(bp);
7845         }
7846         return p;
7847     },
7848
7849     createCallback : function(){
7850         return {
7851             success: this.success,
7852             failure: this.failure,
7853             scope: this,
7854             timeout: (this.form.timeout*1000),
7855             upload: this.form.fileUpload ? this.success : undefined
7856         };
7857     }
7858 };
7859
7860 Roo.form.Action.Submit = function(form, options){
7861     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7862 };
7863
7864 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7865     type : 'submit',
7866
7867     haveProgress : false,
7868     uploadComplete : false,
7869     
7870     // uploadProgress indicator.
7871     uploadProgress : function()
7872     {
7873         if (!this.form.progressUrl) {
7874             return;
7875         }
7876         
7877         if (!this.haveProgress) {
7878             Roo.MessageBox.progress("Uploading", "Uploading");
7879         }
7880         if (this.uploadComplete) {
7881            Roo.MessageBox.hide();
7882            return;
7883         }
7884         
7885         this.haveProgress = true;
7886    
7887         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7888         
7889         var c = new Roo.data.Connection();
7890         c.request({
7891             url : this.form.progressUrl,
7892             params: {
7893                 id : uid
7894             },
7895             method: 'GET',
7896             success : function(req){
7897                //console.log(data);
7898                 var rdata = false;
7899                 var edata;
7900                 try  {
7901                    rdata = Roo.decode(req.responseText)
7902                 } catch (e) {
7903                     Roo.log("Invalid data from server..");
7904                     Roo.log(edata);
7905                     return;
7906                 }
7907                 if (!rdata || !rdata.success) {
7908                     Roo.log(rdata);
7909                     Roo.MessageBox.alert(Roo.encode(rdata));
7910                     return;
7911                 }
7912                 var data = rdata.data;
7913                 
7914                 if (this.uploadComplete) {
7915                    Roo.MessageBox.hide();
7916                    return;
7917                 }
7918                    
7919                 if (data){
7920                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7921                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7922                     );
7923                 }
7924                 this.uploadProgress.defer(2000,this);
7925             },
7926        
7927             failure: function(data) {
7928                 Roo.log('progress url failed ');
7929                 Roo.log(data);
7930             },
7931             scope : this
7932         });
7933            
7934     },
7935     
7936     
7937     run : function()
7938     {
7939         // run get Values on the form, so it syncs any secondary forms.
7940         this.form.getValues();
7941         
7942         var o = this.options;
7943         var method = this.getMethod();
7944         var isPost = method == 'POST';
7945         if(o.clientValidation === false || this.form.isValid()){
7946             
7947             if (this.form.progressUrl) {
7948                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7949                     (new Date() * 1) + '' + Math.random());
7950                     
7951             } 
7952             
7953             
7954             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7955                 form:this.form.el.dom,
7956                 url:this.getUrl(!isPost),
7957                 method: method,
7958                 params:isPost ? this.getParams() : null,
7959                 isUpload: this.form.fileUpload,
7960                 formData : this.form.formData
7961             }));
7962             
7963             this.uploadProgress();
7964
7965         }else if (o.clientValidation !== false){ // client validation failed
7966             this.failureType = Roo.form.Action.CLIENT_INVALID;
7967             this.form.afterAction(this, false);
7968         }
7969     },
7970
7971     success : function(response)
7972     {
7973         this.uploadComplete= true;
7974         if (this.haveProgress) {
7975             Roo.MessageBox.hide();
7976         }
7977         
7978         
7979         var result = this.processResponse(response);
7980         if(result === true || result.success){
7981             this.form.afterAction(this, true);
7982             return;
7983         }
7984         if(result.errors){
7985             this.form.markInvalid(result.errors);
7986             this.failureType = Roo.form.Action.SERVER_INVALID;
7987         }
7988         this.form.afterAction(this, false);
7989     },
7990     failure : function(response)
7991     {
7992         this.uploadComplete= true;
7993         if (this.haveProgress) {
7994             Roo.MessageBox.hide();
7995         }
7996         
7997         this.response = response;
7998         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7999         this.form.afterAction(this, false);
8000     },
8001     
8002     handleResponse : function(response){
8003         if(this.form.errorReader){
8004             var rs = this.form.errorReader.read(response);
8005             var errors = [];
8006             if(rs.records){
8007                 for(var i = 0, len = rs.records.length; i < len; i++) {
8008                     var r = rs.records[i];
8009                     errors[i] = r.data;
8010                 }
8011             }
8012             if(errors.length < 1){
8013                 errors = null;
8014             }
8015             return {
8016                 success : rs.success,
8017                 errors : errors
8018             };
8019         }
8020         var ret = false;
8021         try {
8022             ret = Roo.decode(response.responseText);
8023         } catch (e) {
8024             ret = {
8025                 success: false,
8026                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8027                 errors : []
8028             };
8029         }
8030         return ret;
8031         
8032     }
8033 });
8034
8035
8036 Roo.form.Action.Load = function(form, options){
8037     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8038     this.reader = this.form.reader;
8039 };
8040
8041 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8042     type : 'load',
8043
8044     run : function(){
8045         
8046         Roo.Ajax.request(Roo.apply(
8047                 this.createCallback(), {
8048                     method:this.getMethod(),
8049                     url:this.getUrl(false),
8050                     params:this.getParams()
8051         }));
8052     },
8053
8054     success : function(response){
8055         
8056         var result = this.processResponse(response);
8057         if(result === true || !result.success || !result.data){
8058             this.failureType = Roo.form.Action.LOAD_FAILURE;
8059             this.form.afterAction(this, false);
8060             return;
8061         }
8062         this.form.clearInvalid();
8063         this.form.setValues(result.data);
8064         this.form.afterAction(this, true);
8065     },
8066
8067     handleResponse : function(response){
8068         if(this.form.reader){
8069             var rs = this.form.reader.read(response);
8070             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8071             return {
8072                 success : rs.success,
8073                 data : data
8074             };
8075         }
8076         return Roo.decode(response.responseText);
8077     }
8078 });
8079
8080 Roo.form.Action.ACTION_TYPES = {
8081     'load' : Roo.form.Action.Load,
8082     'submit' : Roo.form.Action.Submit
8083 };/*
8084  * - LGPL
8085  *
8086  * form
8087  *
8088  */
8089
8090 /**
8091  * @class Roo.bootstrap.Form
8092  * @extends Roo.bootstrap.Component
8093  * Bootstrap Form class
8094  * @cfg {String} method  GET | POST (default POST)
8095  * @cfg {String} labelAlign top | left (default top)
8096  * @cfg {String} align left  | right - for navbars
8097  * @cfg {Boolean} loadMask load mask when submit (default true)
8098
8099  *
8100  * @constructor
8101  * Create a new Form
8102  * @param {Object} config The config object
8103  */
8104
8105
8106 Roo.bootstrap.Form = function(config){
8107     
8108     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8109     
8110     Roo.bootstrap.Form.popover.apply();
8111     
8112     this.addEvents({
8113         /**
8114          * @event clientvalidation
8115          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8116          * @param {Form} this
8117          * @param {Boolean} valid true if the form has passed client-side validation
8118          */
8119         clientvalidation: true,
8120         /**
8121          * @event beforeaction
8122          * Fires before any action is performed. Return false to cancel the action.
8123          * @param {Form} this
8124          * @param {Action} action The action to be performed
8125          */
8126         beforeaction: true,
8127         /**
8128          * @event actionfailed
8129          * Fires when an action fails.
8130          * @param {Form} this
8131          * @param {Action} action The action that failed
8132          */
8133         actionfailed : true,
8134         /**
8135          * @event actioncomplete
8136          * Fires when an action is completed.
8137          * @param {Form} this
8138          * @param {Action} action The action that completed
8139          */
8140         actioncomplete : true
8141     });
8142 };
8143
8144 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8145
8146      /**
8147      * @cfg {String} method
8148      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8149      */
8150     method : 'POST',
8151     /**
8152      * @cfg {String} url
8153      * The URL to use for form actions if one isn't supplied in the action options.
8154      */
8155     /**
8156      * @cfg {Boolean} fileUpload
8157      * Set to true if this form is a file upload.
8158      */
8159
8160     /**
8161      * @cfg {Object} baseParams
8162      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8163      */
8164
8165     /**
8166      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8167      */
8168     timeout: 30,
8169     /**
8170      * @cfg {Sting} align (left|right) for navbar forms
8171      */
8172     align : 'left',
8173
8174     // private
8175     activeAction : null,
8176
8177     /**
8178      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8179      * element by passing it or its id or mask the form itself by passing in true.
8180      * @type Mixed
8181      */
8182     waitMsgTarget : false,
8183
8184     loadMask : true,
8185     
8186     /**
8187      * @cfg {Boolean} errorMask (true|false) default false
8188      */
8189     errorMask : false,
8190     
8191     /**
8192      * @cfg {Number} maskOffset Default 100
8193      */
8194     maskOffset : 100,
8195     
8196     /**
8197      * @cfg {Boolean} maskBody
8198      */
8199     maskBody : false,
8200
8201     getAutoCreate : function(){
8202
8203         var cfg = {
8204             tag: 'form',
8205             method : this.method || 'POST',
8206             id : this.id || Roo.id(),
8207             cls : ''
8208         };
8209         if (this.parent().xtype.match(/^Nav/)) {
8210             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8211
8212         }
8213
8214         if (this.labelAlign == 'left' ) {
8215             cfg.cls += ' form-horizontal';
8216         }
8217
8218
8219         return cfg;
8220     },
8221     initEvents : function()
8222     {
8223         this.el.on('submit', this.onSubmit, this);
8224         // this was added as random key presses on the form where triggering form submit.
8225         this.el.on('keypress', function(e) {
8226             if (e.getCharCode() != 13) {
8227                 return true;
8228             }
8229             // we might need to allow it for textareas.. and some other items.
8230             // check e.getTarget().
8231
8232             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8233                 return true;
8234             }
8235
8236             Roo.log("keypress blocked");
8237
8238             e.preventDefault();
8239             return false;
8240         });
8241         
8242     },
8243     // private
8244     onSubmit : function(e){
8245         e.stopEvent();
8246     },
8247
8248      /**
8249      * Returns true if client-side validation on the form is successful.
8250      * @return Boolean
8251      */
8252     isValid : function(){
8253         var items = this.getItems();
8254         var valid = true;
8255         var target = false;
8256         
8257         items.each(function(f){
8258             
8259             if(f.validate()){
8260                 return;
8261             }
8262             
8263             Roo.log('invalid field: ' + f.name);
8264             
8265             valid = false;
8266
8267             if(!target && f.el.isVisible(true)){
8268                 target = f;
8269             }
8270            
8271         });
8272         
8273         if(this.errorMask && !valid){
8274             Roo.bootstrap.Form.popover.mask(this, target);
8275         }
8276         
8277         return valid;
8278     },
8279     
8280     /**
8281      * Returns true if any fields in this form have changed since their original load.
8282      * @return Boolean
8283      */
8284     isDirty : function(){
8285         var dirty = false;
8286         var items = this.getItems();
8287         items.each(function(f){
8288            if(f.isDirty()){
8289                dirty = true;
8290                return false;
8291            }
8292            return true;
8293         });
8294         return dirty;
8295     },
8296      /**
8297      * Performs a predefined action (submit or load) or custom actions you define on this form.
8298      * @param {String} actionName The name of the action type
8299      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8300      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8301      * accept other config options):
8302      * <pre>
8303 Property          Type             Description
8304 ----------------  ---------------  ----------------------------------------------------------------------------------
8305 url               String           The url for the action (defaults to the form's url)
8306 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8307 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8308 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8309                                    validate the form on the client (defaults to false)
8310      * </pre>
8311      * @return {BasicForm} this
8312      */
8313     doAction : function(action, options){
8314         if(typeof action == 'string'){
8315             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8316         }
8317         if(this.fireEvent('beforeaction', this, action) !== false){
8318             this.beforeAction(action);
8319             action.run.defer(100, action);
8320         }
8321         return this;
8322     },
8323
8324     // private
8325     beforeAction : function(action){
8326         var o = action.options;
8327         
8328         if(this.loadMask){
8329             
8330             if(this.maskBody){
8331                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8332             } else {
8333                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8334             }
8335         }
8336         // not really supported yet.. ??
8337
8338         //if(this.waitMsgTarget === true){
8339         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8340         //}else if(this.waitMsgTarget){
8341         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8342         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8343         //}else {
8344         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8345        // }
8346
8347     },
8348
8349     // private
8350     afterAction : function(action, success){
8351         this.activeAction = null;
8352         var o = action.options;
8353
8354         if(this.loadMask){
8355             
8356             if(this.maskBody){
8357                 Roo.get(document.body).unmask();
8358             } else {
8359                 this.el.unmask();
8360             }
8361         }
8362         
8363         //if(this.waitMsgTarget === true){
8364 //            this.el.unmask();
8365         //}else if(this.waitMsgTarget){
8366         //    this.waitMsgTarget.unmask();
8367         //}else{
8368         //    Roo.MessageBox.updateProgress(1);
8369         //    Roo.MessageBox.hide();
8370        // }
8371         //
8372         if(success){
8373             if(o.reset){
8374                 this.reset();
8375             }
8376             Roo.callback(o.success, o.scope, [this, action]);
8377             this.fireEvent('actioncomplete', this, action);
8378
8379         }else{
8380
8381             // failure condition..
8382             // we have a scenario where updates need confirming.
8383             // eg. if a locking scenario exists..
8384             // we look for { errors : { needs_confirm : true }} in the response.
8385             if (
8386                 (typeof(action.result) != 'undefined')  &&
8387                 (typeof(action.result.errors) != 'undefined')  &&
8388                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8389            ){
8390                 var _t = this;
8391                 Roo.log("not supported yet");
8392                  /*
8393
8394                 Roo.MessageBox.confirm(
8395                     "Change requires confirmation",
8396                     action.result.errorMsg,
8397                     function(r) {
8398                         if (r != 'yes') {
8399                             return;
8400                         }
8401                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8402                     }
8403
8404                 );
8405                 */
8406
8407
8408                 return;
8409             }
8410
8411             Roo.callback(o.failure, o.scope, [this, action]);
8412             // show an error message if no failed handler is set..
8413             if (!this.hasListener('actionfailed')) {
8414                 Roo.log("need to add dialog support");
8415                 /*
8416                 Roo.MessageBox.alert("Error",
8417                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8418                         action.result.errorMsg :
8419                         "Saving Failed, please check your entries or try again"
8420                 );
8421                 */
8422             }
8423
8424             this.fireEvent('actionfailed', this, action);
8425         }
8426
8427     },
8428     /**
8429      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8430      * @param {String} id The value to search for
8431      * @return Field
8432      */
8433     findField : function(id){
8434         var items = this.getItems();
8435         var field = items.get(id);
8436         if(!field){
8437              items.each(function(f){
8438                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8439                     field = f;
8440                     return false;
8441                 }
8442                 return true;
8443             });
8444         }
8445         return field || null;
8446     },
8447      /**
8448      * Mark fields in this form invalid in bulk.
8449      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8450      * @return {BasicForm} this
8451      */
8452     markInvalid : function(errors){
8453         if(errors instanceof Array){
8454             for(var i = 0, len = errors.length; i < len; i++){
8455                 var fieldError = errors[i];
8456                 var f = this.findField(fieldError.id);
8457                 if(f){
8458                     f.markInvalid(fieldError.msg);
8459                 }
8460             }
8461         }else{
8462             var field, id;
8463             for(id in errors){
8464                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8465                     field.markInvalid(errors[id]);
8466                 }
8467             }
8468         }
8469         //Roo.each(this.childForms || [], function (f) {
8470         //    f.markInvalid(errors);
8471         //});
8472
8473         return this;
8474     },
8475
8476     /**
8477      * Set values for fields in this form in bulk.
8478      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8479      * @return {BasicForm} this
8480      */
8481     setValues : function(values){
8482         if(values instanceof Array){ // array of objects
8483             for(var i = 0, len = values.length; i < len; i++){
8484                 var v = values[i];
8485                 var f = this.findField(v.id);
8486                 if(f){
8487                     f.setValue(v.value);
8488                     if(this.trackResetOnLoad){
8489                         f.originalValue = f.getValue();
8490                     }
8491                 }
8492             }
8493         }else{ // object hash
8494             var field, id;
8495             for(id in values){
8496                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8497
8498                     if (field.setFromData &&
8499                         field.valueField &&
8500                         field.displayField &&
8501                         // combos' with local stores can
8502                         // be queried via setValue()
8503                         // to set their value..
8504                         (field.store && !field.store.isLocal)
8505                         ) {
8506                         // it's a combo
8507                         var sd = { };
8508                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8509                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8510                         field.setFromData(sd);
8511
8512                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8513                         
8514                         field.setFromData(values);
8515                         
8516                     } else {
8517                         field.setValue(values[id]);
8518                     }
8519
8520
8521                     if(this.trackResetOnLoad){
8522                         field.originalValue = field.getValue();
8523                     }
8524                 }
8525             }
8526         }
8527
8528         //Roo.each(this.childForms || [], function (f) {
8529         //    f.setValues(values);
8530         //});
8531
8532         return this;
8533     },
8534
8535     /**
8536      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8537      * they are returned as an array.
8538      * @param {Boolean} asString
8539      * @return {Object}
8540      */
8541     getValues : function(asString){
8542         //if (this.childForms) {
8543             // copy values from the child forms
8544         //    Roo.each(this.childForms, function (f) {
8545         //        this.setValues(f.getValues());
8546         //    }, this);
8547         //}
8548
8549
8550
8551         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8552         if(asString === true){
8553             return fs;
8554         }
8555         return Roo.urlDecode(fs);
8556     },
8557
8558     /**
8559      * Returns the fields in this form as an object with key/value pairs.
8560      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8561      * @return {Object}
8562      */
8563     getFieldValues : function(with_hidden)
8564     {
8565         var items = this.getItems();
8566         var ret = {};
8567         items.each(function(f){
8568             
8569             if (!f.getName()) {
8570                 return;
8571             }
8572             
8573             var v = f.getValue();
8574             
8575             if (f.inputType =='radio') {
8576                 if (typeof(ret[f.getName()]) == 'undefined') {
8577                     ret[f.getName()] = ''; // empty..
8578                 }
8579
8580                 if (!f.el.dom.checked) {
8581                     return;
8582
8583                 }
8584                 v = f.el.dom.value;
8585
8586             }
8587             
8588             if(f.xtype == 'MoneyField'){
8589                 ret[f.currencyName] = f.getCurrency();
8590             }
8591
8592             // not sure if this supported any more..
8593             if ((typeof(v) == 'object') && f.getRawValue) {
8594                 v = f.getRawValue() ; // dates..
8595             }
8596             // combo boxes where name != hiddenName...
8597             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8598                 ret[f.name] = f.getRawValue();
8599             }
8600             ret[f.getName()] = v;
8601         });
8602
8603         return ret;
8604     },
8605
8606     /**
8607      * Clears all invalid messages in this form.
8608      * @return {BasicForm} this
8609      */
8610     clearInvalid : function(){
8611         var items = this.getItems();
8612
8613         items.each(function(f){
8614            f.clearInvalid();
8615         });
8616
8617         return this;
8618     },
8619
8620     /**
8621      * Resets this form.
8622      * @return {BasicForm} this
8623      */
8624     reset : function(){
8625         var items = this.getItems();
8626         items.each(function(f){
8627             f.reset();
8628         });
8629
8630         Roo.each(this.childForms || [], function (f) {
8631             f.reset();
8632         });
8633
8634
8635         return this;
8636     },
8637     
8638     getItems : function()
8639     {
8640         var r=new Roo.util.MixedCollection(false, function(o){
8641             return o.id || (o.id = Roo.id());
8642         });
8643         var iter = function(el) {
8644             if (el.inputEl) {
8645                 r.add(el);
8646             }
8647             if (!el.items) {
8648                 return;
8649             }
8650             Roo.each(el.items,function(e) {
8651                 iter(e);
8652             });
8653         };
8654
8655         iter(this);
8656         return r;
8657     },
8658     
8659     hideFields : function(items)
8660     {
8661         Roo.each(items, function(i){
8662             
8663             var f = this.findField(i);
8664             
8665             if(!f){
8666                 return;
8667             }
8668             
8669             f.hide();
8670             
8671         }, this);
8672     },
8673     
8674     showFields : function(items)
8675     {
8676         Roo.each(items, function(i){
8677             
8678             var f = this.findField(i);
8679             
8680             if(!f){
8681                 return;
8682             }
8683             
8684             f.show();
8685             
8686         }, this);
8687     }
8688
8689 });
8690
8691 Roo.apply(Roo.bootstrap.Form, {
8692     
8693     popover : {
8694         
8695         padding : 5,
8696         
8697         isApplied : false,
8698         
8699         isMasked : false,
8700         
8701         form : false,
8702         
8703         target : false,
8704         
8705         toolTip : false,
8706         
8707         intervalID : false,
8708         
8709         maskEl : false,
8710         
8711         apply : function()
8712         {
8713             if(this.isApplied){
8714                 return;
8715             }
8716             
8717             this.maskEl = {
8718                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8719                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8720                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8721                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8722             };
8723             
8724             this.maskEl.top.enableDisplayMode("block");
8725             this.maskEl.left.enableDisplayMode("block");
8726             this.maskEl.bottom.enableDisplayMode("block");
8727             this.maskEl.right.enableDisplayMode("block");
8728             
8729             this.toolTip = new Roo.bootstrap.Tooltip({
8730                 cls : 'roo-form-error-popover',
8731                 alignment : {
8732                     'left' : ['r-l', [-2,0], 'right'],
8733                     'right' : ['l-r', [2,0], 'left'],
8734                     'bottom' : ['tl-bl', [0,2], 'top'],
8735                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8736                 }
8737             });
8738             
8739             this.toolTip.render(Roo.get(document.body));
8740
8741             this.toolTip.el.enableDisplayMode("block");
8742             
8743             Roo.get(document.body).on('click', function(){
8744                 this.unmask();
8745             }, this);
8746             
8747             Roo.get(document.body).on('touchstart', function(){
8748                 this.unmask();
8749             }, this);
8750             
8751             this.isApplied = true
8752         },
8753         
8754         mask : function(form, target)
8755         {
8756             this.form = form;
8757             
8758             this.target = target;
8759             
8760             if(!this.form.errorMask || !target.el){
8761                 return;
8762             }
8763             
8764             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8765             
8766             Roo.log(scrollable);
8767             
8768             var ot = this.target.el.calcOffsetsTo(scrollable);
8769             
8770             var scrollTo = ot[1] - this.form.maskOffset;
8771             
8772             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8773             
8774             scrollable.scrollTo('top', scrollTo);
8775             
8776             var box = this.target.el.getBox();
8777             Roo.log(box);
8778             var zIndex = Roo.bootstrap.Modal.zIndex++;
8779
8780             
8781             this.maskEl.top.setStyle('position', 'absolute');
8782             this.maskEl.top.setStyle('z-index', zIndex);
8783             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8784             this.maskEl.top.setLeft(0);
8785             this.maskEl.top.setTop(0);
8786             this.maskEl.top.show();
8787             
8788             this.maskEl.left.setStyle('position', 'absolute');
8789             this.maskEl.left.setStyle('z-index', zIndex);
8790             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8791             this.maskEl.left.setLeft(0);
8792             this.maskEl.left.setTop(box.y - this.padding);
8793             this.maskEl.left.show();
8794
8795             this.maskEl.bottom.setStyle('position', 'absolute');
8796             this.maskEl.bottom.setStyle('z-index', zIndex);
8797             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8798             this.maskEl.bottom.setLeft(0);
8799             this.maskEl.bottom.setTop(box.bottom + this.padding);
8800             this.maskEl.bottom.show();
8801
8802             this.maskEl.right.setStyle('position', 'absolute');
8803             this.maskEl.right.setStyle('z-index', zIndex);
8804             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8805             this.maskEl.right.setLeft(box.right + this.padding);
8806             this.maskEl.right.setTop(box.y - this.padding);
8807             this.maskEl.right.show();
8808
8809             this.toolTip.bindEl = this.target.el;
8810
8811             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8812
8813             var tip = this.target.blankText;
8814
8815             if(this.target.getValue() !== '' ) {
8816                 
8817                 if (this.target.invalidText.length) {
8818                     tip = this.target.invalidText;
8819                 } else if (this.target.regexText.length){
8820                     tip = this.target.regexText;
8821                 }
8822             }
8823
8824             this.toolTip.show(tip);
8825
8826             this.intervalID = window.setInterval(function() {
8827                 Roo.bootstrap.Form.popover.unmask();
8828             }, 10000);
8829
8830             window.onwheel = function(){ return false;};
8831             
8832             (function(){ this.isMasked = true; }).defer(500, this);
8833             
8834         },
8835         
8836         unmask : function()
8837         {
8838             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8839                 return;
8840             }
8841             
8842             this.maskEl.top.setStyle('position', 'absolute');
8843             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8844             this.maskEl.top.hide();
8845
8846             this.maskEl.left.setStyle('position', 'absolute');
8847             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8848             this.maskEl.left.hide();
8849
8850             this.maskEl.bottom.setStyle('position', 'absolute');
8851             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8852             this.maskEl.bottom.hide();
8853
8854             this.maskEl.right.setStyle('position', 'absolute');
8855             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8856             this.maskEl.right.hide();
8857             
8858             this.toolTip.hide();
8859             
8860             this.toolTip.el.hide();
8861             
8862             window.onwheel = function(){ return true;};
8863             
8864             if(this.intervalID){
8865                 window.clearInterval(this.intervalID);
8866                 this.intervalID = false;
8867             }
8868             
8869             this.isMasked = false;
8870             
8871         }
8872         
8873     }
8874     
8875 });
8876
8877 /*
8878  * Based on:
8879  * Ext JS Library 1.1.1
8880  * Copyright(c) 2006-2007, Ext JS, LLC.
8881  *
8882  * Originally Released Under LGPL - original licence link has changed is not relivant.
8883  *
8884  * Fork - LGPL
8885  * <script type="text/javascript">
8886  */
8887 /**
8888  * @class Roo.form.VTypes
8889  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8890  * @singleton
8891  */
8892 Roo.form.VTypes = function(){
8893     // closure these in so they are only created once.
8894     var alpha = /^[a-zA-Z_]+$/;
8895     var alphanum = /^[a-zA-Z0-9_]+$/;
8896     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8897     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8898
8899     // All these messages and functions are configurable
8900     return {
8901         /**
8902          * The function used to validate email addresses
8903          * @param {String} value The email address
8904          */
8905         'email' : function(v){
8906             return email.test(v);
8907         },
8908         /**
8909          * The error text to display when the email validation function returns false
8910          * @type String
8911          */
8912         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8913         /**
8914          * The keystroke filter mask to be applied on email input
8915          * @type RegExp
8916          */
8917         'emailMask' : /[a-z0-9_\.\-@]/i,
8918
8919         /**
8920          * The function used to validate URLs
8921          * @param {String} value The URL
8922          */
8923         'url' : function(v){
8924             return url.test(v);
8925         },
8926         /**
8927          * The error text to display when the url validation function returns false
8928          * @type String
8929          */
8930         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8931         
8932         /**
8933          * The function used to validate alpha values
8934          * @param {String} value The value
8935          */
8936         'alpha' : function(v){
8937             return alpha.test(v);
8938         },
8939         /**
8940          * The error text to display when the alpha validation function returns false
8941          * @type String
8942          */
8943         'alphaText' : 'This field should only contain letters and _',
8944         /**
8945          * The keystroke filter mask to be applied on alpha input
8946          * @type RegExp
8947          */
8948         'alphaMask' : /[a-z_]/i,
8949
8950         /**
8951          * The function used to validate alphanumeric values
8952          * @param {String} value The value
8953          */
8954         'alphanum' : function(v){
8955             return alphanum.test(v);
8956         },
8957         /**
8958          * The error text to display when the alphanumeric validation function returns false
8959          * @type String
8960          */
8961         'alphanumText' : 'This field should only contain letters, numbers and _',
8962         /**
8963          * The keystroke filter mask to be applied on alphanumeric input
8964          * @type RegExp
8965          */
8966         'alphanumMask' : /[a-z0-9_]/i
8967     };
8968 }();/*
8969  * - LGPL
8970  *
8971  * Input
8972  * 
8973  */
8974
8975 /**
8976  * @class Roo.bootstrap.Input
8977  * @extends Roo.bootstrap.Component
8978  * Bootstrap Input class
8979  * @cfg {Boolean} disabled is it disabled
8980  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8981  * @cfg {String} name name of the input
8982  * @cfg {string} fieldLabel - the label associated
8983  * @cfg {string} placeholder - placeholder to put in text.
8984  * @cfg {string}  before - input group add on before
8985  * @cfg {string} after - input group add on after
8986  * @cfg {string} size - (lg|sm) or leave empty..
8987  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8988  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8989  * @cfg {Number} md colspan out of 12 for computer-sized screens
8990  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8991  * @cfg {string} value default value of the input
8992  * @cfg {Number} labelWidth set the width of label 
8993  * @cfg {Number} labellg set the width of label (1-12)
8994  * @cfg {Number} labelmd set the width of label (1-12)
8995  * @cfg {Number} labelsm set the width of label (1-12)
8996  * @cfg {Number} labelxs set the width of label (1-12)
8997  * @cfg {String} labelAlign (top|left)
8998  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8999  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9000  * @cfg {String} indicatorpos (left|right) default left
9001  * @cfg {String} capture (user|camera) use for file input only. (default empty)
9002  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9003
9004  * @cfg {String} align (left|center|right) Default left
9005  * @cfg {Boolean} forceFeedback (true|false) Default false
9006  * 
9007  * @constructor
9008  * Create a new Input
9009  * @param {Object} config The config object
9010  */
9011
9012 Roo.bootstrap.Input = function(config){
9013     
9014     Roo.bootstrap.Input.superclass.constructor.call(this, config);
9015     
9016     this.addEvents({
9017         /**
9018          * @event focus
9019          * Fires when this field receives input focus.
9020          * @param {Roo.form.Field} this
9021          */
9022         focus : true,
9023         /**
9024          * @event blur
9025          * Fires when this field loses input focus.
9026          * @param {Roo.form.Field} this
9027          */
9028         blur : true,
9029         /**
9030          * @event specialkey
9031          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9032          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9033          * @param {Roo.form.Field} this
9034          * @param {Roo.EventObject} e The event object
9035          */
9036         specialkey : true,
9037         /**
9038          * @event change
9039          * Fires just before the field blurs if the field value has changed.
9040          * @param {Roo.form.Field} this
9041          * @param {Mixed} newValue The new value
9042          * @param {Mixed} oldValue The original value
9043          */
9044         change : true,
9045         /**
9046          * @event invalid
9047          * Fires after the field has been marked as invalid.
9048          * @param {Roo.form.Field} this
9049          * @param {String} msg The validation message
9050          */
9051         invalid : true,
9052         /**
9053          * @event valid
9054          * Fires after the field has been validated with no errors.
9055          * @param {Roo.form.Field} this
9056          */
9057         valid : true,
9058          /**
9059          * @event keyup
9060          * Fires after the key up
9061          * @param {Roo.form.Field} this
9062          * @param {Roo.EventObject}  e The event Object
9063          */
9064         keyup : true
9065     });
9066 };
9067
9068 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9069      /**
9070      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9071       automatic validation (defaults to "keyup").
9072      */
9073     validationEvent : "keyup",
9074      /**
9075      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9076      */
9077     validateOnBlur : true,
9078     /**
9079      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9080      */
9081     validationDelay : 250,
9082      /**
9083      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9084      */
9085     focusClass : "x-form-focus",  // not needed???
9086     
9087        
9088     /**
9089      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9090      */
9091     invalidClass : "has-warning",
9092     
9093     /**
9094      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9095      */
9096     validClass : "has-success",
9097     
9098     /**
9099      * @cfg {Boolean} hasFeedback (true|false) default true
9100      */
9101     hasFeedback : true,
9102     
9103     /**
9104      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9105      */
9106     invalidFeedbackClass : "glyphicon-warning-sign",
9107     
9108     /**
9109      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9110      */
9111     validFeedbackClass : "glyphicon-ok",
9112     
9113     /**
9114      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9115      */
9116     selectOnFocus : false,
9117     
9118      /**
9119      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9120      */
9121     maskRe : null,
9122        /**
9123      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9124      */
9125     vtype : null,
9126     
9127       /**
9128      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9129      */
9130     disableKeyFilter : false,
9131     
9132        /**
9133      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9134      */
9135     disabled : false,
9136      /**
9137      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9138      */
9139     allowBlank : true,
9140     /**
9141      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9142      */
9143     blankText : "Please complete this mandatory field",
9144     
9145      /**
9146      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9147      */
9148     minLength : 0,
9149     /**
9150      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9151      */
9152     maxLength : Number.MAX_VALUE,
9153     /**
9154      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9155      */
9156     minLengthText : "The minimum length for this field is {0}",
9157     /**
9158      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9159      */
9160     maxLengthText : "The maximum length for this field is {0}",
9161   
9162     
9163     /**
9164      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9165      * If available, this function will be called only after the basic validators all return true, and will be passed the
9166      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9167      */
9168     validator : null,
9169     /**
9170      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9171      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9172      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9173      */
9174     regex : null,
9175     /**
9176      * @cfg {String} regexText -- Depricated - use Invalid Text
9177      */
9178     regexText : "",
9179     
9180     /**
9181      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9182      */
9183     invalidText : "",
9184     
9185     
9186     
9187     autocomplete: false,
9188     
9189     
9190     fieldLabel : '',
9191     inputType : 'text',
9192     
9193     name : false,
9194     placeholder: false,
9195     before : false,
9196     after : false,
9197     size : false,
9198     hasFocus : false,
9199     preventMark: false,
9200     isFormField : true,
9201     value : '',
9202     labelWidth : 2,
9203     labelAlign : false,
9204     readOnly : false,
9205     align : false,
9206     formatedValue : false,
9207     forceFeedback : false,
9208     
9209     indicatorpos : 'left',
9210     
9211     labellg : 0,
9212     labelmd : 0,
9213     labelsm : 0,
9214     labelxs : 0,
9215     
9216     capture : '',
9217     accept : '',
9218     
9219     parentLabelAlign : function()
9220     {
9221         var parent = this;
9222         while (parent.parent()) {
9223             parent = parent.parent();
9224             if (typeof(parent.labelAlign) !='undefined') {
9225                 return parent.labelAlign;
9226             }
9227         }
9228         return 'left';
9229         
9230     },
9231     
9232     getAutoCreate : function()
9233     {
9234         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9235         
9236         var id = Roo.id();
9237         
9238         var cfg = {};
9239         
9240         if(this.inputType != 'hidden'){
9241             cfg.cls = 'form-group' //input-group
9242         }
9243         
9244         var input =  {
9245             tag: 'input',
9246             id : id,
9247             type : this.inputType,
9248             value : this.value,
9249             cls : 'form-control',
9250             placeholder : this.placeholder || '',
9251             autocomplete : this.autocomplete || 'new-password'
9252         };
9253         
9254         if(this.capture.length){
9255             input.capture = this.capture;
9256         }
9257         
9258         if(this.accept.length){
9259             input.accept = this.accept + "/*";
9260         }
9261         
9262         if(this.align){
9263             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9264         }
9265         
9266         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9267             input.maxLength = this.maxLength;
9268         }
9269         
9270         if (this.disabled) {
9271             input.disabled=true;
9272         }
9273         
9274         if (this.readOnly) {
9275             input.readonly=true;
9276         }
9277         
9278         if (this.name) {
9279             input.name = this.name;
9280         }
9281         
9282         if (this.size) {
9283             input.cls += ' input-' + this.size;
9284         }
9285         
9286         var settings=this;
9287         ['xs','sm','md','lg'].map(function(size){
9288             if (settings[size]) {
9289                 cfg.cls += ' col-' + size + '-' + settings[size];
9290             }
9291         });
9292         
9293         var inputblock = input;
9294         
9295         var feedback = {
9296             tag: 'span',
9297             cls: 'glyphicon form-control-feedback'
9298         };
9299             
9300         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9301             
9302             inputblock = {
9303                 cls : 'has-feedback',
9304                 cn :  [
9305                     input,
9306                     feedback
9307                 ] 
9308             };  
9309         }
9310         
9311         if (this.before || this.after) {
9312             
9313             inputblock = {
9314                 cls : 'input-group',
9315                 cn :  [] 
9316             };
9317             
9318             if (this.before && typeof(this.before) == 'string') {
9319                 
9320                 inputblock.cn.push({
9321                     tag :'span',
9322                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9323                     html : this.before
9324                 });
9325             }
9326             if (this.before && typeof(this.before) == 'object') {
9327                 this.before = Roo.factory(this.before);
9328                 
9329                 inputblock.cn.push({
9330                     tag :'span',
9331                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9332                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9333                 });
9334             }
9335             
9336             inputblock.cn.push(input);
9337             
9338             if (this.after && typeof(this.after) == 'string') {
9339                 inputblock.cn.push({
9340                     tag :'span',
9341                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9342                     html : this.after
9343                 });
9344             }
9345             if (this.after && typeof(this.after) == 'object') {
9346                 this.after = Roo.factory(this.after);
9347                 
9348                 inputblock.cn.push({
9349                     tag :'span',
9350                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9351                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9352                 });
9353             }
9354             
9355             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9356                 inputblock.cls += ' has-feedback';
9357                 inputblock.cn.push(feedback);
9358             }
9359         };
9360         var indicator = {
9361             tag : 'i',
9362             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9363             tooltip : 'This field is required'
9364         };
9365         if (Roo.bootstrap.version == 4) {
9366             indicator = {
9367                 tag : 'i',
9368                 style : 'display-none'
9369             };
9370         }
9371         if (align ==='left' && this.fieldLabel.length) {
9372             
9373             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9374             
9375             cfg.cn = [
9376                 indicator,
9377                 {
9378                     tag: 'label',
9379                     'for' :  id,
9380                     cls : 'control-label col-form-label',
9381                     html : this.fieldLabel
9382
9383                 },
9384                 {
9385                     cls : "", 
9386                     cn: [
9387                         inputblock
9388                     ]
9389                 }
9390             ];
9391             
9392             var labelCfg = cfg.cn[1];
9393             var contentCfg = cfg.cn[2];
9394             
9395             if(this.indicatorpos == 'right'){
9396                 cfg.cn = [
9397                     {
9398                         tag: 'label',
9399                         'for' :  id,
9400                         cls : 'control-label col-form-label',
9401                         cn : [
9402                             {
9403                                 tag : 'span',
9404                                 html : this.fieldLabel
9405                             },
9406                             indicator
9407                         ]
9408                     },
9409                     {
9410                         cls : "",
9411                         cn: [
9412                             inputblock
9413                         ]
9414                     }
9415
9416                 ];
9417                 
9418                 labelCfg = cfg.cn[0];
9419                 contentCfg = cfg.cn[1];
9420             
9421             }
9422             
9423             if(this.labelWidth > 12){
9424                 labelCfg.style = "width: " + this.labelWidth + 'px';
9425             }
9426             
9427             if(this.labelWidth < 13 && this.labelmd == 0){
9428                 this.labelmd = this.labelWidth;
9429             }
9430             
9431             if(this.labellg > 0){
9432                 labelCfg.cls += ' col-lg-' + this.labellg;
9433                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9434             }
9435             
9436             if(this.labelmd > 0){
9437                 labelCfg.cls += ' col-md-' + this.labelmd;
9438                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9439             }
9440             
9441             if(this.labelsm > 0){
9442                 labelCfg.cls += ' col-sm-' + this.labelsm;
9443                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9444             }
9445             
9446             if(this.labelxs > 0){
9447                 labelCfg.cls += ' col-xs-' + this.labelxs;
9448                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9449             }
9450             
9451             
9452         } else if ( this.fieldLabel.length) {
9453                 
9454             cfg.cn = [
9455                 {
9456                     tag : 'i',
9457                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9458                     tooltip : 'This field is required'
9459                 },
9460                 {
9461                     tag: 'label',
9462                    //cls : 'input-group-addon',
9463                     html : this.fieldLabel
9464
9465                 },
9466
9467                inputblock
9468
9469            ];
9470            
9471            if(this.indicatorpos == 'right'){
9472                 
9473                 cfg.cn = [
9474                     {
9475                         tag: 'label',
9476                        //cls : 'input-group-addon',
9477                         html : this.fieldLabel
9478
9479                     },
9480                     {
9481                         tag : 'i',
9482                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9483                         tooltip : 'This field is required'
9484                     },
9485
9486                    inputblock
9487
9488                ];
9489
9490             }
9491
9492         } else {
9493             
9494             cfg.cn = [
9495
9496                     inputblock
9497
9498             ];
9499                 
9500                 
9501         };
9502         
9503         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9504            cfg.cls += ' navbar-form';
9505         }
9506         
9507         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9508             // on BS4 we do this only if not form 
9509             cfg.cls += ' navbar-form';
9510             cfg.tag = 'li';
9511         }
9512         
9513         return cfg;
9514         
9515     },
9516     /**
9517      * return the real input element.
9518      */
9519     inputEl: function ()
9520     {
9521         return this.el.select('input.form-control',true).first();
9522     },
9523     
9524     tooltipEl : function()
9525     {
9526         return this.inputEl();
9527     },
9528     
9529     indicatorEl : function()
9530     {
9531         if (Roo.bootstrap.version == 4) {
9532             return false; // not enabled in v4 yet.
9533         }
9534         
9535         var indicator = this.el.select('i.roo-required-indicator',true).first();
9536         
9537         if(!indicator){
9538             return false;
9539         }
9540         
9541         return indicator;
9542         
9543     },
9544     
9545     setDisabled : function(v)
9546     {
9547         var i  = this.inputEl().dom;
9548         if (!v) {
9549             i.removeAttribute('disabled');
9550             return;
9551             
9552         }
9553         i.setAttribute('disabled','true');
9554     },
9555     initEvents : function()
9556     {
9557           
9558         this.inputEl().on("keydown" , this.fireKey,  this);
9559         this.inputEl().on("focus", this.onFocus,  this);
9560         this.inputEl().on("blur", this.onBlur,  this);
9561         
9562         this.inputEl().relayEvent('keyup', this);
9563         
9564         this.indicator = this.indicatorEl();
9565         
9566         if(this.indicator){
9567             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9568         }
9569  
9570         // reference to original value for reset
9571         this.originalValue = this.getValue();
9572         //Roo.form.TextField.superclass.initEvents.call(this);
9573         if(this.validationEvent == 'keyup'){
9574             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9575             this.inputEl().on('keyup', this.filterValidation, this);
9576         }
9577         else if(this.validationEvent !== false){
9578             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9579         }
9580         
9581         if(this.selectOnFocus){
9582             this.on("focus", this.preFocus, this);
9583             
9584         }
9585         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9586             this.inputEl().on("keypress", this.filterKeys, this);
9587         } else {
9588             this.inputEl().relayEvent('keypress', this);
9589         }
9590        /* if(this.grow){
9591             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9592             this.el.on("click", this.autoSize,  this);
9593         }
9594         */
9595         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9596             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9597         }
9598         
9599         if (typeof(this.before) == 'object') {
9600             this.before.render(this.el.select('.roo-input-before',true).first());
9601         }
9602         if (typeof(this.after) == 'object') {
9603             this.after.render(this.el.select('.roo-input-after',true).first());
9604         }
9605         
9606         this.inputEl().on('change', this.onChange, this);
9607         
9608     },
9609     filterValidation : function(e){
9610         if(!e.isNavKeyPress()){
9611             this.validationTask.delay(this.validationDelay);
9612         }
9613     },
9614      /**
9615      * Validates the field value
9616      * @return {Boolean} True if the value is valid, else false
9617      */
9618     validate : function(){
9619         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9620         if(this.disabled || this.validateValue(this.getRawValue())){
9621             this.markValid();
9622             return true;
9623         }
9624         
9625         this.markInvalid();
9626         return false;
9627     },
9628     
9629     
9630     /**
9631      * Validates a value according to the field's validation rules and marks the field as invalid
9632      * if the validation fails
9633      * @param {Mixed} value The value to validate
9634      * @return {Boolean} True if the value is valid, else false
9635      */
9636     validateValue : function(value)
9637     {
9638         if(this.getVisibilityEl().hasClass('hidden')){
9639             return true;
9640         }
9641         
9642         if(value.length < 1)  { // if it's blank
9643             if(this.allowBlank){
9644                 return true;
9645             }
9646             return false;
9647         }
9648         
9649         if(value.length < this.minLength){
9650             return false;
9651         }
9652         if(value.length > this.maxLength){
9653             return false;
9654         }
9655         if(this.vtype){
9656             var vt = Roo.form.VTypes;
9657             if(!vt[this.vtype](value, this)){
9658                 return false;
9659             }
9660         }
9661         if(typeof this.validator == "function"){
9662             var msg = this.validator(value);
9663             if(msg !== true){
9664                 return false;
9665             }
9666             if (typeof(msg) == 'string') {
9667                 this.invalidText = msg;
9668             }
9669         }
9670         
9671         if(this.regex && !this.regex.test(value)){
9672             return false;
9673         }
9674         
9675         return true;
9676     },
9677     
9678      // private
9679     fireKey : function(e){
9680         //Roo.log('field ' + e.getKey());
9681         if(e.isNavKeyPress()){
9682             this.fireEvent("specialkey", this, e);
9683         }
9684     },
9685     focus : function (selectText){
9686         if(this.rendered){
9687             this.inputEl().focus();
9688             if(selectText === true){
9689                 this.inputEl().dom.select();
9690             }
9691         }
9692         return this;
9693     } ,
9694     
9695     onFocus : function(){
9696         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9697            // this.el.addClass(this.focusClass);
9698         }
9699         if(!this.hasFocus){
9700             this.hasFocus = true;
9701             this.startValue = this.getValue();
9702             this.fireEvent("focus", this);
9703         }
9704     },
9705     
9706     beforeBlur : Roo.emptyFn,
9707
9708     
9709     // private
9710     onBlur : function(){
9711         this.beforeBlur();
9712         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9713             //this.el.removeClass(this.focusClass);
9714         }
9715         this.hasFocus = false;
9716         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9717             this.validate();
9718         }
9719         var v = this.getValue();
9720         if(String(v) !== String(this.startValue)){
9721             this.fireEvent('change', this, v, this.startValue);
9722         }
9723         this.fireEvent("blur", this);
9724     },
9725     
9726     onChange : function(e)
9727     {
9728         var v = this.getValue();
9729         if(String(v) !== String(this.startValue)){
9730             this.fireEvent('change', this, v, this.startValue);
9731         }
9732         
9733     },
9734     
9735     /**
9736      * Resets the current field value to the originally loaded value and clears any validation messages
9737      */
9738     reset : function(){
9739         this.setValue(this.originalValue);
9740         this.validate();
9741     },
9742      /**
9743      * Returns the name of the field
9744      * @return {Mixed} name The name field
9745      */
9746     getName: function(){
9747         return this.name;
9748     },
9749      /**
9750      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9751      * @return {Mixed} value The field value
9752      */
9753     getValue : function(){
9754         
9755         var v = this.inputEl().getValue();
9756         
9757         return v;
9758     },
9759     /**
9760      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9761      * @return {Mixed} value The field value
9762      */
9763     getRawValue : function(){
9764         var v = this.inputEl().getValue();
9765         
9766         return v;
9767     },
9768     
9769     /**
9770      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9771      * @param {Mixed} value The value to set
9772      */
9773     setRawValue : function(v){
9774         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9775     },
9776     
9777     selectText : function(start, end){
9778         var v = this.getRawValue();
9779         if(v.length > 0){
9780             start = start === undefined ? 0 : start;
9781             end = end === undefined ? v.length : end;
9782             var d = this.inputEl().dom;
9783             if(d.setSelectionRange){
9784                 d.setSelectionRange(start, end);
9785             }else if(d.createTextRange){
9786                 var range = d.createTextRange();
9787                 range.moveStart("character", start);
9788                 range.moveEnd("character", v.length-end);
9789                 range.select();
9790             }
9791         }
9792     },
9793     
9794     /**
9795      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9796      * @param {Mixed} value The value to set
9797      */
9798     setValue : function(v){
9799         this.value = v;
9800         if(this.rendered){
9801             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9802             this.validate();
9803         }
9804     },
9805     
9806     /*
9807     processValue : function(value){
9808         if(this.stripCharsRe){
9809             var newValue = value.replace(this.stripCharsRe, '');
9810             if(newValue !== value){
9811                 this.setRawValue(newValue);
9812                 return newValue;
9813             }
9814         }
9815         return value;
9816     },
9817   */
9818     preFocus : function(){
9819         
9820         if(this.selectOnFocus){
9821             this.inputEl().dom.select();
9822         }
9823     },
9824     filterKeys : function(e){
9825         var k = e.getKey();
9826         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9827             return;
9828         }
9829         var c = e.getCharCode(), cc = String.fromCharCode(c);
9830         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9831             return;
9832         }
9833         if(!this.maskRe.test(cc)){
9834             e.stopEvent();
9835         }
9836     },
9837      /**
9838      * Clear any invalid styles/messages for this field
9839      */
9840     clearInvalid : function(){
9841         
9842         if(!this.el || this.preventMark){ // not rendered
9843             return;
9844         }
9845         
9846         
9847         this.el.removeClass([this.invalidClass, 'is-invalid']);
9848         
9849         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9850             
9851             var feedback = this.el.select('.form-control-feedback', true).first();
9852             
9853             if(feedback){
9854                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9855             }
9856             
9857         }
9858         
9859         if(this.indicator){
9860             this.indicator.removeClass('visible');
9861             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9862         }
9863         
9864         this.fireEvent('valid', this);
9865     },
9866     
9867      /**
9868      * Mark this field as valid
9869      */
9870     markValid : function()
9871     {
9872         if(!this.el  || this.preventMark){ // not rendered...
9873             return;
9874         }
9875         
9876         this.el.removeClass([this.invalidClass, this.validClass]);
9877         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9878
9879         var feedback = this.el.select('.form-control-feedback', true).first();
9880             
9881         if(feedback){
9882             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9883         }
9884         
9885         if(this.indicator){
9886             this.indicator.removeClass('visible');
9887             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9888         }
9889         
9890         if(this.disabled){
9891             return;
9892         }
9893         
9894         if(this.allowBlank && !this.getRawValue().length){
9895             return;
9896         }
9897         if (Roo.bootstrap.version == 3) {
9898             this.el.addClass(this.validClass);
9899         } else {
9900             this.inputEl().addClass('is-valid');
9901         }
9902
9903         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9904             
9905             var feedback = this.el.select('.form-control-feedback', true).first();
9906             
9907             if(feedback){
9908                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9909                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9910             }
9911             
9912         }
9913         
9914         this.fireEvent('valid', this);
9915     },
9916     
9917      /**
9918      * Mark this field as invalid
9919      * @param {String} msg The validation message
9920      */
9921     markInvalid : function(msg)
9922     {
9923         if(!this.el  || this.preventMark){ // not rendered
9924             return;
9925         }
9926         
9927         this.el.removeClass([this.invalidClass, this.validClass]);
9928         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9929         
9930         var feedback = this.el.select('.form-control-feedback', true).first();
9931             
9932         if(feedback){
9933             this.el.select('.form-control-feedback', true).first().removeClass(
9934                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9935         }
9936
9937         if(this.disabled){
9938             return;
9939         }
9940         
9941         if(this.allowBlank && !this.getRawValue().length){
9942             return;
9943         }
9944         
9945         if(this.indicator){
9946             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9947             this.indicator.addClass('visible');
9948         }
9949         if (Roo.bootstrap.version == 3) {
9950             this.el.addClass(this.invalidClass);
9951         } else {
9952             this.inputEl().addClass('is-invalid');
9953         }
9954         
9955         
9956         
9957         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9958             
9959             var feedback = this.el.select('.form-control-feedback', true).first();
9960             
9961             if(feedback){
9962                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9963                 
9964                 if(this.getValue().length || this.forceFeedback){
9965                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9966                 }
9967                 
9968             }
9969             
9970         }
9971         
9972         this.fireEvent('invalid', this, msg);
9973     },
9974     // private
9975     SafariOnKeyDown : function(event)
9976     {
9977         // this is a workaround for a password hang bug on chrome/ webkit.
9978         if (this.inputEl().dom.type != 'password') {
9979             return;
9980         }
9981         
9982         var isSelectAll = false;
9983         
9984         if(this.inputEl().dom.selectionEnd > 0){
9985             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9986         }
9987         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9988             event.preventDefault();
9989             this.setValue('');
9990             return;
9991         }
9992         
9993         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9994             
9995             event.preventDefault();
9996             // this is very hacky as keydown always get's upper case.
9997             //
9998             var cc = String.fromCharCode(event.getCharCode());
9999             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
10000             
10001         }
10002     },
10003     adjustWidth : function(tag, w){
10004         tag = tag.toLowerCase();
10005         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10006             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10007                 if(tag == 'input'){
10008                     return w + 2;
10009                 }
10010                 if(tag == 'textarea'){
10011                     return w-2;
10012                 }
10013             }else if(Roo.isOpera){
10014                 if(tag == 'input'){
10015                     return w + 2;
10016                 }
10017                 if(tag == 'textarea'){
10018                     return w-2;
10019                 }
10020             }
10021         }
10022         return w;
10023     },
10024     
10025     setFieldLabel : function(v)
10026     {
10027         if(!this.rendered){
10028             return;
10029         }
10030         
10031         if(this.indicatorEl()){
10032             var ar = this.el.select('label > span',true);
10033             
10034             if (ar.elements.length) {
10035                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10036                 this.fieldLabel = v;
10037                 return;
10038             }
10039             
10040             var br = this.el.select('label',true);
10041             
10042             if(br.elements.length) {
10043                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10044                 this.fieldLabel = v;
10045                 return;
10046             }
10047             
10048             Roo.log('Cannot Found any of label > span || label in input');
10049             return;
10050         }
10051         
10052         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10053         this.fieldLabel = v;
10054         
10055         
10056     }
10057 });
10058
10059  
10060 /*
10061  * - LGPL
10062  *
10063  * Input
10064  * 
10065  */
10066
10067 /**
10068  * @class Roo.bootstrap.TextArea
10069  * @extends Roo.bootstrap.Input
10070  * Bootstrap TextArea class
10071  * @cfg {Number} cols Specifies the visible width of a text area
10072  * @cfg {Number} rows Specifies the visible number of lines in a text area
10073  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10074  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10075  * @cfg {string} html text
10076  * 
10077  * @constructor
10078  * Create a new TextArea
10079  * @param {Object} config The config object
10080  */
10081
10082 Roo.bootstrap.TextArea = function(config){
10083     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10084    
10085 };
10086
10087 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10088      
10089     cols : false,
10090     rows : 5,
10091     readOnly : false,
10092     warp : 'soft',
10093     resize : false,
10094     value: false,
10095     html: false,
10096     
10097     getAutoCreate : function(){
10098         
10099         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10100         
10101         var id = Roo.id();
10102         
10103         var cfg = {};
10104         
10105         if(this.inputType != 'hidden'){
10106             cfg.cls = 'form-group' //input-group
10107         }
10108         
10109         var input =  {
10110             tag: 'textarea',
10111             id : id,
10112             warp : this.warp,
10113             rows : this.rows,
10114             value : this.value || '',
10115             html: this.html || '',
10116             cls : 'form-control',
10117             placeholder : this.placeholder || '' 
10118             
10119         };
10120         
10121         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10122             input.maxLength = this.maxLength;
10123         }
10124         
10125         if(this.resize){
10126             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10127         }
10128         
10129         if(this.cols){
10130             input.cols = this.cols;
10131         }
10132         
10133         if (this.readOnly) {
10134             input.readonly = true;
10135         }
10136         
10137         if (this.name) {
10138             input.name = this.name;
10139         }
10140         
10141         if (this.size) {
10142             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10143         }
10144         
10145         var settings=this;
10146         ['xs','sm','md','lg'].map(function(size){
10147             if (settings[size]) {
10148                 cfg.cls += ' col-' + size + '-' + settings[size];
10149             }
10150         });
10151         
10152         var inputblock = input;
10153         
10154         if(this.hasFeedback && !this.allowBlank){
10155             
10156             var feedback = {
10157                 tag: 'span',
10158                 cls: 'glyphicon form-control-feedback'
10159             };
10160
10161             inputblock = {
10162                 cls : 'has-feedback',
10163                 cn :  [
10164                     input,
10165                     feedback
10166                 ] 
10167             };  
10168         }
10169         
10170         
10171         if (this.before || this.after) {
10172             
10173             inputblock = {
10174                 cls : 'input-group',
10175                 cn :  [] 
10176             };
10177             if (this.before) {
10178                 inputblock.cn.push({
10179                     tag :'span',
10180                     cls : 'input-group-addon',
10181                     html : this.before
10182                 });
10183             }
10184             
10185             inputblock.cn.push(input);
10186             
10187             if(this.hasFeedback && !this.allowBlank){
10188                 inputblock.cls += ' has-feedback';
10189                 inputblock.cn.push(feedback);
10190             }
10191             
10192             if (this.after) {
10193                 inputblock.cn.push({
10194                     tag :'span',
10195                     cls : 'input-group-addon',
10196                     html : this.after
10197                 });
10198             }
10199             
10200         }
10201         
10202         if (align ==='left' && this.fieldLabel.length) {
10203             cfg.cn = [
10204                 {
10205                     tag: 'label',
10206                     'for' :  id,
10207                     cls : 'control-label',
10208                     html : this.fieldLabel
10209                 },
10210                 {
10211                     cls : "",
10212                     cn: [
10213                         inputblock
10214                     ]
10215                 }
10216
10217             ];
10218             
10219             if(this.labelWidth > 12){
10220                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10221             }
10222
10223             if(this.labelWidth < 13 && this.labelmd == 0){
10224                 this.labelmd = this.labelWidth;
10225             }
10226
10227             if(this.labellg > 0){
10228                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10229                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10230             }
10231
10232             if(this.labelmd > 0){
10233                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10234                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10235             }
10236
10237             if(this.labelsm > 0){
10238                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10239                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10240             }
10241
10242             if(this.labelxs > 0){
10243                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10244                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10245             }
10246             
10247         } else if ( this.fieldLabel.length) {
10248             cfg.cn = [
10249
10250                {
10251                    tag: 'label',
10252                    //cls : 'input-group-addon',
10253                    html : this.fieldLabel
10254
10255                },
10256
10257                inputblock
10258
10259            ];
10260
10261         } else {
10262
10263             cfg.cn = [
10264
10265                 inputblock
10266
10267             ];
10268                 
10269         }
10270         
10271         if (this.disabled) {
10272             input.disabled=true;
10273         }
10274         
10275         return cfg;
10276         
10277     },
10278     /**
10279      * return the real textarea element.
10280      */
10281     inputEl: function ()
10282     {
10283         return this.el.select('textarea.form-control',true).first();
10284     },
10285     
10286     /**
10287      * Clear any invalid styles/messages for this field
10288      */
10289     clearInvalid : function()
10290     {
10291         
10292         if(!this.el || this.preventMark){ // not rendered
10293             return;
10294         }
10295         
10296         var label = this.el.select('label', true).first();
10297         var icon = this.el.select('i.fa-star', true).first();
10298         
10299         if(label && icon){
10300             icon.remove();
10301         }
10302         this.el.removeClass( this.validClass);
10303         this.inputEl().removeClass('is-invalid');
10304          
10305         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10306             
10307             var feedback = this.el.select('.form-control-feedback', true).first();
10308             
10309             if(feedback){
10310                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10311             }
10312             
10313         }
10314         
10315         this.fireEvent('valid', this);
10316     },
10317     
10318      /**
10319      * Mark this field as valid
10320      */
10321     markValid : function()
10322     {
10323         if(!this.el  || this.preventMark){ // not rendered
10324             return;
10325         }
10326         
10327         this.el.removeClass([this.invalidClass, this.validClass]);
10328         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10329         
10330         var feedback = this.el.select('.form-control-feedback', true).first();
10331             
10332         if(feedback){
10333             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10334         }
10335
10336         if(this.disabled || this.allowBlank){
10337             return;
10338         }
10339         
10340         var label = this.el.select('label', true).first();
10341         var icon = this.el.select('i.fa-star', true).first();
10342         
10343         if(label && icon){
10344             icon.remove();
10345         }
10346         if (Roo.bootstrap.version == 3) {
10347             this.el.addClass(this.validClass);
10348         } else {
10349             this.inputEl().addClass('is-valid');
10350         }
10351         
10352         
10353         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10354             
10355             var feedback = this.el.select('.form-control-feedback', true).first();
10356             
10357             if(feedback){
10358                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10359                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10360             }
10361             
10362         }
10363         
10364         this.fireEvent('valid', this);
10365     },
10366     
10367      /**
10368      * Mark this field as invalid
10369      * @param {String} msg The validation message
10370      */
10371     markInvalid : function(msg)
10372     {
10373         if(!this.el  || this.preventMark){ // not rendered
10374             return;
10375         }
10376         
10377         this.el.removeClass([this.invalidClass, this.validClass]);
10378         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10379         
10380         var feedback = this.el.select('.form-control-feedback', true).first();
10381             
10382         if(feedback){
10383             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10384         }
10385
10386         if(this.disabled || this.allowBlank){
10387             return;
10388         }
10389         
10390         var label = this.el.select('label', true).first();
10391         var icon = this.el.select('i.fa-star', true).first();
10392         
10393         if(!this.getValue().length && label && !icon){
10394             this.el.createChild({
10395                 tag : 'i',
10396                 cls : 'text-danger fa fa-lg fa-star',
10397                 tooltip : 'This field is required',
10398                 style : 'margin-right:5px;'
10399             }, label, true);
10400         }
10401         
10402         if (Roo.bootstrap.version == 3) {
10403             this.el.addClass(this.invalidClass);
10404         } else {
10405             this.inputEl().addClass('is-invalid');
10406         }
10407         
10408         // fixme ... this may be depricated need to test..
10409         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10410             
10411             var feedback = this.el.select('.form-control-feedback', true).first();
10412             
10413             if(feedback){
10414                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10415                 
10416                 if(this.getValue().length || this.forceFeedback){
10417                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10418                 }
10419                 
10420             }
10421             
10422         }
10423         
10424         this.fireEvent('invalid', this, msg);
10425     }
10426 });
10427
10428  
10429 /*
10430  * - LGPL
10431  *
10432  * trigger field - base class for combo..
10433  * 
10434  */
10435  
10436 /**
10437  * @class Roo.bootstrap.TriggerField
10438  * @extends Roo.bootstrap.Input
10439  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10440  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10441  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10442  * for which you can provide a custom implementation.  For example:
10443  * <pre><code>
10444 var trigger = new Roo.bootstrap.TriggerField();
10445 trigger.onTriggerClick = myTriggerFn;
10446 trigger.applyTo('my-field');
10447 </code></pre>
10448  *
10449  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10450  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10451  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10452  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10453  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10454
10455  * @constructor
10456  * Create a new TriggerField.
10457  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10458  * to the base TextField)
10459  */
10460 Roo.bootstrap.TriggerField = function(config){
10461     this.mimicing = false;
10462     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10463 };
10464
10465 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10466     /**
10467      * @cfg {String} triggerClass A CSS class to apply to the trigger
10468      */
10469      /**
10470      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10471      */
10472     hideTrigger:false,
10473
10474     /**
10475      * @cfg {Boolean} removable (true|false) special filter default false
10476      */
10477     removable : false,
10478     
10479     /** @cfg {Boolean} grow @hide */
10480     /** @cfg {Number} growMin @hide */
10481     /** @cfg {Number} growMax @hide */
10482
10483     /**
10484      * @hide 
10485      * @method
10486      */
10487     autoSize: Roo.emptyFn,
10488     // private
10489     monitorTab : true,
10490     // private
10491     deferHeight : true,
10492
10493     
10494     actionMode : 'wrap',
10495     
10496     caret : false,
10497     
10498     
10499     getAutoCreate : function(){
10500        
10501         var align = this.labelAlign || this.parentLabelAlign();
10502         
10503         var id = Roo.id();
10504         
10505         var cfg = {
10506             cls: 'form-group' //input-group
10507         };
10508         
10509         
10510         var input =  {
10511             tag: 'input',
10512             id : id,
10513             type : this.inputType,
10514             cls : 'form-control',
10515             autocomplete: 'new-password',
10516             placeholder : this.placeholder || '' 
10517             
10518         };
10519         if (this.name) {
10520             input.name = this.name;
10521         }
10522         if (this.size) {
10523             input.cls += ' input-' + this.size;
10524         }
10525         
10526         if (this.disabled) {
10527             input.disabled=true;
10528         }
10529         
10530         var inputblock = input;
10531         
10532         if(this.hasFeedback && !this.allowBlank){
10533             
10534             var feedback = {
10535                 tag: 'span',
10536                 cls: 'glyphicon form-control-feedback'
10537             };
10538             
10539             if(this.removable && !this.editable && !this.tickable){
10540                 inputblock = {
10541                     cls : 'has-feedback',
10542                     cn :  [
10543                         inputblock,
10544                         {
10545                             tag: 'button',
10546                             html : 'x',
10547                             cls : 'roo-combo-removable-btn close'
10548                         },
10549                         feedback
10550                     ] 
10551                 };
10552             } else {
10553                 inputblock = {
10554                     cls : 'has-feedback',
10555                     cn :  [
10556                         inputblock,
10557                         feedback
10558                     ] 
10559                 };
10560             }
10561
10562         } else {
10563             if(this.removable && !this.editable && !this.tickable){
10564                 inputblock = {
10565                     cls : 'roo-removable',
10566                     cn :  [
10567                         inputblock,
10568                         {
10569                             tag: 'button',
10570                             html : 'x',
10571                             cls : 'roo-combo-removable-btn close'
10572                         }
10573                     ] 
10574                 };
10575             }
10576         }
10577         
10578         if (this.before || this.after) {
10579             
10580             inputblock = {
10581                 cls : 'input-group',
10582                 cn :  [] 
10583             };
10584             if (this.before) {
10585                 inputblock.cn.push({
10586                     tag :'span',
10587                     cls : 'input-group-addon input-group-prepend input-group-text',
10588                     html : this.before
10589                 });
10590             }
10591             
10592             inputblock.cn.push(input);
10593             
10594             if(this.hasFeedback && !this.allowBlank){
10595                 inputblock.cls += ' has-feedback';
10596                 inputblock.cn.push(feedback);
10597             }
10598             
10599             if (this.after) {
10600                 inputblock.cn.push({
10601                     tag :'span',
10602                     cls : 'input-group-addon input-group-append input-group-text',
10603                     html : this.after
10604                 });
10605             }
10606             
10607         };
10608         
10609       
10610         
10611         var ibwrap = inputblock;
10612         
10613         if(this.multiple){
10614             ibwrap = {
10615                 tag: 'ul',
10616                 cls: 'roo-select2-choices',
10617                 cn:[
10618                     {
10619                         tag: 'li',
10620                         cls: 'roo-select2-search-field',
10621                         cn: [
10622
10623                             inputblock
10624                         ]
10625                     }
10626                 ]
10627             };
10628                 
10629         }
10630         
10631         var combobox = {
10632             cls: 'roo-select2-container input-group',
10633             cn: [
10634                  {
10635                     tag: 'input',
10636                     type : 'hidden',
10637                     cls: 'form-hidden-field'
10638                 },
10639                 ibwrap
10640             ]
10641         };
10642         
10643         if(!this.multiple && this.showToggleBtn){
10644             
10645             var caret = {
10646                         tag: 'span',
10647                         cls: 'caret'
10648              };
10649             if (this.caret != false) {
10650                 caret = {
10651                      tag: 'i',
10652                      cls: 'fa fa-' + this.caret
10653                 };
10654                 
10655             }
10656             
10657             combobox.cn.push({
10658                 tag :'span',
10659                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10660                 cn : [
10661                     Roo.bootstrap.version == 3 ? caret : '',
10662                     {
10663                         tag: 'span',
10664                         cls: 'combobox-clear',
10665                         cn  : [
10666                             {
10667                                 tag : 'i',
10668                                 cls: 'icon-remove'
10669                             }
10670                         ]
10671                     }
10672                 ]
10673
10674             })
10675         }
10676         
10677         if(this.multiple){
10678             combobox.cls += ' roo-select2-container-multi';
10679         }
10680          var indicator = {
10681             tag : 'i',
10682             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10683             tooltip : 'This field is required'
10684         };
10685         if (Roo.bootstrap.version == 4) {
10686             indicator = {
10687                 tag : 'i',
10688                 style : 'display:none'
10689             };
10690         }
10691         
10692         
10693         if (align ==='left' && this.fieldLabel.length) {
10694             
10695             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10696
10697             cfg.cn = [
10698                 indicator,
10699                 {
10700                     tag: 'label',
10701                     'for' :  id,
10702                     cls : 'control-label',
10703                     html : this.fieldLabel
10704
10705                 },
10706                 {
10707                     cls : "", 
10708                     cn: [
10709                         combobox
10710                     ]
10711                 }
10712
10713             ];
10714             
10715             var labelCfg = cfg.cn[1];
10716             var contentCfg = cfg.cn[2];
10717             
10718             if(this.indicatorpos == 'right'){
10719                 cfg.cn = [
10720                     {
10721                         tag: 'label',
10722                         'for' :  id,
10723                         cls : 'control-label',
10724                         cn : [
10725                             {
10726                                 tag : 'span',
10727                                 html : this.fieldLabel
10728                             },
10729                             indicator
10730                         ]
10731                     },
10732                     {
10733                         cls : "", 
10734                         cn: [
10735                             combobox
10736                         ]
10737                     }
10738
10739                 ];
10740                 
10741                 labelCfg = cfg.cn[0];
10742                 contentCfg = cfg.cn[1];
10743             }
10744             
10745             if(this.labelWidth > 12){
10746                 labelCfg.style = "width: " + this.labelWidth + 'px';
10747             }
10748             
10749             if(this.labelWidth < 13 && this.labelmd == 0){
10750                 this.labelmd = this.labelWidth;
10751             }
10752             
10753             if(this.labellg > 0){
10754                 labelCfg.cls += ' col-lg-' + this.labellg;
10755                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10756             }
10757             
10758             if(this.labelmd > 0){
10759                 labelCfg.cls += ' col-md-' + this.labelmd;
10760                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10761             }
10762             
10763             if(this.labelsm > 0){
10764                 labelCfg.cls += ' col-sm-' + this.labelsm;
10765                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10766             }
10767             
10768             if(this.labelxs > 0){
10769                 labelCfg.cls += ' col-xs-' + this.labelxs;
10770                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10771             }
10772             
10773         } else if ( this.fieldLabel.length) {
10774 //                Roo.log(" label");
10775             cfg.cn = [
10776                 indicator,
10777                {
10778                    tag: 'label',
10779                    //cls : 'input-group-addon',
10780                    html : this.fieldLabel
10781
10782                },
10783
10784                combobox
10785
10786             ];
10787             
10788             if(this.indicatorpos == 'right'){
10789                 
10790                 cfg.cn = [
10791                     {
10792                        tag: 'label',
10793                        cn : [
10794                            {
10795                                tag : 'span',
10796                                html : this.fieldLabel
10797                            },
10798                            indicator
10799                        ]
10800
10801                     },
10802                     combobox
10803
10804                 ];
10805
10806             }
10807
10808         } else {
10809             
10810 //                Roo.log(" no label && no align");
10811                 cfg = combobox
10812                      
10813                 
10814         }
10815         
10816         var settings=this;
10817         ['xs','sm','md','lg'].map(function(size){
10818             if (settings[size]) {
10819                 cfg.cls += ' col-' + size + '-' + settings[size];
10820             }
10821         });
10822         
10823         return cfg;
10824         
10825     },
10826     
10827     
10828     
10829     // private
10830     onResize : function(w, h){
10831 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10832 //        if(typeof w == 'number'){
10833 //            var x = w - this.trigger.getWidth();
10834 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10835 //            this.trigger.setStyle('left', x+'px');
10836 //        }
10837     },
10838
10839     // private
10840     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10841
10842     // private
10843     getResizeEl : function(){
10844         return this.inputEl();
10845     },
10846
10847     // private
10848     getPositionEl : function(){
10849         return this.inputEl();
10850     },
10851
10852     // private
10853     alignErrorIcon : function(){
10854         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10855     },
10856
10857     // private
10858     initEvents : function(){
10859         
10860         this.createList();
10861         
10862         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10863         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10864         if(!this.multiple && this.showToggleBtn){
10865             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10866             if(this.hideTrigger){
10867                 this.trigger.setDisplayed(false);
10868             }
10869             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10870         }
10871         
10872         if(this.multiple){
10873             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10874         }
10875         
10876         if(this.removable && !this.editable && !this.tickable){
10877             var close = this.closeTriggerEl();
10878             
10879             if(close){
10880                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10881                 close.on('click', this.removeBtnClick, this, close);
10882             }
10883         }
10884         
10885         //this.trigger.addClassOnOver('x-form-trigger-over');
10886         //this.trigger.addClassOnClick('x-form-trigger-click');
10887         
10888         //if(!this.width){
10889         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10890         //}
10891     },
10892     
10893     closeTriggerEl : function()
10894     {
10895         var close = this.el.select('.roo-combo-removable-btn', true).first();
10896         return close ? close : false;
10897     },
10898     
10899     removeBtnClick : function(e, h, el)
10900     {
10901         e.preventDefault();
10902         
10903         if(this.fireEvent("remove", this) !== false){
10904             this.reset();
10905             this.fireEvent("afterremove", this)
10906         }
10907     },
10908     
10909     createList : function()
10910     {
10911         this.list = Roo.get(document.body).createChild({
10912             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10913             cls: 'typeahead typeahead-long dropdown-menu',
10914             style: 'display:none'
10915         });
10916         
10917         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10918         
10919     },
10920
10921     // private
10922     initTrigger : function(){
10923        
10924     },
10925
10926     // private
10927     onDestroy : function(){
10928         if(this.trigger){
10929             this.trigger.removeAllListeners();
10930           //  this.trigger.remove();
10931         }
10932         //if(this.wrap){
10933         //    this.wrap.remove();
10934         //}
10935         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10936     },
10937
10938     // private
10939     onFocus : function(){
10940         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10941         /*
10942         if(!this.mimicing){
10943             this.wrap.addClass('x-trigger-wrap-focus');
10944             this.mimicing = true;
10945             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10946             if(this.monitorTab){
10947                 this.el.on("keydown", this.checkTab, this);
10948             }
10949         }
10950         */
10951     },
10952
10953     // private
10954     checkTab : function(e){
10955         if(e.getKey() == e.TAB){
10956             this.triggerBlur();
10957         }
10958     },
10959
10960     // private
10961     onBlur : function(){
10962         // do nothing
10963     },
10964
10965     // private
10966     mimicBlur : function(e, t){
10967         /*
10968         if(!this.wrap.contains(t) && this.validateBlur()){
10969             this.triggerBlur();
10970         }
10971         */
10972     },
10973
10974     // private
10975     triggerBlur : function(){
10976         this.mimicing = false;
10977         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10978         if(this.monitorTab){
10979             this.el.un("keydown", this.checkTab, this);
10980         }
10981         //this.wrap.removeClass('x-trigger-wrap-focus');
10982         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10983     },
10984
10985     // private
10986     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10987     validateBlur : function(e, t){
10988         return true;
10989     },
10990
10991     // private
10992     onDisable : function(){
10993         this.inputEl().dom.disabled = true;
10994         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10995         //if(this.wrap){
10996         //    this.wrap.addClass('x-item-disabled');
10997         //}
10998     },
10999
11000     // private
11001     onEnable : function(){
11002         this.inputEl().dom.disabled = false;
11003         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11004         //if(this.wrap){
11005         //    this.el.removeClass('x-item-disabled');
11006         //}
11007     },
11008
11009     // private
11010     onShow : function(){
11011         var ae = this.getActionEl();
11012         
11013         if(ae){
11014             ae.dom.style.display = '';
11015             ae.dom.style.visibility = 'visible';
11016         }
11017     },
11018
11019     // private
11020     
11021     onHide : function(){
11022         var ae = this.getActionEl();
11023         ae.dom.style.display = 'none';
11024     },
11025
11026     /**
11027      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11028      * by an implementing function.
11029      * @method
11030      * @param {EventObject} e
11031      */
11032     onTriggerClick : Roo.emptyFn
11033 });
11034  /*
11035  * Based on:
11036  * Ext JS Library 1.1.1
11037  * Copyright(c) 2006-2007, Ext JS, LLC.
11038  *
11039  * Originally Released Under LGPL - original licence link has changed is not relivant.
11040  *
11041  * Fork - LGPL
11042  * <script type="text/javascript">
11043  */
11044
11045
11046 /**
11047  * @class Roo.data.SortTypes
11048  * @singleton
11049  * Defines the default sorting (casting?) comparison functions used when sorting data.
11050  */
11051 Roo.data.SortTypes = {
11052     /**
11053      * Default sort that does nothing
11054      * @param {Mixed} s The value being converted
11055      * @return {Mixed} The comparison value
11056      */
11057     none : function(s){
11058         return s;
11059     },
11060     
11061     /**
11062      * The regular expression used to strip tags
11063      * @type {RegExp}
11064      * @property
11065      */
11066     stripTagsRE : /<\/?[^>]+>/gi,
11067     
11068     /**
11069      * Strips all HTML tags to sort on text only
11070      * @param {Mixed} s The value being converted
11071      * @return {String} The comparison value
11072      */
11073     asText : function(s){
11074         return String(s).replace(this.stripTagsRE, "");
11075     },
11076     
11077     /**
11078      * Strips all HTML tags to sort on text only - Case insensitive
11079      * @param {Mixed} s The value being converted
11080      * @return {String} The comparison value
11081      */
11082     asUCText : function(s){
11083         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11084     },
11085     
11086     /**
11087      * Case insensitive string
11088      * @param {Mixed} s The value being converted
11089      * @return {String} The comparison value
11090      */
11091     asUCString : function(s) {
11092         return String(s).toUpperCase();
11093     },
11094     
11095     /**
11096      * Date sorting
11097      * @param {Mixed} s The value being converted
11098      * @return {Number} The comparison value
11099      */
11100     asDate : function(s) {
11101         if(!s){
11102             return 0;
11103         }
11104         if(s instanceof Date){
11105             return s.getTime();
11106         }
11107         return Date.parse(String(s));
11108     },
11109     
11110     /**
11111      * Float sorting
11112      * @param {Mixed} s The value being converted
11113      * @return {Float} The comparison value
11114      */
11115     asFloat : function(s) {
11116         var val = parseFloat(String(s).replace(/,/g, ""));
11117         if(isNaN(val)) {
11118             val = 0;
11119         }
11120         return val;
11121     },
11122     
11123     /**
11124      * Integer sorting
11125      * @param {Mixed} s The value being converted
11126      * @return {Number} The comparison value
11127      */
11128     asInt : function(s) {
11129         var val = parseInt(String(s).replace(/,/g, ""));
11130         if(isNaN(val)) {
11131             val = 0;
11132         }
11133         return val;
11134     }
11135 };/*
11136  * Based on:
11137  * Ext JS Library 1.1.1
11138  * Copyright(c) 2006-2007, Ext JS, LLC.
11139  *
11140  * Originally Released Under LGPL - original licence link has changed is not relivant.
11141  *
11142  * Fork - LGPL
11143  * <script type="text/javascript">
11144  */
11145
11146 /**
11147 * @class Roo.data.Record
11148  * Instances of this class encapsulate both record <em>definition</em> information, and record
11149  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11150  * to access Records cached in an {@link Roo.data.Store} object.<br>
11151  * <p>
11152  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11153  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11154  * objects.<br>
11155  * <p>
11156  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11157  * @constructor
11158  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11159  * {@link #create}. The parameters are the same.
11160  * @param {Array} data An associative Array of data values keyed by the field name.
11161  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11162  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11163  * not specified an integer id is generated.
11164  */
11165 Roo.data.Record = function(data, id){
11166     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11167     this.data = data;
11168 };
11169
11170 /**
11171  * Generate a constructor for a specific record layout.
11172  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11173  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11174  * Each field definition object may contain the following properties: <ul>
11175  * <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,
11176  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11177  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11178  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11179  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11180  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11181  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11182  * this may be omitted.</p></li>
11183  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11184  * <ul><li>auto (Default, implies no conversion)</li>
11185  * <li>string</li>
11186  * <li>int</li>
11187  * <li>float</li>
11188  * <li>boolean</li>
11189  * <li>date</li></ul></p></li>
11190  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11191  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11192  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11193  * by the Reader into an object that will be stored in the Record. It is passed the
11194  * following parameters:<ul>
11195  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11196  * </ul></p></li>
11197  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11198  * </ul>
11199  * <br>usage:<br><pre><code>
11200 var TopicRecord = Roo.data.Record.create(
11201     {name: 'title', mapping: 'topic_title'},
11202     {name: 'author', mapping: 'username'},
11203     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11204     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11205     {name: 'lastPoster', mapping: 'user2'},
11206     {name: 'excerpt', mapping: 'post_text'}
11207 );
11208
11209 var myNewRecord = new TopicRecord({
11210     title: 'Do my job please',
11211     author: 'noobie',
11212     totalPosts: 1,
11213     lastPost: new Date(),
11214     lastPoster: 'Animal',
11215     excerpt: 'No way dude!'
11216 });
11217 myStore.add(myNewRecord);
11218 </code></pre>
11219  * @method create
11220  * @static
11221  */
11222 Roo.data.Record.create = function(o){
11223     var f = function(){
11224         f.superclass.constructor.apply(this, arguments);
11225     };
11226     Roo.extend(f, Roo.data.Record);
11227     var p = f.prototype;
11228     p.fields = new Roo.util.MixedCollection(false, function(field){
11229         return field.name;
11230     });
11231     for(var i = 0, len = o.length; i < len; i++){
11232         p.fields.add(new Roo.data.Field(o[i]));
11233     }
11234     f.getField = function(name){
11235         return p.fields.get(name);  
11236     };
11237     return f;
11238 };
11239
11240 Roo.data.Record.AUTO_ID = 1000;
11241 Roo.data.Record.EDIT = 'edit';
11242 Roo.data.Record.REJECT = 'reject';
11243 Roo.data.Record.COMMIT = 'commit';
11244
11245 Roo.data.Record.prototype = {
11246     /**
11247      * Readonly flag - true if this record has been modified.
11248      * @type Boolean
11249      */
11250     dirty : false,
11251     editing : false,
11252     error: null,
11253     modified: null,
11254
11255     // private
11256     join : function(store){
11257         this.store = store;
11258     },
11259
11260     /**
11261      * Set the named field to the specified value.
11262      * @param {String} name The name of the field to set.
11263      * @param {Object} value The value to set the field to.
11264      */
11265     set : function(name, value){
11266         if(this.data[name] == value){
11267             return;
11268         }
11269         this.dirty = true;
11270         if(!this.modified){
11271             this.modified = {};
11272         }
11273         if(typeof this.modified[name] == 'undefined'){
11274             this.modified[name] = this.data[name];
11275         }
11276         this.data[name] = value;
11277         if(!this.editing && this.store){
11278             this.store.afterEdit(this);
11279         }       
11280     },
11281
11282     /**
11283      * Get the value of the named field.
11284      * @param {String} name The name of the field to get the value of.
11285      * @return {Object} The value of the field.
11286      */
11287     get : function(name){
11288         return this.data[name]; 
11289     },
11290
11291     // private
11292     beginEdit : function(){
11293         this.editing = true;
11294         this.modified = {}; 
11295     },
11296
11297     // private
11298     cancelEdit : function(){
11299         this.editing = false;
11300         delete this.modified;
11301     },
11302
11303     // private
11304     endEdit : function(){
11305         this.editing = false;
11306         if(this.dirty && this.store){
11307             this.store.afterEdit(this);
11308         }
11309     },
11310
11311     /**
11312      * Usually called by the {@link Roo.data.Store} which owns the Record.
11313      * Rejects all changes made to the Record since either creation, or the last commit operation.
11314      * Modified fields are reverted to their original values.
11315      * <p>
11316      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11317      * of reject operations.
11318      */
11319     reject : function(){
11320         var m = this.modified;
11321         for(var n in m){
11322             if(typeof m[n] != "function"){
11323                 this.data[n] = m[n];
11324             }
11325         }
11326         this.dirty = false;
11327         delete this.modified;
11328         this.editing = false;
11329         if(this.store){
11330             this.store.afterReject(this);
11331         }
11332     },
11333
11334     /**
11335      * Usually called by the {@link Roo.data.Store} which owns the Record.
11336      * Commits all changes made to the Record since either creation, or the last commit operation.
11337      * <p>
11338      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11339      * of commit operations.
11340      */
11341     commit : function(){
11342         this.dirty = false;
11343         delete this.modified;
11344         this.editing = false;
11345         if(this.store){
11346             this.store.afterCommit(this);
11347         }
11348     },
11349
11350     // private
11351     hasError : function(){
11352         return this.error != null;
11353     },
11354
11355     // private
11356     clearError : function(){
11357         this.error = null;
11358     },
11359
11360     /**
11361      * Creates a copy of this record.
11362      * @param {String} id (optional) A new record id if you don't want to use this record's id
11363      * @return {Record}
11364      */
11365     copy : function(newId) {
11366         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11367     }
11368 };/*
11369  * Based on:
11370  * Ext JS Library 1.1.1
11371  * Copyright(c) 2006-2007, Ext JS, LLC.
11372  *
11373  * Originally Released Under LGPL - original licence link has changed is not relivant.
11374  *
11375  * Fork - LGPL
11376  * <script type="text/javascript">
11377  */
11378
11379
11380
11381 /**
11382  * @class Roo.data.Store
11383  * @extends Roo.util.Observable
11384  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11385  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11386  * <p>
11387  * 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
11388  * has no knowledge of the format of the data returned by the Proxy.<br>
11389  * <p>
11390  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11391  * instances from the data object. These records are cached and made available through accessor functions.
11392  * @constructor
11393  * Creates a new Store.
11394  * @param {Object} config A config object containing the objects needed for the Store to access data,
11395  * and read the data into Records.
11396  */
11397 Roo.data.Store = function(config){
11398     this.data = new Roo.util.MixedCollection(false);
11399     this.data.getKey = function(o){
11400         return o.id;
11401     };
11402     this.baseParams = {};
11403     // private
11404     this.paramNames = {
11405         "start" : "start",
11406         "limit" : "limit",
11407         "sort" : "sort",
11408         "dir" : "dir",
11409         "multisort" : "_multisort"
11410     };
11411
11412     if(config && config.data){
11413         this.inlineData = config.data;
11414         delete config.data;
11415     }
11416
11417     Roo.apply(this, config);
11418     
11419     if(this.reader){ // reader passed
11420         this.reader = Roo.factory(this.reader, Roo.data);
11421         this.reader.xmodule = this.xmodule || false;
11422         if(!this.recordType){
11423             this.recordType = this.reader.recordType;
11424         }
11425         if(this.reader.onMetaChange){
11426             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11427         }
11428     }
11429
11430     if(this.recordType){
11431         this.fields = this.recordType.prototype.fields;
11432     }
11433     this.modified = [];
11434
11435     this.addEvents({
11436         /**
11437          * @event datachanged
11438          * Fires when the data cache has changed, and a widget which is using this Store
11439          * as a Record cache should refresh its view.
11440          * @param {Store} this
11441          */
11442         datachanged : true,
11443         /**
11444          * @event metachange
11445          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11446          * @param {Store} this
11447          * @param {Object} meta The JSON metadata
11448          */
11449         metachange : true,
11450         /**
11451          * @event add
11452          * Fires when Records have been added to the Store
11453          * @param {Store} this
11454          * @param {Roo.data.Record[]} records The array of Records added
11455          * @param {Number} index The index at which the record(s) were added
11456          */
11457         add : true,
11458         /**
11459          * @event remove
11460          * Fires when a Record has been removed from the Store
11461          * @param {Store} this
11462          * @param {Roo.data.Record} record The Record that was removed
11463          * @param {Number} index The index at which the record was removed
11464          */
11465         remove : true,
11466         /**
11467          * @event update
11468          * Fires when a Record has been updated
11469          * @param {Store} this
11470          * @param {Roo.data.Record} record The Record that was updated
11471          * @param {String} operation The update operation being performed.  Value may be one of:
11472          * <pre><code>
11473  Roo.data.Record.EDIT
11474  Roo.data.Record.REJECT
11475  Roo.data.Record.COMMIT
11476          * </code></pre>
11477          */
11478         update : true,
11479         /**
11480          * @event clear
11481          * Fires when the data cache has been cleared.
11482          * @param {Store} this
11483          */
11484         clear : true,
11485         /**
11486          * @event beforeload
11487          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11488          * the load action will be canceled.
11489          * @param {Store} this
11490          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11491          */
11492         beforeload : true,
11493         /**
11494          * @event beforeloadadd
11495          * Fires after a new set of Records has been loaded.
11496          * @param {Store} this
11497          * @param {Roo.data.Record[]} records The Records that were loaded
11498          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11499          */
11500         beforeloadadd : true,
11501         /**
11502          * @event load
11503          * Fires after a new set of Records has been loaded, before they are added to the store.
11504          * @param {Store} this
11505          * @param {Roo.data.Record[]} records The Records that were loaded
11506          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11507          * @params {Object} return from reader
11508          */
11509         load : true,
11510         /**
11511          * @event loadexception
11512          * Fires if an exception occurs in the Proxy during loading.
11513          * Called with the signature of the Proxy's "loadexception" event.
11514          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11515          * 
11516          * @param {Proxy} 
11517          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11518          * @param {Object} load options 
11519          * @param {Object} jsonData from your request (normally this contains the Exception)
11520          */
11521         loadexception : true
11522     });
11523     
11524     if(this.proxy){
11525         this.proxy = Roo.factory(this.proxy, Roo.data);
11526         this.proxy.xmodule = this.xmodule || false;
11527         this.relayEvents(this.proxy,  ["loadexception"]);
11528     }
11529     this.sortToggle = {};
11530     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11531
11532     Roo.data.Store.superclass.constructor.call(this);
11533
11534     if(this.inlineData){
11535         this.loadData(this.inlineData);
11536         delete this.inlineData;
11537     }
11538 };
11539
11540 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11541      /**
11542     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11543     * without a remote query - used by combo/forms at present.
11544     */
11545     
11546     /**
11547     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11548     */
11549     /**
11550     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11551     */
11552     /**
11553     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11554     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11555     */
11556     /**
11557     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11558     * on any HTTP request
11559     */
11560     /**
11561     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11562     */
11563     /**
11564     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11565     */
11566     multiSort: false,
11567     /**
11568     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11569     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11570     */
11571     remoteSort : false,
11572
11573     /**
11574     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11575      * loaded or when a record is removed. (defaults to false).
11576     */
11577     pruneModifiedRecords : false,
11578
11579     // private
11580     lastOptions : null,
11581
11582     /**
11583      * Add Records to the Store and fires the add event.
11584      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11585      */
11586     add : function(records){
11587         records = [].concat(records);
11588         for(var i = 0, len = records.length; i < len; i++){
11589             records[i].join(this);
11590         }
11591         var index = this.data.length;
11592         this.data.addAll(records);
11593         this.fireEvent("add", this, records, index);
11594     },
11595
11596     /**
11597      * Remove a Record from the Store and fires the remove event.
11598      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11599      */
11600     remove : function(record){
11601         var index = this.data.indexOf(record);
11602         this.data.removeAt(index);
11603  
11604         if(this.pruneModifiedRecords){
11605             this.modified.remove(record);
11606         }
11607         this.fireEvent("remove", this, record, index);
11608     },
11609
11610     /**
11611      * Remove all Records from the Store and fires the clear event.
11612      */
11613     removeAll : function(){
11614         this.data.clear();
11615         if(this.pruneModifiedRecords){
11616             this.modified = [];
11617         }
11618         this.fireEvent("clear", this);
11619     },
11620
11621     /**
11622      * Inserts Records to the Store at the given index and fires the add event.
11623      * @param {Number} index The start index at which to insert the passed Records.
11624      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11625      */
11626     insert : function(index, records){
11627         records = [].concat(records);
11628         for(var i = 0, len = records.length; i < len; i++){
11629             this.data.insert(index, records[i]);
11630             records[i].join(this);
11631         }
11632         this.fireEvent("add", this, records, index);
11633     },
11634
11635     /**
11636      * Get the index within the cache of the passed Record.
11637      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11638      * @return {Number} The index of the passed Record. Returns -1 if not found.
11639      */
11640     indexOf : function(record){
11641         return this.data.indexOf(record);
11642     },
11643
11644     /**
11645      * Get the index within the cache of the Record with the passed id.
11646      * @param {String} id The id of the Record to find.
11647      * @return {Number} The index of the Record. Returns -1 if not found.
11648      */
11649     indexOfId : function(id){
11650         return this.data.indexOfKey(id);
11651     },
11652
11653     /**
11654      * Get the Record with the specified id.
11655      * @param {String} id The id of the Record to find.
11656      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11657      */
11658     getById : function(id){
11659         return this.data.key(id);
11660     },
11661
11662     /**
11663      * Get the Record at the specified index.
11664      * @param {Number} index The index of the Record to find.
11665      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11666      */
11667     getAt : function(index){
11668         return this.data.itemAt(index);
11669     },
11670
11671     /**
11672      * Returns a range of Records between specified indices.
11673      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11674      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11675      * @return {Roo.data.Record[]} An array of Records
11676      */
11677     getRange : function(start, end){
11678         return this.data.getRange(start, end);
11679     },
11680
11681     // private
11682     storeOptions : function(o){
11683         o = Roo.apply({}, o);
11684         delete o.callback;
11685         delete o.scope;
11686         this.lastOptions = o;
11687     },
11688
11689     /**
11690      * Loads the Record cache from the configured Proxy using the configured Reader.
11691      * <p>
11692      * If using remote paging, then the first load call must specify the <em>start</em>
11693      * and <em>limit</em> properties in the options.params property to establish the initial
11694      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11695      * <p>
11696      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11697      * and this call will return before the new data has been loaded. Perform any post-processing
11698      * in a callback function, or in a "load" event handler.</strong>
11699      * <p>
11700      * @param {Object} options An object containing properties which control loading options:<ul>
11701      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11702      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11703      * passed the following arguments:<ul>
11704      * <li>r : Roo.data.Record[]</li>
11705      * <li>options: Options object from the load call</li>
11706      * <li>success: Boolean success indicator</li></ul></li>
11707      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11708      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11709      * </ul>
11710      */
11711     load : function(options){
11712         options = options || {};
11713         if(this.fireEvent("beforeload", this, options) !== false){
11714             this.storeOptions(options);
11715             var p = Roo.apply(options.params || {}, this.baseParams);
11716             // if meta was not loaded from remote source.. try requesting it.
11717             if (!this.reader.metaFromRemote) {
11718                 p._requestMeta = 1;
11719             }
11720             if(this.sortInfo && this.remoteSort){
11721                 var pn = this.paramNames;
11722                 p[pn["sort"]] = this.sortInfo.field;
11723                 p[pn["dir"]] = this.sortInfo.direction;
11724             }
11725             if (this.multiSort) {
11726                 var pn = this.paramNames;
11727                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11728             }
11729             
11730             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11731         }
11732     },
11733
11734     /**
11735      * Reloads the Record cache from the configured Proxy using the configured Reader and
11736      * the options from the last load operation performed.
11737      * @param {Object} options (optional) An object containing properties which may override the options
11738      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11739      * the most recently used options are reused).
11740      */
11741     reload : function(options){
11742         this.load(Roo.applyIf(options||{}, this.lastOptions));
11743     },
11744
11745     // private
11746     // Called as a callback by the Reader during a load operation.
11747     loadRecords : function(o, options, success){
11748         if(!o || success === false){
11749             if(success !== false){
11750                 this.fireEvent("load", this, [], options, o);
11751             }
11752             if(options.callback){
11753                 options.callback.call(options.scope || this, [], options, false);
11754             }
11755             return;
11756         }
11757         // if data returned failure - throw an exception.
11758         if (o.success === false) {
11759             // show a message if no listener is registered.
11760             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11761                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11762             }
11763             // loadmask wil be hooked into this..
11764             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11765             return;
11766         }
11767         var r = o.records, t = o.totalRecords || r.length;
11768         
11769         this.fireEvent("beforeloadadd", this, r, options, o);
11770         
11771         if(!options || options.add !== true){
11772             if(this.pruneModifiedRecords){
11773                 this.modified = [];
11774             }
11775             for(var i = 0, len = r.length; i < len; i++){
11776                 r[i].join(this);
11777             }
11778             if(this.snapshot){
11779                 this.data = this.snapshot;
11780                 delete this.snapshot;
11781             }
11782             this.data.clear();
11783             this.data.addAll(r);
11784             this.totalLength = t;
11785             this.applySort();
11786             this.fireEvent("datachanged", this);
11787         }else{
11788             this.totalLength = Math.max(t, this.data.length+r.length);
11789             this.add(r);
11790         }
11791         
11792         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11793                 
11794             var e = new Roo.data.Record({});
11795
11796             e.set(this.parent.displayField, this.parent.emptyTitle);
11797             e.set(this.parent.valueField, '');
11798
11799             this.insert(0, e);
11800         }
11801             
11802         this.fireEvent("load", this, r, options, o);
11803         if(options.callback){
11804             options.callback.call(options.scope || this, r, options, true);
11805         }
11806     },
11807
11808
11809     /**
11810      * Loads data from a passed data block. A Reader which understands the format of the data
11811      * must have been configured in the constructor.
11812      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11813      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11814      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11815      */
11816     loadData : function(o, append){
11817         var r = this.reader.readRecords(o);
11818         this.loadRecords(r, {add: append}, true);
11819     },
11820
11821     /**
11822      * Gets the number of cached records.
11823      * <p>
11824      * <em>If using paging, this may not be the total size of the dataset. If the data object
11825      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11826      * the data set size</em>
11827      */
11828     getCount : function(){
11829         return this.data.length || 0;
11830     },
11831
11832     /**
11833      * Gets the total number of records in the dataset as returned by the server.
11834      * <p>
11835      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11836      * the dataset size</em>
11837      */
11838     getTotalCount : function(){
11839         return this.totalLength || 0;
11840     },
11841
11842     /**
11843      * Returns the sort state of the Store as an object with two properties:
11844      * <pre><code>
11845  field {String} The name of the field by which the Records are sorted
11846  direction {String} The sort order, "ASC" or "DESC"
11847      * </code></pre>
11848      */
11849     getSortState : function(){
11850         return this.sortInfo;
11851     },
11852
11853     // private
11854     applySort : function(){
11855         if(this.sortInfo && !this.remoteSort){
11856             var s = this.sortInfo, f = s.field;
11857             var st = this.fields.get(f).sortType;
11858             var fn = function(r1, r2){
11859                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11860                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11861             };
11862             this.data.sort(s.direction, fn);
11863             if(this.snapshot && this.snapshot != this.data){
11864                 this.snapshot.sort(s.direction, fn);
11865             }
11866         }
11867     },
11868
11869     /**
11870      * Sets the default sort column and order to be used by the next load operation.
11871      * @param {String} fieldName The name of the field to sort by.
11872      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11873      */
11874     setDefaultSort : function(field, dir){
11875         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11876     },
11877
11878     /**
11879      * Sort the Records.
11880      * If remote sorting is used, the sort is performed on the server, and the cache is
11881      * reloaded. If local sorting is used, the cache is sorted internally.
11882      * @param {String} fieldName The name of the field to sort by.
11883      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11884      */
11885     sort : function(fieldName, dir){
11886         var f = this.fields.get(fieldName);
11887         if(!dir){
11888             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11889             
11890             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11891                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11892             }else{
11893                 dir = f.sortDir;
11894             }
11895         }
11896         this.sortToggle[f.name] = dir;
11897         this.sortInfo = {field: f.name, direction: dir};
11898         if(!this.remoteSort){
11899             this.applySort();
11900             this.fireEvent("datachanged", this);
11901         }else{
11902             this.load(this.lastOptions);
11903         }
11904     },
11905
11906     /**
11907      * Calls the specified function for each of the Records in the cache.
11908      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11909      * Returning <em>false</em> aborts and exits the iteration.
11910      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11911      */
11912     each : function(fn, scope){
11913         this.data.each(fn, scope);
11914     },
11915
11916     /**
11917      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11918      * (e.g., during paging).
11919      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11920      */
11921     getModifiedRecords : function(){
11922         return this.modified;
11923     },
11924
11925     // private
11926     createFilterFn : function(property, value, anyMatch){
11927         if(!value.exec){ // not a regex
11928             value = String(value);
11929             if(value.length == 0){
11930                 return false;
11931             }
11932             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11933         }
11934         return function(r){
11935             return value.test(r.data[property]);
11936         };
11937     },
11938
11939     /**
11940      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11941      * @param {String} property A field on your records
11942      * @param {Number} start The record index to start at (defaults to 0)
11943      * @param {Number} end The last record index to include (defaults to length - 1)
11944      * @return {Number} The sum
11945      */
11946     sum : function(property, start, end){
11947         var rs = this.data.items, v = 0;
11948         start = start || 0;
11949         end = (end || end === 0) ? end : rs.length-1;
11950
11951         for(var i = start; i <= end; i++){
11952             v += (rs[i].data[property] || 0);
11953         }
11954         return v;
11955     },
11956
11957     /**
11958      * Filter the records by a specified property.
11959      * @param {String} field A field on your records
11960      * @param {String/RegExp} value Either a string that the field
11961      * should start with or a RegExp to test against the field
11962      * @param {Boolean} anyMatch True to match any part not just the beginning
11963      */
11964     filter : function(property, value, anyMatch){
11965         var fn = this.createFilterFn(property, value, anyMatch);
11966         return fn ? this.filterBy(fn) : this.clearFilter();
11967     },
11968
11969     /**
11970      * Filter by a function. The specified function will be called with each
11971      * record in this data source. If the function returns true the record is included,
11972      * otherwise it is filtered.
11973      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11974      * @param {Object} scope (optional) The scope of the function (defaults to this)
11975      */
11976     filterBy : function(fn, scope){
11977         this.snapshot = this.snapshot || this.data;
11978         this.data = this.queryBy(fn, scope||this);
11979         this.fireEvent("datachanged", this);
11980     },
11981
11982     /**
11983      * Query the records by a specified property.
11984      * @param {String} field A field on your records
11985      * @param {String/RegExp} value Either a string that the field
11986      * should start with or a RegExp to test against the field
11987      * @param {Boolean} anyMatch True to match any part not just the beginning
11988      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11989      */
11990     query : function(property, value, anyMatch){
11991         var fn = this.createFilterFn(property, value, anyMatch);
11992         return fn ? this.queryBy(fn) : this.data.clone();
11993     },
11994
11995     /**
11996      * Query by a function. The specified function will be called with each
11997      * record in this data source. If the function returns true the record is included
11998      * in the results.
11999      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12000      * @param {Object} scope (optional) The scope of the function (defaults to this)
12001       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12002      **/
12003     queryBy : function(fn, scope){
12004         var data = this.snapshot || this.data;
12005         return data.filterBy(fn, scope||this);
12006     },
12007
12008     /**
12009      * Collects unique values for a particular dataIndex from this store.
12010      * @param {String} dataIndex The property to collect
12011      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12012      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12013      * @return {Array} An array of the unique values
12014      **/
12015     collect : function(dataIndex, allowNull, bypassFilter){
12016         var d = (bypassFilter === true && this.snapshot) ?
12017                 this.snapshot.items : this.data.items;
12018         var v, sv, r = [], l = {};
12019         for(var i = 0, len = d.length; i < len; i++){
12020             v = d[i].data[dataIndex];
12021             sv = String(v);
12022             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12023                 l[sv] = true;
12024                 r[r.length] = v;
12025             }
12026         }
12027         return r;
12028     },
12029
12030     /**
12031      * Revert to a view of the Record cache with no filtering applied.
12032      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12033      */
12034     clearFilter : function(suppressEvent){
12035         if(this.snapshot && this.snapshot != this.data){
12036             this.data = this.snapshot;
12037             delete this.snapshot;
12038             if(suppressEvent !== true){
12039                 this.fireEvent("datachanged", this);
12040             }
12041         }
12042     },
12043
12044     // private
12045     afterEdit : function(record){
12046         if(this.modified.indexOf(record) == -1){
12047             this.modified.push(record);
12048         }
12049         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12050     },
12051     
12052     // private
12053     afterReject : function(record){
12054         this.modified.remove(record);
12055         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12056     },
12057
12058     // private
12059     afterCommit : function(record){
12060         this.modified.remove(record);
12061         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12062     },
12063
12064     /**
12065      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12066      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12067      */
12068     commitChanges : function(){
12069         var m = this.modified.slice(0);
12070         this.modified = [];
12071         for(var i = 0, len = m.length; i < len; i++){
12072             m[i].commit();
12073         }
12074     },
12075
12076     /**
12077      * Cancel outstanding changes on all changed records.
12078      */
12079     rejectChanges : function(){
12080         var m = this.modified.slice(0);
12081         this.modified = [];
12082         for(var i = 0, len = m.length; i < len; i++){
12083             m[i].reject();
12084         }
12085     },
12086
12087     onMetaChange : function(meta, rtype, o){
12088         this.recordType = rtype;
12089         this.fields = rtype.prototype.fields;
12090         delete this.snapshot;
12091         this.sortInfo = meta.sortInfo || this.sortInfo;
12092         this.modified = [];
12093         this.fireEvent('metachange', this, this.reader.meta);
12094     },
12095     
12096     moveIndex : function(data, type)
12097     {
12098         var index = this.indexOf(data);
12099         
12100         var newIndex = index + type;
12101         
12102         this.remove(data);
12103         
12104         this.insert(newIndex, data);
12105         
12106     }
12107 });/*
12108  * Based on:
12109  * Ext JS Library 1.1.1
12110  * Copyright(c) 2006-2007, Ext JS, LLC.
12111  *
12112  * Originally Released Under LGPL - original licence link has changed is not relivant.
12113  *
12114  * Fork - LGPL
12115  * <script type="text/javascript">
12116  */
12117
12118 /**
12119  * @class Roo.data.SimpleStore
12120  * @extends Roo.data.Store
12121  * Small helper class to make creating Stores from Array data easier.
12122  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12123  * @cfg {Array} fields An array of field definition objects, or field name strings.
12124  * @cfg {Array} data The multi-dimensional array of data
12125  * @constructor
12126  * @param {Object} config
12127  */
12128 Roo.data.SimpleStore = function(config){
12129     Roo.data.SimpleStore.superclass.constructor.call(this, {
12130         isLocal : true,
12131         reader: new Roo.data.ArrayReader({
12132                 id: config.id
12133             },
12134             Roo.data.Record.create(config.fields)
12135         ),
12136         proxy : new Roo.data.MemoryProxy(config.data)
12137     });
12138     this.load();
12139 };
12140 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12141  * Based on:
12142  * Ext JS Library 1.1.1
12143  * Copyright(c) 2006-2007, Ext JS, LLC.
12144  *
12145  * Originally Released Under LGPL - original licence link has changed is not relivant.
12146  *
12147  * Fork - LGPL
12148  * <script type="text/javascript">
12149  */
12150
12151 /**
12152 /**
12153  * @extends Roo.data.Store
12154  * @class Roo.data.JsonStore
12155  * Small helper class to make creating Stores for JSON data easier. <br/>
12156 <pre><code>
12157 var store = new Roo.data.JsonStore({
12158     url: 'get-images.php',
12159     root: 'images',
12160     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12161 });
12162 </code></pre>
12163  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12164  * JsonReader and HttpProxy (unless inline data is provided).</b>
12165  * @cfg {Array} fields An array of field definition objects, or field name strings.
12166  * @constructor
12167  * @param {Object} config
12168  */
12169 Roo.data.JsonStore = function(c){
12170     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12171         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12172         reader: new Roo.data.JsonReader(c, c.fields)
12173     }));
12174 };
12175 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12176  * Based on:
12177  * Ext JS Library 1.1.1
12178  * Copyright(c) 2006-2007, Ext JS, LLC.
12179  *
12180  * Originally Released Under LGPL - original licence link has changed is not relivant.
12181  *
12182  * Fork - LGPL
12183  * <script type="text/javascript">
12184  */
12185
12186  
12187 Roo.data.Field = function(config){
12188     if(typeof config == "string"){
12189         config = {name: config};
12190     }
12191     Roo.apply(this, config);
12192     
12193     if(!this.type){
12194         this.type = "auto";
12195     }
12196     
12197     var st = Roo.data.SortTypes;
12198     // named sortTypes are supported, here we look them up
12199     if(typeof this.sortType == "string"){
12200         this.sortType = st[this.sortType];
12201     }
12202     
12203     // set default sortType for strings and dates
12204     if(!this.sortType){
12205         switch(this.type){
12206             case "string":
12207                 this.sortType = st.asUCString;
12208                 break;
12209             case "date":
12210                 this.sortType = st.asDate;
12211                 break;
12212             default:
12213                 this.sortType = st.none;
12214         }
12215     }
12216
12217     // define once
12218     var stripRe = /[\$,%]/g;
12219
12220     // prebuilt conversion function for this field, instead of
12221     // switching every time we're reading a value
12222     if(!this.convert){
12223         var cv, dateFormat = this.dateFormat;
12224         switch(this.type){
12225             case "":
12226             case "auto":
12227             case undefined:
12228                 cv = function(v){ return v; };
12229                 break;
12230             case "string":
12231                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12232                 break;
12233             case "int":
12234                 cv = function(v){
12235                     return v !== undefined && v !== null && v !== '' ?
12236                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12237                     };
12238                 break;
12239             case "float":
12240                 cv = function(v){
12241                     return v !== undefined && v !== null && v !== '' ?
12242                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12243                     };
12244                 break;
12245             case "bool":
12246             case "boolean":
12247                 cv = function(v){ return v === true || v === "true" || v == 1; };
12248                 break;
12249             case "date":
12250                 cv = function(v){
12251                     if(!v){
12252                         return '';
12253                     }
12254                     if(v instanceof Date){
12255                         return v;
12256                     }
12257                     if(dateFormat){
12258                         if(dateFormat == "timestamp"){
12259                             return new Date(v*1000);
12260                         }
12261                         return Date.parseDate(v, dateFormat);
12262                     }
12263                     var parsed = Date.parse(v);
12264                     return parsed ? new Date(parsed) : null;
12265                 };
12266              break;
12267             
12268         }
12269         this.convert = cv;
12270     }
12271 };
12272
12273 Roo.data.Field.prototype = {
12274     dateFormat: null,
12275     defaultValue: "",
12276     mapping: null,
12277     sortType : null,
12278     sortDir : "ASC"
12279 };/*
12280  * Based on:
12281  * Ext JS Library 1.1.1
12282  * Copyright(c) 2006-2007, Ext JS, LLC.
12283  *
12284  * Originally Released Under LGPL - original licence link has changed is not relivant.
12285  *
12286  * Fork - LGPL
12287  * <script type="text/javascript">
12288  */
12289  
12290 // Base class for reading structured data from a data source.  This class is intended to be
12291 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12292
12293 /**
12294  * @class Roo.data.DataReader
12295  * Base class for reading structured data from a data source.  This class is intended to be
12296  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12297  */
12298
12299 Roo.data.DataReader = function(meta, recordType){
12300     
12301     this.meta = meta;
12302     
12303     this.recordType = recordType instanceof Array ? 
12304         Roo.data.Record.create(recordType) : recordType;
12305 };
12306
12307 Roo.data.DataReader.prototype = {
12308      /**
12309      * Create an empty record
12310      * @param {Object} data (optional) - overlay some values
12311      * @return {Roo.data.Record} record created.
12312      */
12313     newRow :  function(d) {
12314         var da =  {};
12315         this.recordType.prototype.fields.each(function(c) {
12316             switch( c.type) {
12317                 case 'int' : da[c.name] = 0; break;
12318                 case 'date' : da[c.name] = new Date(); break;
12319                 case 'float' : da[c.name] = 0.0; break;
12320                 case 'boolean' : da[c.name] = false; break;
12321                 default : da[c.name] = ""; break;
12322             }
12323             
12324         });
12325         return new this.recordType(Roo.apply(da, d));
12326     }
12327     
12328 };/*
12329  * Based on:
12330  * Ext JS Library 1.1.1
12331  * Copyright(c) 2006-2007, Ext JS, LLC.
12332  *
12333  * Originally Released Under LGPL - original licence link has changed is not relivant.
12334  *
12335  * Fork - LGPL
12336  * <script type="text/javascript">
12337  */
12338
12339 /**
12340  * @class Roo.data.DataProxy
12341  * @extends Roo.data.Observable
12342  * This class is an abstract base class for implementations which provide retrieval of
12343  * unformatted data objects.<br>
12344  * <p>
12345  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12346  * (of the appropriate type which knows how to parse the data object) to provide a block of
12347  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12348  * <p>
12349  * Custom implementations must implement the load method as described in
12350  * {@link Roo.data.HttpProxy#load}.
12351  */
12352 Roo.data.DataProxy = function(){
12353     this.addEvents({
12354         /**
12355          * @event beforeload
12356          * Fires before a network request is made to retrieve a data object.
12357          * @param {Object} This DataProxy object.
12358          * @param {Object} params The params parameter to the load function.
12359          */
12360         beforeload : true,
12361         /**
12362          * @event load
12363          * Fires before the load method's callback is called.
12364          * @param {Object} This DataProxy object.
12365          * @param {Object} o The data object.
12366          * @param {Object} arg The callback argument object passed to the load function.
12367          */
12368         load : true,
12369         /**
12370          * @event loadexception
12371          * Fires if an Exception occurs during data retrieval.
12372          * @param {Object} This DataProxy object.
12373          * @param {Object} o The data object.
12374          * @param {Object} arg The callback argument object passed to the load function.
12375          * @param {Object} e The Exception.
12376          */
12377         loadexception : true
12378     });
12379     Roo.data.DataProxy.superclass.constructor.call(this);
12380 };
12381
12382 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12383
12384     /**
12385      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12386      */
12387 /*
12388  * Based on:
12389  * Ext JS Library 1.1.1
12390  * Copyright(c) 2006-2007, Ext JS, LLC.
12391  *
12392  * Originally Released Under LGPL - original licence link has changed is not relivant.
12393  *
12394  * Fork - LGPL
12395  * <script type="text/javascript">
12396  */
12397 /**
12398  * @class Roo.data.MemoryProxy
12399  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12400  * to the Reader when its load method is called.
12401  * @constructor
12402  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12403  */
12404 Roo.data.MemoryProxy = function(data){
12405     if (data.data) {
12406         data = data.data;
12407     }
12408     Roo.data.MemoryProxy.superclass.constructor.call(this);
12409     this.data = data;
12410 };
12411
12412 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12413     
12414     /**
12415      * Load data from the requested source (in this case an in-memory
12416      * data object passed to the constructor), read the data object into
12417      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12418      * process that block using the passed callback.
12419      * @param {Object} params This parameter is not used by the MemoryProxy class.
12420      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12421      * object into a block of Roo.data.Records.
12422      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12423      * The function must be passed <ul>
12424      * <li>The Record block object</li>
12425      * <li>The "arg" argument from the load function</li>
12426      * <li>A boolean success indicator</li>
12427      * </ul>
12428      * @param {Object} scope The scope in which to call the callback
12429      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12430      */
12431     load : function(params, reader, callback, scope, arg){
12432         params = params || {};
12433         var result;
12434         try {
12435             result = reader.readRecords(params.data ? params.data :this.data);
12436         }catch(e){
12437             this.fireEvent("loadexception", this, arg, null, e);
12438             callback.call(scope, null, arg, false);
12439             return;
12440         }
12441         callback.call(scope, result, arg, true);
12442     },
12443     
12444     // private
12445     update : function(params, records){
12446         
12447     }
12448 });/*
12449  * Based on:
12450  * Ext JS Library 1.1.1
12451  * Copyright(c) 2006-2007, Ext JS, LLC.
12452  *
12453  * Originally Released Under LGPL - original licence link has changed is not relivant.
12454  *
12455  * Fork - LGPL
12456  * <script type="text/javascript">
12457  */
12458 /**
12459  * @class Roo.data.HttpProxy
12460  * @extends Roo.data.DataProxy
12461  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12462  * configured to reference a certain URL.<br><br>
12463  * <p>
12464  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12465  * from which the running page was served.<br><br>
12466  * <p>
12467  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12468  * <p>
12469  * Be aware that to enable the browser to parse an XML document, the server must set
12470  * the Content-Type header in the HTTP response to "text/xml".
12471  * @constructor
12472  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12473  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12474  * will be used to make the request.
12475  */
12476 Roo.data.HttpProxy = function(conn){
12477     Roo.data.HttpProxy.superclass.constructor.call(this);
12478     // is conn a conn config or a real conn?
12479     this.conn = conn;
12480     this.useAjax = !conn || !conn.events;
12481   
12482 };
12483
12484 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12485     // thse are take from connection...
12486     
12487     /**
12488      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12489      */
12490     /**
12491      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12492      * extra parameters to each request made by this object. (defaults to undefined)
12493      */
12494     /**
12495      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12496      *  to each request made by this object. (defaults to undefined)
12497      */
12498     /**
12499      * @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)
12500      */
12501     /**
12502      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12503      */
12504      /**
12505      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12506      * @type Boolean
12507      */
12508   
12509
12510     /**
12511      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12512      * @type Boolean
12513      */
12514     /**
12515      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12516      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12517      * a finer-grained basis than the DataProxy events.
12518      */
12519     getConnection : function(){
12520         return this.useAjax ? Roo.Ajax : this.conn;
12521     },
12522
12523     /**
12524      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12525      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12526      * process that block using the passed callback.
12527      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12528      * for the request to the remote server.
12529      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12530      * object into a block of Roo.data.Records.
12531      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12532      * The function must be passed <ul>
12533      * <li>The Record block object</li>
12534      * <li>The "arg" argument from the load function</li>
12535      * <li>A boolean success indicator</li>
12536      * </ul>
12537      * @param {Object} scope The scope in which to call the callback
12538      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12539      */
12540     load : function(params, reader, callback, scope, arg){
12541         if(this.fireEvent("beforeload", this, params) !== false){
12542             var  o = {
12543                 params : params || {},
12544                 request: {
12545                     callback : callback,
12546                     scope : scope,
12547                     arg : arg
12548                 },
12549                 reader: reader,
12550                 callback : this.loadResponse,
12551                 scope: this
12552             };
12553             if(this.useAjax){
12554                 Roo.applyIf(o, this.conn);
12555                 if(this.activeRequest){
12556                     Roo.Ajax.abort(this.activeRequest);
12557                 }
12558                 this.activeRequest = Roo.Ajax.request(o);
12559             }else{
12560                 this.conn.request(o);
12561             }
12562         }else{
12563             callback.call(scope||this, null, arg, false);
12564         }
12565     },
12566
12567     // private
12568     loadResponse : function(o, success, response){
12569         delete this.activeRequest;
12570         if(!success){
12571             this.fireEvent("loadexception", this, o, response);
12572             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12573             return;
12574         }
12575         var result;
12576         try {
12577             result = o.reader.read(response);
12578         }catch(e){
12579             this.fireEvent("loadexception", this, o, response, e);
12580             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12581             return;
12582         }
12583         
12584         this.fireEvent("load", this, o, o.request.arg);
12585         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12586     },
12587
12588     // private
12589     update : function(dataSet){
12590
12591     },
12592
12593     // private
12594     updateResponse : function(dataSet){
12595
12596     }
12597 });/*
12598  * Based on:
12599  * Ext JS Library 1.1.1
12600  * Copyright(c) 2006-2007, Ext JS, LLC.
12601  *
12602  * Originally Released Under LGPL - original licence link has changed is not relivant.
12603  *
12604  * Fork - LGPL
12605  * <script type="text/javascript">
12606  */
12607
12608 /**
12609  * @class Roo.data.ScriptTagProxy
12610  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12611  * other than the originating domain of the running page.<br><br>
12612  * <p>
12613  * <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
12614  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12615  * <p>
12616  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12617  * source code that is used as the source inside a &lt;script> tag.<br><br>
12618  * <p>
12619  * In order for the browser to process the returned data, the server must wrap the data object
12620  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12621  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12622  * depending on whether the callback name was passed:
12623  * <p>
12624  * <pre><code>
12625 boolean scriptTag = false;
12626 String cb = request.getParameter("callback");
12627 if (cb != null) {
12628     scriptTag = true;
12629     response.setContentType("text/javascript");
12630 } else {
12631     response.setContentType("application/x-json");
12632 }
12633 Writer out = response.getWriter();
12634 if (scriptTag) {
12635     out.write(cb + "(");
12636 }
12637 out.print(dataBlock.toJsonString());
12638 if (scriptTag) {
12639     out.write(");");
12640 }
12641 </pre></code>
12642  *
12643  * @constructor
12644  * @param {Object} config A configuration object.
12645  */
12646 Roo.data.ScriptTagProxy = function(config){
12647     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12648     Roo.apply(this, config);
12649     this.head = document.getElementsByTagName("head")[0];
12650 };
12651
12652 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12653
12654 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12655     /**
12656      * @cfg {String} url The URL from which to request the data object.
12657      */
12658     /**
12659      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12660      */
12661     timeout : 30000,
12662     /**
12663      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12664      * the server the name of the callback function set up by the load call to process the returned data object.
12665      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12666      * javascript output which calls this named function passing the data object as its only parameter.
12667      */
12668     callbackParam : "callback",
12669     /**
12670      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12671      * name to the request.
12672      */
12673     nocache : true,
12674
12675     /**
12676      * Load data from the configured URL, read the data object into
12677      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12678      * process that block using the passed callback.
12679      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12680      * for the request to the remote server.
12681      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12682      * object into a block of Roo.data.Records.
12683      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12684      * The function must be passed <ul>
12685      * <li>The Record block object</li>
12686      * <li>The "arg" argument from the load function</li>
12687      * <li>A boolean success indicator</li>
12688      * </ul>
12689      * @param {Object} scope The scope in which to call the callback
12690      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12691      */
12692     load : function(params, reader, callback, scope, arg){
12693         if(this.fireEvent("beforeload", this, params) !== false){
12694
12695             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12696
12697             var url = this.url;
12698             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12699             if(this.nocache){
12700                 url += "&_dc=" + (new Date().getTime());
12701             }
12702             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12703             var trans = {
12704                 id : transId,
12705                 cb : "stcCallback"+transId,
12706                 scriptId : "stcScript"+transId,
12707                 params : params,
12708                 arg : arg,
12709                 url : url,
12710                 callback : callback,
12711                 scope : scope,
12712                 reader : reader
12713             };
12714             var conn = this;
12715
12716             window[trans.cb] = function(o){
12717                 conn.handleResponse(o, trans);
12718             };
12719
12720             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12721
12722             if(this.autoAbort !== false){
12723                 this.abort();
12724             }
12725
12726             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12727
12728             var script = document.createElement("script");
12729             script.setAttribute("src", url);
12730             script.setAttribute("type", "text/javascript");
12731             script.setAttribute("id", trans.scriptId);
12732             this.head.appendChild(script);
12733
12734             this.trans = trans;
12735         }else{
12736             callback.call(scope||this, null, arg, false);
12737         }
12738     },
12739
12740     // private
12741     isLoading : function(){
12742         return this.trans ? true : false;
12743     },
12744
12745     /**
12746      * Abort the current server request.
12747      */
12748     abort : function(){
12749         if(this.isLoading()){
12750             this.destroyTrans(this.trans);
12751         }
12752     },
12753
12754     // private
12755     destroyTrans : function(trans, isLoaded){
12756         this.head.removeChild(document.getElementById(trans.scriptId));
12757         clearTimeout(trans.timeoutId);
12758         if(isLoaded){
12759             window[trans.cb] = undefined;
12760             try{
12761                 delete window[trans.cb];
12762             }catch(e){}
12763         }else{
12764             // if hasn't been loaded, wait for load to remove it to prevent script error
12765             window[trans.cb] = function(){
12766                 window[trans.cb] = undefined;
12767                 try{
12768                     delete window[trans.cb];
12769                 }catch(e){}
12770             };
12771         }
12772     },
12773
12774     // private
12775     handleResponse : function(o, trans){
12776         this.trans = false;
12777         this.destroyTrans(trans, true);
12778         var result;
12779         try {
12780             result = trans.reader.readRecords(o);
12781         }catch(e){
12782             this.fireEvent("loadexception", this, o, trans.arg, e);
12783             trans.callback.call(trans.scope||window, null, trans.arg, false);
12784             return;
12785         }
12786         this.fireEvent("load", this, o, trans.arg);
12787         trans.callback.call(trans.scope||window, result, trans.arg, true);
12788     },
12789
12790     // private
12791     handleFailure : function(trans){
12792         this.trans = false;
12793         this.destroyTrans(trans, false);
12794         this.fireEvent("loadexception", this, null, trans.arg);
12795         trans.callback.call(trans.scope||window, null, trans.arg, false);
12796     }
12797 });/*
12798  * Based on:
12799  * Ext JS Library 1.1.1
12800  * Copyright(c) 2006-2007, Ext JS, LLC.
12801  *
12802  * Originally Released Under LGPL - original licence link has changed is not relivant.
12803  *
12804  * Fork - LGPL
12805  * <script type="text/javascript">
12806  */
12807
12808 /**
12809  * @class Roo.data.JsonReader
12810  * @extends Roo.data.DataReader
12811  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12812  * based on mappings in a provided Roo.data.Record constructor.
12813  * 
12814  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12815  * in the reply previously. 
12816  * 
12817  * <p>
12818  * Example code:
12819  * <pre><code>
12820 var RecordDef = Roo.data.Record.create([
12821     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12822     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12823 ]);
12824 var myReader = new Roo.data.JsonReader({
12825     totalProperty: "results",    // The property which contains the total dataset size (optional)
12826     root: "rows",                // The property which contains an Array of row objects
12827     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12828 }, RecordDef);
12829 </code></pre>
12830  * <p>
12831  * This would consume a JSON file like this:
12832  * <pre><code>
12833 { 'results': 2, 'rows': [
12834     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12835     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12836 }
12837 </code></pre>
12838  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12839  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12840  * paged from the remote server.
12841  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12842  * @cfg {String} root name of the property which contains the Array of row objects.
12843  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12844  * @cfg {Array} fields Array of field definition objects
12845  * @constructor
12846  * Create a new JsonReader
12847  * @param {Object} meta Metadata configuration options
12848  * @param {Object} recordType Either an Array of field definition objects,
12849  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12850  */
12851 Roo.data.JsonReader = function(meta, recordType){
12852     
12853     meta = meta || {};
12854     // set some defaults:
12855     Roo.applyIf(meta, {
12856         totalProperty: 'total',
12857         successProperty : 'success',
12858         root : 'data',
12859         id : 'id'
12860     });
12861     
12862     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12863 };
12864 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12865     
12866     /**
12867      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12868      * Used by Store query builder to append _requestMeta to params.
12869      * 
12870      */
12871     metaFromRemote : false,
12872     /**
12873      * This method is only used by a DataProxy which has retrieved data from a remote server.
12874      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12875      * @return {Object} data A data block which is used by an Roo.data.Store object as
12876      * a cache of Roo.data.Records.
12877      */
12878     read : function(response){
12879         var json = response.responseText;
12880        
12881         var o = /* eval:var:o */ eval("("+json+")");
12882         if(!o) {
12883             throw {message: "JsonReader.read: Json object not found"};
12884         }
12885         
12886         if(o.metaData){
12887             
12888             delete this.ef;
12889             this.metaFromRemote = true;
12890             this.meta = o.metaData;
12891             this.recordType = Roo.data.Record.create(o.metaData.fields);
12892             this.onMetaChange(this.meta, this.recordType, o);
12893         }
12894         return this.readRecords(o);
12895     },
12896
12897     // private function a store will implement
12898     onMetaChange : function(meta, recordType, o){
12899
12900     },
12901
12902     /**
12903          * @ignore
12904          */
12905     simpleAccess: function(obj, subsc) {
12906         return obj[subsc];
12907     },
12908
12909         /**
12910          * @ignore
12911          */
12912     getJsonAccessor: function(){
12913         var re = /[\[\.]/;
12914         return function(expr) {
12915             try {
12916                 return(re.test(expr))
12917                     ? new Function("obj", "return obj." + expr)
12918                     : function(obj){
12919                         return obj[expr];
12920                     };
12921             } catch(e){}
12922             return Roo.emptyFn;
12923         };
12924     }(),
12925
12926     /**
12927      * Create a data block containing Roo.data.Records from an XML document.
12928      * @param {Object} o An object which contains an Array of row objects in the property specified
12929      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12930      * which contains the total size of the dataset.
12931      * @return {Object} data A data block which is used by an Roo.data.Store object as
12932      * a cache of Roo.data.Records.
12933      */
12934     readRecords : function(o){
12935         /**
12936          * After any data loads, the raw JSON data is available for further custom processing.
12937          * @type Object
12938          */
12939         this.o = o;
12940         var s = this.meta, Record = this.recordType,
12941             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12942
12943 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12944         if (!this.ef) {
12945             if(s.totalProperty) {
12946                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12947                 }
12948                 if(s.successProperty) {
12949                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12950                 }
12951                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12952                 if (s.id) {
12953                         var g = this.getJsonAccessor(s.id);
12954                         this.getId = function(rec) {
12955                                 var r = g(rec);  
12956                                 return (r === undefined || r === "") ? null : r;
12957                         };
12958                 } else {
12959                         this.getId = function(){return null;};
12960                 }
12961             this.ef = [];
12962             for(var jj = 0; jj < fl; jj++){
12963                 f = fi[jj];
12964                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12965                 this.ef[jj] = this.getJsonAccessor(map);
12966             }
12967         }
12968
12969         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12970         if(s.totalProperty){
12971             var vt = parseInt(this.getTotal(o), 10);
12972             if(!isNaN(vt)){
12973                 totalRecords = vt;
12974             }
12975         }
12976         if(s.successProperty){
12977             var vs = this.getSuccess(o);
12978             if(vs === false || vs === 'false'){
12979                 success = false;
12980             }
12981         }
12982         var records = [];
12983         for(var i = 0; i < c; i++){
12984                 var n = root[i];
12985             var values = {};
12986             var id = this.getId(n);
12987             for(var j = 0; j < fl; j++){
12988                 f = fi[j];
12989             var v = this.ef[j](n);
12990             if (!f.convert) {
12991                 Roo.log('missing convert for ' + f.name);
12992                 Roo.log(f);
12993                 continue;
12994             }
12995             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12996             }
12997             var record = new Record(values, id);
12998             record.json = n;
12999             records[i] = record;
13000         }
13001         return {
13002             raw : o,
13003             success : success,
13004             records : records,
13005             totalRecords : totalRecords
13006         };
13007     }
13008 });/*
13009  * Based on:
13010  * Ext JS Library 1.1.1
13011  * Copyright(c) 2006-2007, Ext JS, LLC.
13012  *
13013  * Originally Released Under LGPL - original licence link has changed is not relivant.
13014  *
13015  * Fork - LGPL
13016  * <script type="text/javascript">
13017  */
13018
13019 /**
13020  * @class Roo.data.ArrayReader
13021  * @extends Roo.data.DataReader
13022  * Data reader class to create an Array of Roo.data.Record objects from an Array.
13023  * Each element of that Array represents a row of data fields. The
13024  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13025  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13026  * <p>
13027  * Example code:.
13028  * <pre><code>
13029 var RecordDef = Roo.data.Record.create([
13030     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
13031     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
13032 ]);
13033 var myReader = new Roo.data.ArrayReader({
13034     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
13035 }, RecordDef);
13036 </code></pre>
13037  * <p>
13038  * This would consume an Array like this:
13039  * <pre><code>
13040 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13041   </code></pre>
13042  
13043  * @constructor
13044  * Create a new JsonReader
13045  * @param {Object} meta Metadata configuration options.
13046  * @param {Object|Array} recordType Either an Array of field definition objects
13047  * 
13048  * @cfg {Array} fields Array of field definition objects
13049  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13050  * as specified to {@link Roo.data.Record#create},
13051  * or an {@link Roo.data.Record} object
13052  *
13053  * 
13054  * created using {@link Roo.data.Record#create}.
13055  */
13056 Roo.data.ArrayReader = function(meta, recordType){
13057     
13058      
13059     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13060 };
13061
13062 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13063     /**
13064      * Create a data block containing Roo.data.Records from an XML document.
13065      * @param {Object} o An Array of row objects which represents the dataset.
13066      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13067      * a cache of Roo.data.Records.
13068      */
13069     readRecords : function(o){
13070         var sid = this.meta ? this.meta.id : null;
13071         var recordType = this.recordType, fields = recordType.prototype.fields;
13072         var records = [];
13073         var root = o;
13074             for(var i = 0; i < root.length; i++){
13075                     var n = root[i];
13076                 var values = {};
13077                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13078                 for(var j = 0, jlen = fields.length; j < jlen; j++){
13079                 var f = fields.items[j];
13080                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13081                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13082                 v = f.convert(v);
13083                 values[f.name] = v;
13084             }
13085                 var record = new recordType(values, id);
13086                 record.json = n;
13087                 records[records.length] = record;
13088             }
13089             return {
13090                 records : records,
13091                 totalRecords : records.length
13092             };
13093     }
13094 });/*
13095  * - LGPL
13096  * * 
13097  */
13098
13099 /**
13100  * @class Roo.bootstrap.ComboBox
13101  * @extends Roo.bootstrap.TriggerField
13102  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13103  * @cfg {Boolean} append (true|false) default false
13104  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13105  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13106  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13107  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13108  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13109  * @cfg {Boolean} animate default true
13110  * @cfg {Boolean} emptyResultText only for touch device
13111  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13112  * @cfg {String} emptyTitle default ''
13113  * @constructor
13114  * Create a new ComboBox.
13115  * @param {Object} config Configuration options
13116  */
13117 Roo.bootstrap.ComboBox = function(config){
13118     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13119     this.addEvents({
13120         /**
13121          * @event expand
13122          * Fires when the dropdown list is expanded
13123         * @param {Roo.bootstrap.ComboBox} combo This combo box
13124         */
13125         'expand' : true,
13126         /**
13127          * @event collapse
13128          * Fires when the dropdown list is collapsed
13129         * @param {Roo.bootstrap.ComboBox} combo This combo box
13130         */
13131         'collapse' : true,
13132         /**
13133          * @event beforeselect
13134          * Fires before a list item is selected. Return false to cancel the selection.
13135         * @param {Roo.bootstrap.ComboBox} combo This combo box
13136         * @param {Roo.data.Record} record The data record returned from the underlying store
13137         * @param {Number} index The index of the selected item in the dropdown list
13138         */
13139         'beforeselect' : true,
13140         /**
13141          * @event select
13142          * Fires when a list item is selected
13143         * @param {Roo.bootstrap.ComboBox} combo This combo box
13144         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13145         * @param {Number} index The index of the selected item in the dropdown list
13146         */
13147         'select' : true,
13148         /**
13149          * @event beforequery
13150          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13151          * The event object passed has these properties:
13152         * @param {Roo.bootstrap.ComboBox} combo This combo box
13153         * @param {String} query The query
13154         * @param {Boolean} forceAll true to force "all" query
13155         * @param {Boolean} cancel true to cancel the query
13156         * @param {Object} e The query event object
13157         */
13158         'beforequery': true,
13159          /**
13160          * @event add
13161          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13162         * @param {Roo.bootstrap.ComboBox} combo This combo box
13163         */
13164         'add' : true,
13165         /**
13166          * @event edit
13167          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13168         * @param {Roo.bootstrap.ComboBox} combo This combo box
13169         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13170         */
13171         'edit' : true,
13172         /**
13173          * @event remove
13174          * Fires when the remove value from the combobox array
13175         * @param {Roo.bootstrap.ComboBox} combo This combo box
13176         */
13177         'remove' : true,
13178         /**
13179          * @event afterremove
13180          * Fires when the remove value from the combobox array
13181         * @param {Roo.bootstrap.ComboBox} combo This combo box
13182         */
13183         'afterremove' : true,
13184         /**
13185          * @event specialfilter
13186          * Fires when specialfilter
13187             * @param {Roo.bootstrap.ComboBox} combo This combo box
13188             */
13189         'specialfilter' : true,
13190         /**
13191          * @event tick
13192          * Fires when tick the element
13193             * @param {Roo.bootstrap.ComboBox} combo This combo box
13194             */
13195         'tick' : true,
13196         /**
13197          * @event touchviewdisplay
13198          * Fires when touch view require special display (default is using displayField)
13199             * @param {Roo.bootstrap.ComboBox} combo This combo box
13200             * @param {Object} cfg set html .
13201             */
13202         'touchviewdisplay' : true
13203         
13204     });
13205     
13206     this.item = [];
13207     this.tickItems = [];
13208     
13209     this.selectedIndex = -1;
13210     if(this.mode == 'local'){
13211         if(config.queryDelay === undefined){
13212             this.queryDelay = 10;
13213         }
13214         if(config.minChars === undefined){
13215             this.minChars = 0;
13216         }
13217     }
13218 };
13219
13220 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13221      
13222     /**
13223      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13224      * rendering into an Roo.Editor, defaults to false)
13225      */
13226     /**
13227      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13228      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13229      */
13230     /**
13231      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13232      */
13233     /**
13234      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13235      * the dropdown list (defaults to undefined, with no header element)
13236      */
13237
13238      /**
13239      * @cfg {String/Roo.Template} tpl The template to use to render the output
13240      */
13241      
13242      /**
13243      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13244      */
13245     listWidth: undefined,
13246     /**
13247      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13248      * mode = 'remote' or 'text' if mode = 'local')
13249      */
13250     displayField: undefined,
13251     
13252     /**
13253      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13254      * mode = 'remote' or 'value' if mode = 'local'). 
13255      * Note: use of a valueField requires the user make a selection
13256      * in order for a value to be mapped.
13257      */
13258     valueField: undefined,
13259     /**
13260      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13261      */
13262     modalTitle : '',
13263     
13264     /**
13265      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13266      * field's data value (defaults to the underlying DOM element's name)
13267      */
13268     hiddenName: undefined,
13269     /**
13270      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13271      */
13272     listClass: '',
13273     /**
13274      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13275      */
13276     selectedClass: 'active',
13277     
13278     /**
13279      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13280      */
13281     shadow:'sides',
13282     /**
13283      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13284      * anchor positions (defaults to 'tl-bl')
13285      */
13286     listAlign: 'tl-bl?',
13287     /**
13288      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13289      */
13290     maxHeight: 300,
13291     /**
13292      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13293      * query specified by the allQuery config option (defaults to 'query')
13294      */
13295     triggerAction: 'query',
13296     /**
13297      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13298      * (defaults to 4, does not apply if editable = false)
13299      */
13300     minChars : 4,
13301     /**
13302      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13303      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13304      */
13305     typeAhead: false,
13306     /**
13307      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13308      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13309      */
13310     queryDelay: 500,
13311     /**
13312      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13313      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13314      */
13315     pageSize: 0,
13316     /**
13317      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13318      * when editable = true (defaults to false)
13319      */
13320     selectOnFocus:false,
13321     /**
13322      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13323      */
13324     queryParam: 'query',
13325     /**
13326      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13327      * when mode = 'remote' (defaults to 'Loading...')
13328      */
13329     loadingText: 'Loading...',
13330     /**
13331      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13332      */
13333     resizable: false,
13334     /**
13335      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13336      */
13337     handleHeight : 8,
13338     /**
13339      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13340      * traditional select (defaults to true)
13341      */
13342     editable: true,
13343     /**
13344      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13345      */
13346     allQuery: '',
13347     /**
13348      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13349      */
13350     mode: 'remote',
13351     /**
13352      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13353      * listWidth has a higher value)
13354      */
13355     minListWidth : 70,
13356     /**
13357      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13358      * allow the user to set arbitrary text into the field (defaults to false)
13359      */
13360     forceSelection:false,
13361     /**
13362      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13363      * if typeAhead = true (defaults to 250)
13364      */
13365     typeAheadDelay : 250,
13366     /**
13367      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13368      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13369      */
13370     valueNotFoundText : undefined,
13371     /**
13372      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13373      */
13374     blockFocus : false,
13375     
13376     /**
13377      * @cfg {Boolean} disableClear Disable showing of clear button.
13378      */
13379     disableClear : false,
13380     /**
13381      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13382      */
13383     alwaysQuery : false,
13384     
13385     /**
13386      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13387      */
13388     multiple : false,
13389     
13390     /**
13391      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13392      */
13393     invalidClass : "has-warning",
13394     
13395     /**
13396      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13397      */
13398     validClass : "has-success",
13399     
13400     /**
13401      * @cfg {Boolean} specialFilter (true|false) special filter default false
13402      */
13403     specialFilter : false,
13404     
13405     /**
13406      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13407      */
13408     mobileTouchView : true,
13409     
13410     /**
13411      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13412      */
13413     useNativeIOS : false,
13414     
13415     /**
13416      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13417      */
13418     mobile_restrict_height : false,
13419     
13420     ios_options : false,
13421     
13422     //private
13423     addicon : false,
13424     editicon: false,
13425     
13426     page: 0,
13427     hasQuery: false,
13428     append: false,
13429     loadNext: false,
13430     autoFocus : true,
13431     tickable : false,
13432     btnPosition : 'right',
13433     triggerList : true,
13434     showToggleBtn : true,
13435     animate : true,
13436     emptyResultText: 'Empty',
13437     triggerText : 'Select',
13438     emptyTitle : '',
13439     
13440     // element that contains real text value.. (when hidden is used..)
13441     
13442     getAutoCreate : function()
13443     {   
13444         var cfg = false;
13445         //render
13446         /*
13447          * Render classic select for iso
13448          */
13449         
13450         if(Roo.isIOS && this.useNativeIOS){
13451             cfg = this.getAutoCreateNativeIOS();
13452             return cfg;
13453         }
13454         
13455         /*
13456          * Touch Devices
13457          */
13458         
13459         if(Roo.isTouch && this.mobileTouchView){
13460             cfg = this.getAutoCreateTouchView();
13461             return cfg;;
13462         }
13463         
13464         /*
13465          *  Normal ComboBox
13466          */
13467         if(!this.tickable){
13468             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13469             return cfg;
13470         }
13471         
13472         /*
13473          *  ComboBox with tickable selections
13474          */
13475              
13476         var align = this.labelAlign || this.parentLabelAlign();
13477         
13478         cfg = {
13479             cls : 'form-group roo-combobox-tickable' //input-group
13480         };
13481         
13482         var btn_text_select = '';
13483         var btn_text_done = '';
13484         var btn_text_cancel = '';
13485         
13486         if (this.btn_text_show) {
13487             btn_text_select = 'Select';
13488             btn_text_done = 'Done';
13489             btn_text_cancel = 'Cancel'; 
13490         }
13491         
13492         var buttons = {
13493             tag : 'div',
13494             cls : 'tickable-buttons',
13495             cn : [
13496                 {
13497                     tag : 'button',
13498                     type : 'button',
13499                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13500                     //html : this.triggerText
13501                     html: btn_text_select
13502                 },
13503                 {
13504                     tag : 'button',
13505                     type : 'button',
13506                     name : 'ok',
13507                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13508                     //html : 'Done'
13509                     html: btn_text_done
13510                 },
13511                 {
13512                     tag : 'button',
13513                     type : 'button',
13514                     name : 'cancel',
13515                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13516                     //html : 'Cancel'
13517                     html: btn_text_cancel
13518                 }
13519             ]
13520         };
13521         
13522         if(this.editable){
13523             buttons.cn.unshift({
13524                 tag: 'input',
13525                 cls: 'roo-select2-search-field-input'
13526             });
13527         }
13528         
13529         var _this = this;
13530         
13531         Roo.each(buttons.cn, function(c){
13532             if (_this.size) {
13533                 c.cls += ' btn-' + _this.size;
13534             }
13535
13536             if (_this.disabled) {
13537                 c.disabled = true;
13538             }
13539         });
13540         
13541         var box = {
13542             tag: 'div',
13543             style : 'display: contents',
13544             cn: [
13545                 {
13546                     tag: 'input',
13547                     type : 'hidden',
13548                     cls: 'form-hidden-field'
13549                 },
13550                 {
13551                     tag: 'ul',
13552                     cls: 'roo-select2-choices',
13553                     cn:[
13554                         {
13555                             tag: 'li',
13556                             cls: 'roo-select2-search-field',
13557                             cn: [
13558                                 buttons
13559                             ]
13560                         }
13561                     ]
13562                 }
13563             ]
13564         };
13565         
13566         var combobox = {
13567             cls: 'roo-select2-container input-group roo-select2-container-multi',
13568             cn: [
13569                 
13570                 box
13571 //                {
13572 //                    tag: 'ul',
13573 //                    cls: 'typeahead typeahead-long dropdown-menu',
13574 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13575 //                }
13576             ]
13577         };
13578         
13579         if(this.hasFeedback && !this.allowBlank){
13580             
13581             var feedback = {
13582                 tag: 'span',
13583                 cls: 'glyphicon form-control-feedback'
13584             };
13585
13586             combobox.cn.push(feedback);
13587         }
13588         
13589         var indicator = {
13590             tag : 'i',
13591             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13592             tooltip : 'This field is required'
13593         };
13594         if (Roo.bootstrap.version == 4) {
13595             indicator = {
13596                 tag : 'i',
13597                 style : 'display:none'
13598             };
13599         }
13600         if (align ==='left' && this.fieldLabel.length) {
13601             
13602             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13603             
13604             cfg.cn = [
13605                 indicator,
13606                 {
13607                     tag: 'label',
13608                     'for' :  id,
13609                     cls : 'control-label col-form-label',
13610                     html : this.fieldLabel
13611
13612                 },
13613                 {
13614                     cls : "", 
13615                     cn: [
13616                         combobox
13617                     ]
13618                 }
13619
13620             ];
13621             
13622             var labelCfg = cfg.cn[1];
13623             var contentCfg = cfg.cn[2];
13624             
13625
13626             if(this.indicatorpos == 'right'){
13627                 
13628                 cfg.cn = [
13629                     {
13630                         tag: 'label',
13631                         'for' :  id,
13632                         cls : 'control-label col-form-label',
13633                         cn : [
13634                             {
13635                                 tag : 'span',
13636                                 html : this.fieldLabel
13637                             },
13638                             indicator
13639                         ]
13640                     },
13641                     {
13642                         cls : "",
13643                         cn: [
13644                             combobox
13645                         ]
13646                     }
13647
13648                 ];
13649                 
13650                 
13651                 
13652                 labelCfg = cfg.cn[0];
13653                 contentCfg = cfg.cn[1];
13654             
13655             }
13656             
13657             if(this.labelWidth > 12){
13658                 labelCfg.style = "width: " + this.labelWidth + 'px';
13659             }
13660             
13661             if(this.labelWidth < 13 && this.labelmd == 0){
13662                 this.labelmd = this.labelWidth;
13663             }
13664             
13665             if(this.labellg > 0){
13666                 labelCfg.cls += ' col-lg-' + this.labellg;
13667                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13668             }
13669             
13670             if(this.labelmd > 0){
13671                 labelCfg.cls += ' col-md-' + this.labelmd;
13672                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13673             }
13674             
13675             if(this.labelsm > 0){
13676                 labelCfg.cls += ' col-sm-' + this.labelsm;
13677                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13678             }
13679             
13680             if(this.labelxs > 0){
13681                 labelCfg.cls += ' col-xs-' + this.labelxs;
13682                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13683             }
13684                 
13685                 
13686         } else if ( this.fieldLabel.length) {
13687 //                Roo.log(" label");
13688                  cfg.cn = [
13689                    indicator,
13690                     {
13691                         tag: 'label',
13692                         //cls : 'input-group-addon',
13693                         html : this.fieldLabel
13694                     },
13695                     combobox
13696                 ];
13697                 
13698                 if(this.indicatorpos == 'right'){
13699                     cfg.cn = [
13700                         {
13701                             tag: 'label',
13702                             //cls : 'input-group-addon',
13703                             html : this.fieldLabel
13704                         },
13705                         indicator,
13706                         combobox
13707                     ];
13708                     
13709                 }
13710
13711         } else {
13712             
13713 //                Roo.log(" no label && no align");
13714                 cfg = combobox
13715                      
13716                 
13717         }
13718          
13719         var settings=this;
13720         ['xs','sm','md','lg'].map(function(size){
13721             if (settings[size]) {
13722                 cfg.cls += ' col-' + size + '-' + settings[size];
13723             }
13724         });
13725         
13726         return cfg;
13727         
13728     },
13729     
13730     _initEventsCalled : false,
13731     
13732     // private
13733     initEvents: function()
13734     {   
13735         if (this._initEventsCalled) { // as we call render... prevent looping...
13736             return;
13737         }
13738         this._initEventsCalled = true;
13739         
13740         if (!this.store) {
13741             throw "can not find store for combo";
13742         }
13743         
13744         this.indicator = this.indicatorEl();
13745         
13746         this.store = Roo.factory(this.store, Roo.data);
13747         this.store.parent = this;
13748         
13749         // if we are building from html. then this element is so complex, that we can not really
13750         // use the rendered HTML.
13751         // so we have to trash and replace the previous code.
13752         if (Roo.XComponent.build_from_html) {
13753             // remove this element....
13754             var e = this.el.dom, k=0;
13755             while (e ) { e = e.previousSibling;  ++k;}
13756
13757             this.el.remove();
13758             
13759             this.el=false;
13760             this.rendered = false;
13761             
13762             this.render(this.parent().getChildContainer(true), k);
13763         }
13764         
13765         if(Roo.isIOS && this.useNativeIOS){
13766             this.initIOSView();
13767             return;
13768         }
13769         
13770         /*
13771          * Touch Devices
13772          */
13773         
13774         if(Roo.isTouch && this.mobileTouchView){
13775             this.initTouchView();
13776             return;
13777         }
13778         
13779         if(this.tickable){
13780             this.initTickableEvents();
13781             return;
13782         }
13783         
13784         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13785         
13786         if(this.hiddenName){
13787             
13788             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13789             
13790             this.hiddenField.dom.value =
13791                 this.hiddenValue !== undefined ? this.hiddenValue :
13792                 this.value !== undefined ? this.value : '';
13793
13794             // prevent input submission
13795             this.el.dom.removeAttribute('name');
13796             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13797              
13798              
13799         }
13800         //if(Roo.isGecko){
13801         //    this.el.dom.setAttribute('autocomplete', 'off');
13802         //}
13803         
13804         var cls = 'x-combo-list';
13805         
13806         //this.list = new Roo.Layer({
13807         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13808         //});
13809         
13810         var _this = this;
13811         
13812         (function(){
13813             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13814             _this.list.setWidth(lw);
13815         }).defer(100);
13816         
13817         this.list.on('mouseover', this.onViewOver, this);
13818         this.list.on('mousemove', this.onViewMove, this);
13819         this.list.on('scroll', this.onViewScroll, this);
13820         
13821         /*
13822         this.list.swallowEvent('mousewheel');
13823         this.assetHeight = 0;
13824
13825         if(this.title){
13826             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13827             this.assetHeight += this.header.getHeight();
13828         }
13829
13830         this.innerList = this.list.createChild({cls:cls+'-inner'});
13831         this.innerList.on('mouseover', this.onViewOver, this);
13832         this.innerList.on('mousemove', this.onViewMove, this);
13833         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13834         
13835         if(this.allowBlank && !this.pageSize && !this.disableClear){
13836             this.footer = this.list.createChild({cls:cls+'-ft'});
13837             this.pageTb = new Roo.Toolbar(this.footer);
13838            
13839         }
13840         if(this.pageSize){
13841             this.footer = this.list.createChild({cls:cls+'-ft'});
13842             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13843                     {pageSize: this.pageSize});
13844             
13845         }
13846         
13847         if (this.pageTb && this.allowBlank && !this.disableClear) {
13848             var _this = this;
13849             this.pageTb.add(new Roo.Toolbar.Fill(), {
13850                 cls: 'x-btn-icon x-btn-clear',
13851                 text: '&#160;',
13852                 handler: function()
13853                 {
13854                     _this.collapse();
13855                     _this.clearValue();
13856                     _this.onSelect(false, -1);
13857                 }
13858             });
13859         }
13860         if (this.footer) {
13861             this.assetHeight += this.footer.getHeight();
13862         }
13863         */
13864             
13865         if(!this.tpl){
13866             this.tpl = Roo.bootstrap.version == 4 ?
13867                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13868                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13869         }
13870
13871         this.view = new Roo.View(this.list, this.tpl, {
13872             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13873         });
13874         //this.view.wrapEl.setDisplayed(false);
13875         this.view.on('click', this.onViewClick, this);
13876         
13877         
13878         this.store.on('beforeload', this.onBeforeLoad, this);
13879         this.store.on('load', this.onLoad, this);
13880         this.store.on('loadexception', this.onLoadException, this);
13881         /*
13882         if(this.resizable){
13883             this.resizer = new Roo.Resizable(this.list,  {
13884                pinned:true, handles:'se'
13885             });
13886             this.resizer.on('resize', function(r, w, h){
13887                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13888                 this.listWidth = w;
13889                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13890                 this.restrictHeight();
13891             }, this);
13892             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13893         }
13894         */
13895         if(!this.editable){
13896             this.editable = true;
13897             this.setEditable(false);
13898         }
13899         
13900         /*
13901         
13902         if (typeof(this.events.add.listeners) != 'undefined') {
13903             
13904             this.addicon = this.wrap.createChild(
13905                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13906        
13907             this.addicon.on('click', function(e) {
13908                 this.fireEvent('add', this);
13909             }, this);
13910         }
13911         if (typeof(this.events.edit.listeners) != 'undefined') {
13912             
13913             this.editicon = this.wrap.createChild(
13914                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13915             if (this.addicon) {
13916                 this.editicon.setStyle('margin-left', '40px');
13917             }
13918             this.editicon.on('click', function(e) {
13919                 
13920                 // we fire even  if inothing is selected..
13921                 this.fireEvent('edit', this, this.lastData );
13922                 
13923             }, this);
13924         }
13925         */
13926         
13927         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13928             "up" : function(e){
13929                 this.inKeyMode = true;
13930                 this.selectPrev();
13931             },
13932
13933             "down" : function(e){
13934                 if(!this.isExpanded()){
13935                     this.onTriggerClick();
13936                 }else{
13937                     this.inKeyMode = true;
13938                     this.selectNext();
13939                 }
13940             },
13941
13942             "enter" : function(e){
13943 //                this.onViewClick();
13944                 //return true;
13945                 this.collapse();
13946                 
13947                 if(this.fireEvent("specialkey", this, e)){
13948                     this.onViewClick(false);
13949                 }
13950                 
13951                 return true;
13952             },
13953
13954             "esc" : function(e){
13955                 this.collapse();
13956             },
13957
13958             "tab" : function(e){
13959                 this.collapse();
13960                 
13961                 if(this.fireEvent("specialkey", this, e)){
13962                     this.onViewClick(false);
13963                 }
13964                 
13965                 return true;
13966             },
13967
13968             scope : this,
13969
13970             doRelay : function(foo, bar, hname){
13971                 if(hname == 'down' || this.scope.isExpanded()){
13972                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13973                 }
13974                 return true;
13975             },
13976
13977             forceKeyDown: true
13978         });
13979         
13980         
13981         this.queryDelay = Math.max(this.queryDelay || 10,
13982                 this.mode == 'local' ? 10 : 250);
13983         
13984         
13985         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13986         
13987         if(this.typeAhead){
13988             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13989         }
13990         if(this.editable !== false){
13991             this.inputEl().on("keyup", this.onKeyUp, this);
13992         }
13993         if(this.forceSelection){
13994             this.inputEl().on('blur', this.doForce, this);
13995         }
13996         
13997         if(this.multiple){
13998             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13999             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14000         }
14001     },
14002     
14003     initTickableEvents: function()
14004     {   
14005         this.createList();
14006         
14007         if(this.hiddenName){
14008             
14009             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14010             
14011             this.hiddenField.dom.value =
14012                 this.hiddenValue !== undefined ? this.hiddenValue :
14013                 this.value !== undefined ? this.value : '';
14014
14015             // prevent input submission
14016             this.el.dom.removeAttribute('name');
14017             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14018              
14019              
14020         }
14021         
14022 //        this.list = this.el.select('ul.dropdown-menu',true).first();
14023         
14024         this.choices = this.el.select('ul.roo-select2-choices', true).first();
14025         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14026         if(this.triggerList){
14027             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14028         }
14029          
14030         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14031         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14032         
14033         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14034         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14035         
14036         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14037         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14038         
14039         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14040         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14041         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14042         
14043         this.okBtn.hide();
14044         this.cancelBtn.hide();
14045         
14046         var _this = this;
14047         
14048         (function(){
14049             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14050             _this.list.setWidth(lw);
14051         }).defer(100);
14052         
14053         this.list.on('mouseover', this.onViewOver, this);
14054         this.list.on('mousemove', this.onViewMove, this);
14055         
14056         this.list.on('scroll', this.onViewScroll, this);
14057         
14058         if(!this.tpl){
14059             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14060                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14061         }
14062
14063         this.view = new Roo.View(this.list, this.tpl, {
14064             singleSelect:true,
14065             tickable:true,
14066             parent:this,
14067             store: this.store,
14068             selectedClass: this.selectedClass
14069         });
14070         
14071         //this.view.wrapEl.setDisplayed(false);
14072         this.view.on('click', this.onViewClick, this);
14073         
14074         
14075         
14076         this.store.on('beforeload', this.onBeforeLoad, this);
14077         this.store.on('load', this.onLoad, this);
14078         this.store.on('loadexception', this.onLoadException, this);
14079         
14080         if(this.editable){
14081             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14082                 "up" : function(e){
14083                     this.inKeyMode = true;
14084                     this.selectPrev();
14085                 },
14086
14087                 "down" : function(e){
14088                     this.inKeyMode = true;
14089                     this.selectNext();
14090                 },
14091
14092                 "enter" : function(e){
14093                     if(this.fireEvent("specialkey", this, e)){
14094                         this.onViewClick(false);
14095                     }
14096                     
14097                     return true;
14098                 },
14099
14100                 "esc" : function(e){
14101                     this.onTickableFooterButtonClick(e, false, false);
14102                 },
14103
14104                 "tab" : function(e){
14105                     this.fireEvent("specialkey", this, e);
14106                     
14107                     this.onTickableFooterButtonClick(e, false, false);
14108                     
14109                     return true;
14110                 },
14111
14112                 scope : this,
14113
14114                 doRelay : function(e, fn, key){
14115                     if(this.scope.isExpanded()){
14116                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14117                     }
14118                     return true;
14119                 },
14120
14121                 forceKeyDown: true
14122             });
14123         }
14124         
14125         this.queryDelay = Math.max(this.queryDelay || 10,
14126                 this.mode == 'local' ? 10 : 250);
14127         
14128         
14129         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14130         
14131         if(this.typeAhead){
14132             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14133         }
14134         
14135         if(this.editable !== false){
14136             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14137         }
14138         
14139         this.indicator = this.indicatorEl();
14140         
14141         if(this.indicator){
14142             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14143             this.indicator.hide();
14144         }
14145         
14146     },
14147
14148     onDestroy : function(){
14149         if(this.view){
14150             this.view.setStore(null);
14151             this.view.el.removeAllListeners();
14152             this.view.el.remove();
14153             this.view.purgeListeners();
14154         }
14155         if(this.list){
14156             this.list.dom.innerHTML  = '';
14157         }
14158         
14159         if(this.store){
14160             this.store.un('beforeload', this.onBeforeLoad, this);
14161             this.store.un('load', this.onLoad, this);
14162             this.store.un('loadexception', this.onLoadException, this);
14163         }
14164         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14165     },
14166
14167     // private
14168     fireKey : function(e){
14169         if(e.isNavKeyPress() && !this.list.isVisible()){
14170             this.fireEvent("specialkey", this, e);
14171         }
14172     },
14173
14174     // private
14175     onResize: function(w, h){
14176 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14177 //        
14178 //        if(typeof w != 'number'){
14179 //            // we do not handle it!?!?
14180 //            return;
14181 //        }
14182 //        var tw = this.trigger.getWidth();
14183 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14184 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14185 //        var x = w - tw;
14186 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14187 //            
14188 //        //this.trigger.setStyle('left', x+'px');
14189 //        
14190 //        if(this.list && this.listWidth === undefined){
14191 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14192 //            this.list.setWidth(lw);
14193 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14194 //        }
14195         
14196     
14197         
14198     },
14199
14200     /**
14201      * Allow or prevent the user from directly editing the field text.  If false is passed,
14202      * the user will only be able to select from the items defined in the dropdown list.  This method
14203      * is the runtime equivalent of setting the 'editable' config option at config time.
14204      * @param {Boolean} value True to allow the user to directly edit the field text
14205      */
14206     setEditable : function(value){
14207         if(value == this.editable){
14208             return;
14209         }
14210         this.editable = value;
14211         if(!value){
14212             this.inputEl().dom.setAttribute('readOnly', true);
14213             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14214             this.inputEl().addClass('x-combo-noedit');
14215         }else{
14216             this.inputEl().dom.setAttribute('readOnly', false);
14217             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14218             this.inputEl().removeClass('x-combo-noedit');
14219         }
14220     },
14221
14222     // private
14223     
14224     onBeforeLoad : function(combo,opts){
14225         if(!this.hasFocus){
14226             return;
14227         }
14228          if (!opts.add) {
14229             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14230          }
14231         this.restrictHeight();
14232         this.selectedIndex = -1;
14233     },
14234
14235     // private
14236     onLoad : function(){
14237         
14238         this.hasQuery = false;
14239         
14240         if(!this.hasFocus){
14241             return;
14242         }
14243         
14244         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14245             this.loading.hide();
14246         }
14247         
14248         if(this.store.getCount() > 0){
14249             
14250             this.expand();
14251             this.restrictHeight();
14252             if(this.lastQuery == this.allQuery){
14253                 if(this.editable && !this.tickable){
14254                     this.inputEl().dom.select();
14255                 }
14256                 
14257                 if(
14258                     !this.selectByValue(this.value, true) &&
14259                     this.autoFocus && 
14260                     (
14261                         !this.store.lastOptions ||
14262                         typeof(this.store.lastOptions.add) == 'undefined' || 
14263                         this.store.lastOptions.add != true
14264                     )
14265                 ){
14266                     this.select(0, true);
14267                 }
14268             }else{
14269                 if(this.autoFocus){
14270                     this.selectNext();
14271                 }
14272                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14273                     this.taTask.delay(this.typeAheadDelay);
14274                 }
14275             }
14276         }else{
14277             this.onEmptyResults();
14278         }
14279         
14280         //this.el.focus();
14281     },
14282     // private
14283     onLoadException : function()
14284     {
14285         this.hasQuery = false;
14286         
14287         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14288             this.loading.hide();
14289         }
14290         
14291         if(this.tickable && this.editable){
14292             return;
14293         }
14294         
14295         this.collapse();
14296         // only causes errors at present
14297         //Roo.log(this.store.reader.jsonData);
14298         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14299             // fixme
14300             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14301         //}
14302         
14303         
14304     },
14305     // private
14306     onTypeAhead : function(){
14307         if(this.store.getCount() > 0){
14308             var r = this.store.getAt(0);
14309             var newValue = r.data[this.displayField];
14310             var len = newValue.length;
14311             var selStart = this.getRawValue().length;
14312             
14313             if(selStart != len){
14314                 this.setRawValue(newValue);
14315                 this.selectText(selStart, newValue.length);
14316             }
14317         }
14318     },
14319
14320     // private
14321     onSelect : function(record, index){
14322         
14323         if(this.fireEvent('beforeselect', this, record, index) !== false){
14324         
14325             this.setFromData(index > -1 ? record.data : false);
14326             
14327             this.collapse();
14328             this.fireEvent('select', this, record, index);
14329         }
14330     },
14331
14332     /**
14333      * Returns the currently selected field value or empty string if no value is set.
14334      * @return {String} value The selected value
14335      */
14336     getValue : function()
14337     {
14338         if(Roo.isIOS && this.useNativeIOS){
14339             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14340         }
14341         
14342         if(this.multiple){
14343             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14344         }
14345         
14346         if(this.valueField){
14347             return typeof this.value != 'undefined' ? this.value : '';
14348         }else{
14349             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14350         }
14351     },
14352     
14353     getRawValue : function()
14354     {
14355         if(Roo.isIOS && this.useNativeIOS){
14356             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14357         }
14358         
14359         var v = this.inputEl().getValue();
14360         
14361         return v;
14362     },
14363
14364     /**
14365      * Clears any text/value currently set in the field
14366      */
14367     clearValue : function(){
14368         
14369         if(this.hiddenField){
14370             this.hiddenField.dom.value = '';
14371         }
14372         this.value = '';
14373         this.setRawValue('');
14374         this.lastSelectionText = '';
14375         this.lastData = false;
14376         
14377         var close = this.closeTriggerEl();
14378         
14379         if(close){
14380             close.hide();
14381         }
14382         
14383         this.validate();
14384         
14385     },
14386
14387     /**
14388      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14389      * will be displayed in the field.  If the value does not match the data value of an existing item,
14390      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14391      * Otherwise the field will be blank (although the value will still be set).
14392      * @param {String} value The value to match
14393      */
14394     setValue : function(v)
14395     {
14396         if(Roo.isIOS && this.useNativeIOS){
14397             this.setIOSValue(v);
14398             return;
14399         }
14400         
14401         if(this.multiple){
14402             this.syncValue();
14403             return;
14404         }
14405         
14406         var text = v;
14407         if(this.valueField){
14408             var r = this.findRecord(this.valueField, v);
14409             if(r){
14410                 text = r.data[this.displayField];
14411             }else if(this.valueNotFoundText !== undefined){
14412                 text = this.valueNotFoundText;
14413             }
14414         }
14415         this.lastSelectionText = text;
14416         if(this.hiddenField){
14417             this.hiddenField.dom.value = v;
14418         }
14419         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14420         this.value = v;
14421         
14422         var close = this.closeTriggerEl();
14423         
14424         if(close){
14425             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14426         }
14427         
14428         this.validate();
14429     },
14430     /**
14431      * @property {Object} the last set data for the element
14432      */
14433     
14434     lastData : false,
14435     /**
14436      * Sets the value of the field based on a object which is related to the record format for the store.
14437      * @param {Object} value the value to set as. or false on reset?
14438      */
14439     setFromData : function(o){
14440         
14441         if(this.multiple){
14442             this.addItem(o);
14443             return;
14444         }
14445             
14446         var dv = ''; // display value
14447         var vv = ''; // value value..
14448         this.lastData = o;
14449         if (this.displayField) {
14450             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14451         } else {
14452             // this is an error condition!!!
14453             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14454         }
14455         
14456         if(this.valueField){
14457             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14458         }
14459         
14460         var close = this.closeTriggerEl();
14461         
14462         if(close){
14463             if(dv.length || vv * 1 > 0){
14464                 close.show() ;
14465                 this.blockFocus=true;
14466             } else {
14467                 close.hide();
14468             }             
14469         }
14470         
14471         if(this.hiddenField){
14472             this.hiddenField.dom.value = vv;
14473             
14474             this.lastSelectionText = dv;
14475             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14476             this.value = vv;
14477             return;
14478         }
14479         // no hidden field.. - we store the value in 'value', but still display
14480         // display field!!!!
14481         this.lastSelectionText = dv;
14482         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14483         this.value = vv;
14484         
14485         
14486         
14487     },
14488     // private
14489     reset : function(){
14490         // overridden so that last data is reset..
14491         
14492         if(this.multiple){
14493             this.clearItem();
14494             return;
14495         }
14496         
14497         this.setValue(this.originalValue);
14498         //this.clearInvalid();
14499         this.lastData = false;
14500         if (this.view) {
14501             this.view.clearSelections();
14502         }
14503         
14504         this.validate();
14505     },
14506     // private
14507     findRecord : function(prop, value){
14508         var record;
14509         if(this.store.getCount() > 0){
14510             this.store.each(function(r){
14511                 if(r.data[prop] == value){
14512                     record = r;
14513                     return false;
14514                 }
14515                 return true;
14516             });
14517         }
14518         return record;
14519     },
14520     
14521     getName: function()
14522     {
14523         // returns hidden if it's set..
14524         if (!this.rendered) {return ''};
14525         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14526         
14527     },
14528     // private
14529     onViewMove : function(e, t){
14530         this.inKeyMode = false;
14531     },
14532
14533     // private
14534     onViewOver : function(e, t){
14535         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14536             return;
14537         }
14538         var item = this.view.findItemFromChild(t);
14539         
14540         if(item){
14541             var index = this.view.indexOf(item);
14542             this.select(index, false);
14543         }
14544     },
14545
14546     // private
14547     onViewClick : function(view, doFocus, el, e)
14548     {
14549         var index = this.view.getSelectedIndexes()[0];
14550         
14551         var r = this.store.getAt(index);
14552         
14553         if(this.tickable){
14554             
14555             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14556                 return;
14557             }
14558             
14559             var rm = false;
14560             var _this = this;
14561             
14562             Roo.each(this.tickItems, function(v,k){
14563                 
14564                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14565                     Roo.log(v);
14566                     _this.tickItems.splice(k, 1);
14567                     
14568                     if(typeof(e) == 'undefined' && view == false){
14569                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14570                     }
14571                     
14572                     rm = true;
14573                     return;
14574                 }
14575             });
14576             
14577             if(rm){
14578                 return;
14579             }
14580             
14581             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14582                 this.tickItems.push(r.data);
14583             }
14584             
14585             if(typeof(e) == 'undefined' && view == false){
14586                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14587             }
14588                     
14589             return;
14590         }
14591         
14592         if(r){
14593             this.onSelect(r, index);
14594         }
14595         if(doFocus !== false && !this.blockFocus){
14596             this.inputEl().focus();
14597         }
14598     },
14599
14600     // private
14601     restrictHeight : function(){
14602         //this.innerList.dom.style.height = '';
14603         //var inner = this.innerList.dom;
14604         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14605         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14606         //this.list.beginUpdate();
14607         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14608         this.list.alignTo(this.inputEl(), this.listAlign);
14609         this.list.alignTo(this.inputEl(), this.listAlign);
14610         //this.list.endUpdate();
14611     },
14612
14613     // private
14614     onEmptyResults : function(){
14615         
14616         if(this.tickable && this.editable){
14617             this.hasFocus = false;
14618             this.restrictHeight();
14619             return;
14620         }
14621         
14622         this.collapse();
14623     },
14624
14625     /**
14626      * Returns true if the dropdown list is expanded, else false.
14627      */
14628     isExpanded : function(){
14629         return this.list.isVisible();
14630     },
14631
14632     /**
14633      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14634      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14635      * @param {String} value The data value of the item to select
14636      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14637      * selected item if it is not currently in view (defaults to true)
14638      * @return {Boolean} True if the value matched an item in the list, else false
14639      */
14640     selectByValue : function(v, scrollIntoView){
14641         if(v !== undefined && v !== null){
14642             var r = this.findRecord(this.valueField || this.displayField, v);
14643             if(r){
14644                 this.select(this.store.indexOf(r), scrollIntoView);
14645                 return true;
14646             }
14647         }
14648         return false;
14649     },
14650
14651     /**
14652      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14653      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14654      * @param {Number} index The zero-based index of the list item to select
14655      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14656      * selected item if it is not currently in view (defaults to true)
14657      */
14658     select : function(index, scrollIntoView){
14659         this.selectedIndex = index;
14660         this.view.select(index);
14661         if(scrollIntoView !== false){
14662             var el = this.view.getNode(index);
14663             /*
14664              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14665              */
14666             if(el){
14667                 this.list.scrollChildIntoView(el, false);
14668             }
14669         }
14670     },
14671
14672     // private
14673     selectNext : function(){
14674         var ct = this.store.getCount();
14675         if(ct > 0){
14676             if(this.selectedIndex == -1){
14677                 this.select(0);
14678             }else if(this.selectedIndex < ct-1){
14679                 this.select(this.selectedIndex+1);
14680             }
14681         }
14682     },
14683
14684     // private
14685     selectPrev : function(){
14686         var ct = this.store.getCount();
14687         if(ct > 0){
14688             if(this.selectedIndex == -1){
14689                 this.select(0);
14690             }else if(this.selectedIndex != 0){
14691                 this.select(this.selectedIndex-1);
14692             }
14693         }
14694     },
14695
14696     // private
14697     onKeyUp : function(e){
14698         if(this.editable !== false && !e.isSpecialKey()){
14699             this.lastKey = e.getKey();
14700             this.dqTask.delay(this.queryDelay);
14701         }
14702     },
14703
14704     // private
14705     validateBlur : function(){
14706         return !this.list || !this.list.isVisible();   
14707     },
14708
14709     // private
14710     initQuery : function(){
14711         
14712         var v = this.getRawValue();
14713         
14714         if(this.tickable && this.editable){
14715             v = this.tickableInputEl().getValue();
14716         }
14717         
14718         this.doQuery(v);
14719     },
14720
14721     // private
14722     doForce : function(){
14723         if(this.inputEl().dom.value.length > 0){
14724             this.inputEl().dom.value =
14725                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14726              
14727         }
14728     },
14729
14730     /**
14731      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14732      * query allowing the query action to be canceled if needed.
14733      * @param {String} query The SQL query to execute
14734      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14735      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14736      * saved in the current store (defaults to false)
14737      */
14738     doQuery : function(q, forceAll){
14739         
14740         if(q === undefined || q === null){
14741             q = '';
14742         }
14743         var qe = {
14744             query: q,
14745             forceAll: forceAll,
14746             combo: this,
14747             cancel:false
14748         };
14749         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14750             return false;
14751         }
14752         q = qe.query;
14753         
14754         forceAll = qe.forceAll;
14755         if(forceAll === true || (q.length >= this.minChars)){
14756             
14757             this.hasQuery = true;
14758             
14759             if(this.lastQuery != q || this.alwaysQuery){
14760                 this.lastQuery = q;
14761                 if(this.mode == 'local'){
14762                     this.selectedIndex = -1;
14763                     if(forceAll){
14764                         this.store.clearFilter();
14765                     }else{
14766                         
14767                         if(this.specialFilter){
14768                             this.fireEvent('specialfilter', this);
14769                             this.onLoad();
14770                             return;
14771                         }
14772                         
14773                         this.store.filter(this.displayField, q);
14774                     }
14775                     
14776                     this.store.fireEvent("datachanged", this.store);
14777                     
14778                     this.onLoad();
14779                     
14780                     
14781                 }else{
14782                     
14783                     this.store.baseParams[this.queryParam] = q;
14784                     
14785                     var options = {params : this.getParams(q)};
14786                     
14787                     if(this.loadNext){
14788                         options.add = true;
14789                         options.params.start = this.page * this.pageSize;
14790                     }
14791                     
14792                     this.store.load(options);
14793                     
14794                     /*
14795                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14796                      *  we should expand the list on onLoad
14797                      *  so command out it
14798                      */
14799 //                    this.expand();
14800                 }
14801             }else{
14802                 this.selectedIndex = -1;
14803                 this.onLoad();   
14804             }
14805         }
14806         
14807         this.loadNext = false;
14808     },
14809     
14810     // private
14811     getParams : function(q){
14812         var p = {};
14813         //p[this.queryParam] = q;
14814         
14815         if(this.pageSize){
14816             p.start = 0;
14817             p.limit = this.pageSize;
14818         }
14819         return p;
14820     },
14821
14822     /**
14823      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14824      */
14825     collapse : function(){
14826         if(!this.isExpanded()){
14827             return;
14828         }
14829         
14830         this.list.hide();
14831         
14832         this.hasFocus = false;
14833         
14834         if(this.tickable){
14835             this.okBtn.hide();
14836             this.cancelBtn.hide();
14837             this.trigger.show();
14838             
14839             if(this.editable){
14840                 this.tickableInputEl().dom.value = '';
14841                 this.tickableInputEl().blur();
14842             }
14843             
14844         }
14845         
14846         Roo.get(document).un('mousedown', this.collapseIf, this);
14847         Roo.get(document).un('mousewheel', this.collapseIf, this);
14848         if (!this.editable) {
14849             Roo.get(document).un('keydown', this.listKeyPress, this);
14850         }
14851         this.fireEvent('collapse', this);
14852         
14853         this.validate();
14854     },
14855
14856     // private
14857     collapseIf : function(e){
14858         var in_combo  = e.within(this.el);
14859         var in_list =  e.within(this.list);
14860         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14861         
14862         if (in_combo || in_list || is_list) {
14863             //e.stopPropagation();
14864             return;
14865         }
14866         
14867         if(this.tickable){
14868             this.onTickableFooterButtonClick(e, false, false);
14869         }
14870
14871         this.collapse();
14872         
14873     },
14874
14875     /**
14876      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14877      */
14878     expand : function(){
14879        
14880         if(this.isExpanded() || !this.hasFocus){
14881             return;
14882         }
14883         
14884         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14885         this.list.setWidth(lw);
14886         
14887         Roo.log('expand');
14888         
14889         this.list.show();
14890         
14891         this.restrictHeight();
14892         
14893         if(this.tickable){
14894             
14895             this.tickItems = Roo.apply([], this.item);
14896             
14897             this.okBtn.show();
14898             this.cancelBtn.show();
14899             this.trigger.hide();
14900             
14901             if(this.editable){
14902                 this.tickableInputEl().focus();
14903             }
14904             
14905         }
14906         
14907         Roo.get(document).on('mousedown', this.collapseIf, this);
14908         Roo.get(document).on('mousewheel', this.collapseIf, this);
14909         if (!this.editable) {
14910             Roo.get(document).on('keydown', this.listKeyPress, this);
14911         }
14912         
14913         this.fireEvent('expand', this);
14914     },
14915
14916     // private
14917     // Implements the default empty TriggerField.onTriggerClick function
14918     onTriggerClick : function(e)
14919     {
14920         Roo.log('trigger click');
14921         
14922         if(this.disabled || !this.triggerList){
14923             return;
14924         }
14925         
14926         this.page = 0;
14927         this.loadNext = false;
14928         
14929         if(this.isExpanded()){
14930             this.collapse();
14931             if (!this.blockFocus) {
14932                 this.inputEl().focus();
14933             }
14934             
14935         }else {
14936             this.hasFocus = true;
14937             if(this.triggerAction == 'all') {
14938                 this.doQuery(this.allQuery, true);
14939             } else {
14940                 this.doQuery(this.getRawValue());
14941             }
14942             if (!this.blockFocus) {
14943                 this.inputEl().focus();
14944             }
14945         }
14946     },
14947     
14948     onTickableTriggerClick : function(e)
14949     {
14950         if(this.disabled){
14951             return;
14952         }
14953         
14954         this.page = 0;
14955         this.loadNext = false;
14956         this.hasFocus = true;
14957         
14958         if(this.triggerAction == 'all') {
14959             this.doQuery(this.allQuery, true);
14960         } else {
14961             this.doQuery(this.getRawValue());
14962         }
14963     },
14964     
14965     onSearchFieldClick : function(e)
14966     {
14967         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14968             this.onTickableFooterButtonClick(e, false, false);
14969             return;
14970         }
14971         
14972         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14973             return;
14974         }
14975         
14976         this.page = 0;
14977         this.loadNext = false;
14978         this.hasFocus = true;
14979         
14980         if(this.triggerAction == 'all') {
14981             this.doQuery(this.allQuery, true);
14982         } else {
14983             this.doQuery(this.getRawValue());
14984         }
14985     },
14986     
14987     listKeyPress : function(e)
14988     {
14989         //Roo.log('listkeypress');
14990         // scroll to first matching element based on key pres..
14991         if (e.isSpecialKey()) {
14992             return false;
14993         }
14994         var k = String.fromCharCode(e.getKey()).toUpperCase();
14995         //Roo.log(k);
14996         var match  = false;
14997         var csel = this.view.getSelectedNodes();
14998         var cselitem = false;
14999         if (csel.length) {
15000             var ix = this.view.indexOf(csel[0]);
15001             cselitem  = this.store.getAt(ix);
15002             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15003                 cselitem = false;
15004             }
15005             
15006         }
15007         
15008         this.store.each(function(v) { 
15009             if (cselitem) {
15010                 // start at existing selection.
15011                 if (cselitem.id == v.id) {
15012                     cselitem = false;
15013                 }
15014                 return true;
15015             }
15016                 
15017             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15018                 match = this.store.indexOf(v);
15019                 return false;
15020             }
15021             return true;
15022         }, this);
15023         
15024         if (match === false) {
15025             return true; // no more action?
15026         }
15027         // scroll to?
15028         this.view.select(match);
15029         var sn = Roo.get(this.view.getSelectedNodes()[0]);
15030         sn.scrollIntoView(sn.dom.parentNode, false);
15031     },
15032     
15033     onViewScroll : function(e, t){
15034         
15035         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){
15036             return;
15037         }
15038         
15039         this.hasQuery = true;
15040         
15041         this.loading = this.list.select('.loading', true).first();
15042         
15043         if(this.loading === null){
15044             this.list.createChild({
15045                 tag: 'div',
15046                 cls: 'loading roo-select2-more-results roo-select2-active',
15047                 html: 'Loading more results...'
15048             });
15049             
15050             this.loading = this.list.select('.loading', true).first();
15051             
15052             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15053             
15054             this.loading.hide();
15055         }
15056         
15057         this.loading.show();
15058         
15059         var _combo = this;
15060         
15061         this.page++;
15062         this.loadNext = true;
15063         
15064         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15065         
15066         return;
15067     },
15068     
15069     addItem : function(o)
15070     {   
15071         var dv = ''; // display value
15072         
15073         if (this.displayField) {
15074             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15075         } else {
15076             // this is an error condition!!!
15077             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15078         }
15079         
15080         if(!dv.length){
15081             return;
15082         }
15083         
15084         var choice = this.choices.createChild({
15085             tag: 'li',
15086             cls: 'roo-select2-search-choice',
15087             cn: [
15088                 {
15089                     tag: 'div',
15090                     html: dv
15091                 },
15092                 {
15093                     tag: 'a',
15094                     href: '#',
15095                     cls: 'roo-select2-search-choice-close fa fa-times',
15096                     tabindex: '-1'
15097                 }
15098             ]
15099             
15100         }, this.searchField);
15101         
15102         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15103         
15104         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15105         
15106         this.item.push(o);
15107         
15108         this.lastData = o;
15109         
15110         this.syncValue();
15111         
15112         this.inputEl().dom.value = '';
15113         
15114         this.validate();
15115     },
15116     
15117     onRemoveItem : function(e, _self, o)
15118     {
15119         e.preventDefault();
15120         
15121         this.lastItem = Roo.apply([], this.item);
15122         
15123         var index = this.item.indexOf(o.data) * 1;
15124         
15125         if( index < 0){
15126             Roo.log('not this item?!');
15127             return;
15128         }
15129         
15130         this.item.splice(index, 1);
15131         o.item.remove();
15132         
15133         this.syncValue();
15134         
15135         this.fireEvent('remove', this, e);
15136         
15137         this.validate();
15138         
15139     },
15140     
15141     syncValue : function()
15142     {
15143         if(!this.item.length){
15144             this.clearValue();
15145             return;
15146         }
15147             
15148         var value = [];
15149         var _this = this;
15150         Roo.each(this.item, function(i){
15151             if(_this.valueField){
15152                 value.push(i[_this.valueField]);
15153                 return;
15154             }
15155
15156             value.push(i);
15157         });
15158
15159         this.value = value.join(',');
15160
15161         if(this.hiddenField){
15162             this.hiddenField.dom.value = this.value;
15163         }
15164         
15165         this.store.fireEvent("datachanged", this.store);
15166         
15167         this.validate();
15168     },
15169     
15170     clearItem : function()
15171     {
15172         if(!this.multiple){
15173             return;
15174         }
15175         
15176         this.item = [];
15177         
15178         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15179            c.remove();
15180         });
15181         
15182         this.syncValue();
15183         
15184         this.validate();
15185         
15186         if(this.tickable && !Roo.isTouch){
15187             this.view.refresh();
15188         }
15189     },
15190     
15191     inputEl: function ()
15192     {
15193         if(Roo.isIOS && this.useNativeIOS){
15194             return this.el.select('select.roo-ios-select', true).first();
15195         }
15196         
15197         if(Roo.isTouch && this.mobileTouchView){
15198             return this.el.select('input.form-control',true).first();
15199         }
15200         
15201         if(this.tickable){
15202             return this.searchField;
15203         }
15204         
15205         return this.el.select('input.form-control',true).first();
15206     },
15207     
15208     onTickableFooterButtonClick : function(e, btn, el)
15209     {
15210         e.preventDefault();
15211         
15212         this.lastItem = Roo.apply([], this.item);
15213         
15214         if(btn && btn.name == 'cancel'){
15215             this.tickItems = Roo.apply([], this.item);
15216             this.collapse();
15217             return;
15218         }
15219         
15220         this.clearItem();
15221         
15222         var _this = this;
15223         
15224         Roo.each(this.tickItems, function(o){
15225             _this.addItem(o);
15226         });
15227         
15228         this.collapse();
15229         
15230     },
15231     
15232     validate : function()
15233     {
15234         if(this.getVisibilityEl().hasClass('hidden')){
15235             return true;
15236         }
15237         
15238         var v = this.getRawValue();
15239         
15240         if(this.multiple){
15241             v = this.getValue();
15242         }
15243         
15244         if(this.disabled || this.allowBlank || v.length){
15245             this.markValid();
15246             return true;
15247         }
15248         
15249         this.markInvalid();
15250         return false;
15251     },
15252     
15253     tickableInputEl : function()
15254     {
15255         if(!this.tickable || !this.editable){
15256             return this.inputEl();
15257         }
15258         
15259         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15260     },
15261     
15262     
15263     getAutoCreateTouchView : function()
15264     {
15265         var id = Roo.id();
15266         
15267         var cfg = {
15268             cls: 'form-group' //input-group
15269         };
15270         
15271         var input =  {
15272             tag: 'input',
15273             id : id,
15274             type : this.inputType,
15275             cls : 'form-control x-combo-noedit',
15276             autocomplete: 'new-password',
15277             placeholder : this.placeholder || '',
15278             readonly : true
15279         };
15280         
15281         if (this.name) {
15282             input.name = this.name;
15283         }
15284         
15285         if (this.size) {
15286             input.cls += ' input-' + this.size;
15287         }
15288         
15289         if (this.disabled) {
15290             input.disabled = true;
15291         }
15292         
15293         var inputblock = {
15294             cls : '',
15295             cn : [
15296                 input
15297             ]
15298         };
15299         
15300         if(this.before){
15301             inputblock.cls += ' input-group';
15302             
15303             inputblock.cn.unshift({
15304                 tag :'span',
15305                 cls : 'input-group-addon input-group-prepend input-group-text',
15306                 html : this.before
15307             });
15308         }
15309         
15310         if(this.removable && !this.multiple){
15311             inputblock.cls += ' roo-removable';
15312             
15313             inputblock.cn.push({
15314                 tag: 'button',
15315                 html : 'x',
15316                 cls : 'roo-combo-removable-btn close'
15317             });
15318         }
15319
15320         if(this.hasFeedback && !this.allowBlank){
15321             
15322             inputblock.cls += ' has-feedback';
15323             
15324             inputblock.cn.push({
15325                 tag: 'span',
15326                 cls: 'glyphicon form-control-feedback'
15327             });
15328             
15329         }
15330         
15331         if (this.after) {
15332             
15333             inputblock.cls += (this.before) ? '' : ' input-group';
15334             
15335             inputblock.cn.push({
15336                 tag :'span',
15337                 cls : 'input-group-addon input-group-append input-group-text',
15338                 html : this.after
15339             });
15340         }
15341
15342         
15343         var ibwrap = inputblock;
15344         
15345         if(this.multiple){
15346             ibwrap = {
15347                 tag: 'ul',
15348                 cls: 'roo-select2-choices',
15349                 cn:[
15350                     {
15351                         tag: 'li',
15352                         cls: 'roo-select2-search-field',
15353                         cn: [
15354
15355                             inputblock
15356                         ]
15357                     }
15358                 ]
15359             };
15360         
15361             
15362         }
15363         
15364         var combobox = {
15365             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15366             cn: [
15367                 {
15368                     tag: 'input',
15369                     type : 'hidden',
15370                     cls: 'form-hidden-field'
15371                 },
15372                 ibwrap
15373             ]
15374         };
15375         
15376         if(!this.multiple && this.showToggleBtn){
15377             
15378             var caret = {
15379                 cls: 'caret'
15380             };
15381             
15382             if (this.caret != false) {
15383                 caret = {
15384                      tag: 'i',
15385                      cls: 'fa fa-' + this.caret
15386                 };
15387                 
15388             }
15389             
15390             combobox.cn.push({
15391                 tag :'span',
15392                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15393                 cn : [
15394                     Roo.bootstrap.version == 3 ? caret : '',
15395                     {
15396                         tag: 'span',
15397                         cls: 'combobox-clear',
15398                         cn  : [
15399                             {
15400                                 tag : 'i',
15401                                 cls: 'icon-remove'
15402                             }
15403                         ]
15404                     }
15405                 ]
15406
15407             })
15408         }
15409         
15410         if(this.multiple){
15411             combobox.cls += ' roo-select2-container-multi';
15412         }
15413         
15414         var align = this.labelAlign || this.parentLabelAlign();
15415         
15416         if (align ==='left' && this.fieldLabel.length) {
15417
15418             cfg.cn = [
15419                 {
15420                    tag : 'i',
15421                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15422                    tooltip : 'This field is required'
15423                 },
15424                 {
15425                     tag: 'label',
15426                     cls : 'control-label col-form-label',
15427                     html : this.fieldLabel
15428
15429                 },
15430                 {
15431                     cls : '', 
15432                     cn: [
15433                         combobox
15434                     ]
15435                 }
15436             ];
15437             
15438             var labelCfg = cfg.cn[1];
15439             var contentCfg = cfg.cn[2];
15440             
15441
15442             if(this.indicatorpos == 'right'){
15443                 cfg.cn = [
15444                     {
15445                         tag: 'label',
15446                         'for' :  id,
15447                         cls : 'control-label col-form-label',
15448                         cn : [
15449                             {
15450                                 tag : 'span',
15451                                 html : this.fieldLabel
15452                             },
15453                             {
15454                                 tag : 'i',
15455                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15456                                 tooltip : 'This field is required'
15457                             }
15458                         ]
15459                     },
15460                     {
15461                         cls : "",
15462                         cn: [
15463                             combobox
15464                         ]
15465                     }
15466
15467                 ];
15468                 
15469                 labelCfg = cfg.cn[0];
15470                 contentCfg = cfg.cn[1];
15471             }
15472             
15473            
15474             
15475             if(this.labelWidth > 12){
15476                 labelCfg.style = "width: " + this.labelWidth + 'px';
15477             }
15478             
15479             if(this.labelWidth < 13 && this.labelmd == 0){
15480                 this.labelmd = this.labelWidth;
15481             }
15482             
15483             if(this.labellg > 0){
15484                 labelCfg.cls += ' col-lg-' + this.labellg;
15485                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15486             }
15487             
15488             if(this.labelmd > 0){
15489                 labelCfg.cls += ' col-md-' + this.labelmd;
15490                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15491             }
15492             
15493             if(this.labelsm > 0){
15494                 labelCfg.cls += ' col-sm-' + this.labelsm;
15495                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15496             }
15497             
15498             if(this.labelxs > 0){
15499                 labelCfg.cls += ' col-xs-' + this.labelxs;
15500                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15501             }
15502                 
15503                 
15504         } else if ( this.fieldLabel.length) {
15505             cfg.cn = [
15506                 {
15507                    tag : 'i',
15508                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15509                    tooltip : 'This field is required'
15510                 },
15511                 {
15512                     tag: 'label',
15513                     cls : 'control-label',
15514                     html : this.fieldLabel
15515
15516                 },
15517                 {
15518                     cls : '', 
15519                     cn: [
15520                         combobox
15521                     ]
15522                 }
15523             ];
15524             
15525             if(this.indicatorpos == 'right'){
15526                 cfg.cn = [
15527                     {
15528                         tag: 'label',
15529                         cls : 'control-label',
15530                         html : this.fieldLabel,
15531                         cn : [
15532                             {
15533                                tag : 'i',
15534                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15535                                tooltip : 'This field is required'
15536                             }
15537                         ]
15538                     },
15539                     {
15540                         cls : '', 
15541                         cn: [
15542                             combobox
15543                         ]
15544                     }
15545                 ];
15546             }
15547         } else {
15548             cfg.cn = combobox;    
15549         }
15550         
15551         
15552         var settings = this;
15553         
15554         ['xs','sm','md','lg'].map(function(size){
15555             if (settings[size]) {
15556                 cfg.cls += ' col-' + size + '-' + settings[size];
15557             }
15558         });
15559         
15560         return cfg;
15561     },
15562     
15563     initTouchView : function()
15564     {
15565         this.renderTouchView();
15566         
15567         this.touchViewEl.on('scroll', function(){
15568             this.el.dom.scrollTop = 0;
15569         }, this);
15570         
15571         this.originalValue = this.getValue();
15572         
15573         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15574         
15575         this.inputEl().on("click", this.showTouchView, this);
15576         if (this.triggerEl) {
15577             this.triggerEl.on("click", this.showTouchView, this);
15578         }
15579         
15580         
15581         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15582         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15583         
15584         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15585         
15586         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15587         this.store.on('load', this.onTouchViewLoad, this);
15588         this.store.on('loadexception', this.onTouchViewLoadException, this);
15589         
15590         if(this.hiddenName){
15591             
15592             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15593             
15594             this.hiddenField.dom.value =
15595                 this.hiddenValue !== undefined ? this.hiddenValue :
15596                 this.value !== undefined ? this.value : '';
15597         
15598             this.el.dom.removeAttribute('name');
15599             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15600         }
15601         
15602         if(this.multiple){
15603             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15604             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15605         }
15606         
15607         if(this.removable && !this.multiple){
15608             var close = this.closeTriggerEl();
15609             if(close){
15610                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15611                 close.on('click', this.removeBtnClick, this, close);
15612             }
15613         }
15614         /*
15615          * fix the bug in Safari iOS8
15616          */
15617         this.inputEl().on("focus", function(e){
15618             document.activeElement.blur();
15619         }, this);
15620         
15621         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15622         
15623         return;
15624         
15625         
15626     },
15627     
15628     renderTouchView : function()
15629     {
15630         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15631         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15632         
15633         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15634         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15635         
15636         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15637         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15638         this.touchViewBodyEl.setStyle('overflow', 'auto');
15639         
15640         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15641         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15642         
15643         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15644         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15645         
15646     },
15647     
15648     showTouchView : function()
15649     {
15650         if(this.disabled){
15651             return;
15652         }
15653         
15654         this.touchViewHeaderEl.hide();
15655
15656         if(this.modalTitle.length){
15657             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15658             this.touchViewHeaderEl.show();
15659         }
15660
15661         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15662         this.touchViewEl.show();
15663
15664         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15665         
15666         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15667         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15668
15669         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15670
15671         if(this.modalTitle.length){
15672             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15673         }
15674         
15675         this.touchViewBodyEl.setHeight(bodyHeight);
15676
15677         if(this.animate){
15678             var _this = this;
15679             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15680         }else{
15681             this.touchViewEl.addClass('in');
15682         }
15683         
15684         if(this._touchViewMask){
15685             Roo.get(document.body).addClass("x-body-masked");
15686             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15687             this._touchViewMask.setStyle('z-index', 10000);
15688             this._touchViewMask.addClass('show');
15689         }
15690         
15691         this.doTouchViewQuery();
15692         
15693     },
15694     
15695     hideTouchView : function()
15696     {
15697         this.touchViewEl.removeClass('in');
15698
15699         if(this.animate){
15700             var _this = this;
15701             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15702         }else{
15703             this.touchViewEl.setStyle('display', 'none');
15704         }
15705         
15706         if(this._touchViewMask){
15707             this._touchViewMask.removeClass('show');
15708             Roo.get(document.body).removeClass("x-body-masked");
15709         }
15710     },
15711     
15712     setTouchViewValue : function()
15713     {
15714         if(this.multiple){
15715             this.clearItem();
15716         
15717             var _this = this;
15718
15719             Roo.each(this.tickItems, function(o){
15720                 this.addItem(o);
15721             }, this);
15722         }
15723         
15724         this.hideTouchView();
15725     },
15726     
15727     doTouchViewQuery : function()
15728     {
15729         var qe = {
15730             query: '',
15731             forceAll: true,
15732             combo: this,
15733             cancel:false
15734         };
15735         
15736         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15737             return false;
15738         }
15739         
15740         if(!this.alwaysQuery || this.mode == 'local'){
15741             this.onTouchViewLoad();
15742             return;
15743         }
15744         
15745         this.store.load();
15746     },
15747     
15748     onTouchViewBeforeLoad : function(combo,opts)
15749     {
15750         return;
15751     },
15752
15753     // private
15754     onTouchViewLoad : function()
15755     {
15756         if(this.store.getCount() < 1){
15757             this.onTouchViewEmptyResults();
15758             return;
15759         }
15760         
15761         this.clearTouchView();
15762         
15763         var rawValue = this.getRawValue();
15764         
15765         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15766         
15767         this.tickItems = [];
15768         
15769         this.store.data.each(function(d, rowIndex){
15770             var row = this.touchViewListGroup.createChild(template);
15771             
15772             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15773                 row.addClass(d.data.cls);
15774             }
15775             
15776             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15777                 var cfg = {
15778                     data : d.data,
15779                     html : d.data[this.displayField]
15780                 };
15781                 
15782                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15783                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15784                 }
15785             }
15786             row.removeClass('selected');
15787             if(!this.multiple && this.valueField &&
15788                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15789             {
15790                 // radio buttons..
15791                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15792                 row.addClass('selected');
15793             }
15794             
15795             if(this.multiple && this.valueField &&
15796                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15797             {
15798                 
15799                 // checkboxes...
15800                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15801                 this.tickItems.push(d.data);
15802             }
15803             
15804             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15805             
15806         }, this);
15807         
15808         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15809         
15810         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15811
15812         if(this.modalTitle.length){
15813             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15814         }
15815
15816         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15817         
15818         if(this.mobile_restrict_height && listHeight < bodyHeight){
15819             this.touchViewBodyEl.setHeight(listHeight);
15820         }
15821         
15822         var _this = this;
15823         
15824         if(firstChecked && listHeight > bodyHeight){
15825             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15826         }
15827         
15828     },
15829     
15830     onTouchViewLoadException : function()
15831     {
15832         this.hideTouchView();
15833     },
15834     
15835     onTouchViewEmptyResults : function()
15836     {
15837         this.clearTouchView();
15838         
15839         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15840         
15841         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15842         
15843     },
15844     
15845     clearTouchView : function()
15846     {
15847         this.touchViewListGroup.dom.innerHTML = '';
15848     },
15849     
15850     onTouchViewClick : function(e, el, o)
15851     {
15852         e.preventDefault();
15853         
15854         var row = o.row;
15855         var rowIndex = o.rowIndex;
15856         
15857         var r = this.store.getAt(rowIndex);
15858         
15859         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15860             
15861             if(!this.multiple){
15862                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15863                     c.dom.removeAttribute('checked');
15864                 }, this);
15865
15866                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15867
15868                 this.setFromData(r.data);
15869
15870                 var close = this.closeTriggerEl();
15871
15872                 if(close){
15873                     close.show();
15874                 }
15875
15876                 this.hideTouchView();
15877
15878                 this.fireEvent('select', this, r, rowIndex);
15879
15880                 return;
15881             }
15882
15883             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15884                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15885                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15886                 return;
15887             }
15888
15889             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15890             this.addItem(r.data);
15891             this.tickItems.push(r.data);
15892         }
15893     },
15894     
15895     getAutoCreateNativeIOS : function()
15896     {
15897         var cfg = {
15898             cls: 'form-group' //input-group,
15899         };
15900         
15901         var combobox =  {
15902             tag: 'select',
15903             cls : 'roo-ios-select'
15904         };
15905         
15906         if (this.name) {
15907             combobox.name = this.name;
15908         }
15909         
15910         if (this.disabled) {
15911             combobox.disabled = true;
15912         }
15913         
15914         var settings = this;
15915         
15916         ['xs','sm','md','lg'].map(function(size){
15917             if (settings[size]) {
15918                 cfg.cls += ' col-' + size + '-' + settings[size];
15919             }
15920         });
15921         
15922         cfg.cn = combobox;
15923         
15924         return cfg;
15925         
15926     },
15927     
15928     initIOSView : function()
15929     {
15930         this.store.on('load', this.onIOSViewLoad, this);
15931         
15932         return;
15933     },
15934     
15935     onIOSViewLoad : function()
15936     {
15937         if(this.store.getCount() < 1){
15938             return;
15939         }
15940         
15941         this.clearIOSView();
15942         
15943         if(this.allowBlank) {
15944             
15945             var default_text = '-- SELECT --';
15946             
15947             if(this.placeholder.length){
15948                 default_text = this.placeholder;
15949             }
15950             
15951             if(this.emptyTitle.length){
15952                 default_text += ' - ' + this.emptyTitle + ' -';
15953             }
15954             
15955             var opt = this.inputEl().createChild({
15956                 tag: 'option',
15957                 value : 0,
15958                 html : default_text
15959             });
15960             
15961             var o = {};
15962             o[this.valueField] = 0;
15963             o[this.displayField] = default_text;
15964             
15965             this.ios_options.push({
15966                 data : o,
15967                 el : opt
15968             });
15969             
15970         }
15971         
15972         this.store.data.each(function(d, rowIndex){
15973             
15974             var html = '';
15975             
15976             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15977                 html = d.data[this.displayField];
15978             }
15979             
15980             var value = '';
15981             
15982             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15983                 value = d.data[this.valueField];
15984             }
15985             
15986             var option = {
15987                 tag: 'option',
15988                 value : value,
15989                 html : html
15990             };
15991             
15992             if(this.value == d.data[this.valueField]){
15993                 option['selected'] = true;
15994             }
15995             
15996             var opt = this.inputEl().createChild(option);
15997             
15998             this.ios_options.push({
15999                 data : d.data,
16000                 el : opt
16001             });
16002             
16003         }, this);
16004         
16005         this.inputEl().on('change', function(){
16006            this.fireEvent('select', this);
16007         }, this);
16008         
16009     },
16010     
16011     clearIOSView: function()
16012     {
16013         this.inputEl().dom.innerHTML = '';
16014         
16015         this.ios_options = [];
16016     },
16017     
16018     setIOSValue: function(v)
16019     {
16020         this.value = v;
16021         
16022         if(!this.ios_options){
16023             return;
16024         }
16025         
16026         Roo.each(this.ios_options, function(opts){
16027            
16028            opts.el.dom.removeAttribute('selected');
16029            
16030            if(opts.data[this.valueField] != v){
16031                return;
16032            }
16033            
16034            opts.el.dom.setAttribute('selected', true);
16035            
16036         }, this);
16037     }
16038
16039     /** 
16040     * @cfg {Boolean} grow 
16041     * @hide 
16042     */
16043     /** 
16044     * @cfg {Number} growMin 
16045     * @hide 
16046     */
16047     /** 
16048     * @cfg {Number} growMax 
16049     * @hide 
16050     */
16051     /**
16052      * @hide
16053      * @method autoSize
16054      */
16055 });
16056
16057 Roo.apply(Roo.bootstrap.ComboBox,  {
16058     
16059     header : {
16060         tag: 'div',
16061         cls: 'modal-header',
16062         cn: [
16063             {
16064                 tag: 'h4',
16065                 cls: 'modal-title'
16066             }
16067         ]
16068     },
16069     
16070     body : {
16071         tag: 'div',
16072         cls: 'modal-body',
16073         cn: [
16074             {
16075                 tag: 'ul',
16076                 cls: 'list-group'
16077             }
16078         ]
16079     },
16080     
16081     listItemRadio : {
16082         tag: 'li',
16083         cls: 'list-group-item',
16084         cn: [
16085             {
16086                 tag: 'span',
16087                 cls: 'roo-combobox-list-group-item-value'
16088             },
16089             {
16090                 tag: 'div',
16091                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16092                 cn: [
16093                     {
16094                         tag: 'input',
16095                         type: 'radio'
16096                     },
16097                     {
16098                         tag: 'label'
16099                     }
16100                 ]
16101             }
16102         ]
16103     },
16104     
16105     listItemCheckbox : {
16106         tag: 'li',
16107         cls: 'list-group-item',
16108         cn: [
16109             {
16110                 tag: 'span',
16111                 cls: 'roo-combobox-list-group-item-value'
16112             },
16113             {
16114                 tag: 'div',
16115                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16116                 cn: [
16117                     {
16118                         tag: 'input',
16119                         type: 'checkbox'
16120                     },
16121                     {
16122                         tag: 'label'
16123                     }
16124                 ]
16125             }
16126         ]
16127     },
16128     
16129     emptyResult : {
16130         tag: 'div',
16131         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16132     },
16133     
16134     footer : {
16135         tag: 'div',
16136         cls: 'modal-footer',
16137         cn: [
16138             {
16139                 tag: 'div',
16140                 cls: 'row',
16141                 cn: [
16142                     {
16143                         tag: 'div',
16144                         cls: 'col-xs-6 text-left',
16145                         cn: {
16146                             tag: 'button',
16147                             cls: 'btn btn-danger roo-touch-view-cancel',
16148                             html: 'Cancel'
16149                         }
16150                     },
16151                     {
16152                         tag: 'div',
16153                         cls: 'col-xs-6 text-right',
16154                         cn: {
16155                             tag: 'button',
16156                             cls: 'btn btn-success roo-touch-view-ok',
16157                             html: 'OK'
16158                         }
16159                     }
16160                 ]
16161             }
16162         ]
16163         
16164     }
16165 });
16166
16167 Roo.apply(Roo.bootstrap.ComboBox,  {
16168     
16169     touchViewTemplate : {
16170         tag: 'div',
16171         cls: 'modal fade roo-combobox-touch-view',
16172         cn: [
16173             {
16174                 tag: 'div',
16175                 cls: 'modal-dialog',
16176                 style : 'position:fixed', // we have to fix position....
16177                 cn: [
16178                     {
16179                         tag: 'div',
16180                         cls: 'modal-content',
16181                         cn: [
16182                             Roo.bootstrap.ComboBox.header,
16183                             Roo.bootstrap.ComboBox.body,
16184                             Roo.bootstrap.ComboBox.footer
16185                         ]
16186                     }
16187                 ]
16188             }
16189         ]
16190     }
16191 });/*
16192  * Based on:
16193  * Ext JS Library 1.1.1
16194  * Copyright(c) 2006-2007, Ext JS, LLC.
16195  *
16196  * Originally Released Under LGPL - original licence link has changed is not relivant.
16197  *
16198  * Fork - LGPL
16199  * <script type="text/javascript">
16200  */
16201
16202 /**
16203  * @class Roo.View
16204  * @extends Roo.util.Observable
16205  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16206  * This class also supports single and multi selection modes. <br>
16207  * Create a data model bound view:
16208  <pre><code>
16209  var store = new Roo.data.Store(...);
16210
16211  var view = new Roo.View({
16212     el : "my-element",
16213     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16214  
16215     singleSelect: true,
16216     selectedClass: "ydataview-selected",
16217     store: store
16218  });
16219
16220  // listen for node click?
16221  view.on("click", function(vw, index, node, e){
16222  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16223  });
16224
16225  // load XML data
16226  dataModel.load("foobar.xml");
16227  </code></pre>
16228  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16229  * <br><br>
16230  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16231  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16232  * 
16233  * Note: old style constructor is still suported (container, template, config)
16234  * 
16235  * @constructor
16236  * Create a new View
16237  * @param {Object} config The config object
16238  * 
16239  */
16240 Roo.View = function(config, depreciated_tpl, depreciated_config){
16241     
16242     this.parent = false;
16243     
16244     if (typeof(depreciated_tpl) == 'undefined') {
16245         // new way.. - universal constructor.
16246         Roo.apply(this, config);
16247         this.el  = Roo.get(this.el);
16248     } else {
16249         // old format..
16250         this.el  = Roo.get(config);
16251         this.tpl = depreciated_tpl;
16252         Roo.apply(this, depreciated_config);
16253     }
16254     this.wrapEl  = this.el.wrap().wrap();
16255     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16256     
16257     
16258     if(typeof(this.tpl) == "string"){
16259         this.tpl = new Roo.Template(this.tpl);
16260     } else {
16261         // support xtype ctors..
16262         this.tpl = new Roo.factory(this.tpl, Roo);
16263     }
16264     
16265     
16266     this.tpl.compile();
16267     
16268     /** @private */
16269     this.addEvents({
16270         /**
16271          * @event beforeclick
16272          * Fires before a click is processed. Returns false to cancel the default action.
16273          * @param {Roo.View} this
16274          * @param {Number} index The index of the target node
16275          * @param {HTMLElement} node The target node
16276          * @param {Roo.EventObject} e The raw event object
16277          */
16278             "beforeclick" : true,
16279         /**
16280          * @event click
16281          * Fires when a template node is clicked.
16282          * @param {Roo.View} this
16283          * @param {Number} index The index of the target node
16284          * @param {HTMLElement} node The target node
16285          * @param {Roo.EventObject} e The raw event object
16286          */
16287             "click" : true,
16288         /**
16289          * @event dblclick
16290          * Fires when a template node is double clicked.
16291          * @param {Roo.View} this
16292          * @param {Number} index The index of the target node
16293          * @param {HTMLElement} node The target node
16294          * @param {Roo.EventObject} e The raw event object
16295          */
16296             "dblclick" : true,
16297         /**
16298          * @event contextmenu
16299          * Fires when a template node is right clicked.
16300          * @param {Roo.View} this
16301          * @param {Number} index The index of the target node
16302          * @param {HTMLElement} node The target node
16303          * @param {Roo.EventObject} e The raw event object
16304          */
16305             "contextmenu" : true,
16306         /**
16307          * @event selectionchange
16308          * Fires when the selected nodes change.
16309          * @param {Roo.View} this
16310          * @param {Array} selections Array of the selected nodes
16311          */
16312             "selectionchange" : true,
16313     
16314         /**
16315          * @event beforeselect
16316          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16317          * @param {Roo.View} this
16318          * @param {HTMLElement} node The node to be selected
16319          * @param {Array} selections Array of currently selected nodes
16320          */
16321             "beforeselect" : true,
16322         /**
16323          * @event preparedata
16324          * Fires on every row to render, to allow you to change the data.
16325          * @param {Roo.View} this
16326          * @param {Object} data to be rendered (change this)
16327          */
16328           "preparedata" : true
16329           
16330           
16331         });
16332
16333
16334
16335     this.el.on({
16336         "click": this.onClick,
16337         "dblclick": this.onDblClick,
16338         "contextmenu": this.onContextMenu,
16339         scope:this
16340     });
16341
16342     this.selections = [];
16343     this.nodes = [];
16344     this.cmp = new Roo.CompositeElementLite([]);
16345     if(this.store){
16346         this.store = Roo.factory(this.store, Roo.data);
16347         this.setStore(this.store, true);
16348     }
16349     
16350     if ( this.footer && this.footer.xtype) {
16351            
16352          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16353         
16354         this.footer.dataSource = this.store;
16355         this.footer.container = fctr;
16356         this.footer = Roo.factory(this.footer, Roo);
16357         fctr.insertFirst(this.el);
16358         
16359         // this is a bit insane - as the paging toolbar seems to detach the el..
16360 //        dom.parentNode.parentNode.parentNode
16361          // they get detached?
16362     }
16363     
16364     
16365     Roo.View.superclass.constructor.call(this);
16366     
16367     
16368 };
16369
16370 Roo.extend(Roo.View, Roo.util.Observable, {
16371     
16372      /**
16373      * @cfg {Roo.data.Store} store Data store to load data from.
16374      */
16375     store : false,
16376     
16377     /**
16378      * @cfg {String|Roo.Element} el The container element.
16379      */
16380     el : '',
16381     
16382     /**
16383      * @cfg {String|Roo.Template} tpl The template used by this View 
16384      */
16385     tpl : false,
16386     /**
16387      * @cfg {String} dataName the named area of the template to use as the data area
16388      *                          Works with domtemplates roo-name="name"
16389      */
16390     dataName: false,
16391     /**
16392      * @cfg {String} selectedClass The css class to add to selected nodes
16393      */
16394     selectedClass : "x-view-selected",
16395      /**
16396      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16397      */
16398     emptyText : "",
16399     
16400     /**
16401      * @cfg {String} text to display on mask (default Loading)
16402      */
16403     mask : false,
16404     /**
16405      * @cfg {Boolean} multiSelect Allow multiple selection
16406      */
16407     multiSelect : false,
16408     /**
16409      * @cfg {Boolean} singleSelect Allow single selection
16410      */
16411     singleSelect:  false,
16412     
16413     /**
16414      * @cfg {Boolean} toggleSelect - selecting 
16415      */
16416     toggleSelect : false,
16417     
16418     /**
16419      * @cfg {Boolean} tickable - selecting 
16420      */
16421     tickable : false,
16422     
16423     /**
16424      * Returns the element this view is bound to.
16425      * @return {Roo.Element}
16426      */
16427     getEl : function(){
16428         return this.wrapEl;
16429     },
16430     
16431     
16432
16433     /**
16434      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16435      */
16436     refresh : function(){
16437         //Roo.log('refresh');
16438         var t = this.tpl;
16439         
16440         // if we are using something like 'domtemplate', then
16441         // the what gets used is:
16442         // t.applySubtemplate(NAME, data, wrapping data..)
16443         // the outer template then get' applied with
16444         //     the store 'extra data'
16445         // and the body get's added to the
16446         //      roo-name="data" node?
16447         //      <span class='roo-tpl-{name}'></span> ?????
16448         
16449         
16450         
16451         this.clearSelections();
16452         this.el.update("");
16453         var html = [];
16454         var records = this.store.getRange();
16455         if(records.length < 1) {
16456             
16457             // is this valid??  = should it render a template??
16458             
16459             this.el.update(this.emptyText);
16460             return;
16461         }
16462         var el = this.el;
16463         if (this.dataName) {
16464             this.el.update(t.apply(this.store.meta)); //????
16465             el = this.el.child('.roo-tpl-' + this.dataName);
16466         }
16467         
16468         for(var i = 0, len = records.length; i < len; i++){
16469             var data = this.prepareData(records[i].data, i, records[i]);
16470             this.fireEvent("preparedata", this, data, i, records[i]);
16471             
16472             var d = Roo.apply({}, data);
16473             
16474             if(this.tickable){
16475                 Roo.apply(d, {'roo-id' : Roo.id()});
16476                 
16477                 var _this = this;
16478             
16479                 Roo.each(this.parent.item, function(item){
16480                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16481                         return;
16482                     }
16483                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16484                 });
16485             }
16486             
16487             html[html.length] = Roo.util.Format.trim(
16488                 this.dataName ?
16489                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16490                     t.apply(d)
16491             );
16492         }
16493         
16494         
16495         
16496         el.update(html.join(""));
16497         this.nodes = el.dom.childNodes;
16498         this.updateIndexes(0);
16499     },
16500     
16501
16502     /**
16503      * Function to override to reformat the data that is sent to
16504      * the template for each node.
16505      * DEPRICATED - use the preparedata event handler.
16506      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16507      * a JSON object for an UpdateManager bound view).
16508      */
16509     prepareData : function(data, index, record)
16510     {
16511         this.fireEvent("preparedata", this, data, index, record);
16512         return data;
16513     },
16514
16515     onUpdate : function(ds, record){
16516         // Roo.log('on update');   
16517         this.clearSelections();
16518         var index = this.store.indexOf(record);
16519         var n = this.nodes[index];
16520         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16521         n.parentNode.removeChild(n);
16522         this.updateIndexes(index, index);
16523     },
16524
16525     
16526     
16527 // --------- FIXME     
16528     onAdd : function(ds, records, index)
16529     {
16530         //Roo.log(['on Add', ds, records, index] );        
16531         this.clearSelections();
16532         if(this.nodes.length == 0){
16533             this.refresh();
16534             return;
16535         }
16536         var n = this.nodes[index];
16537         for(var i = 0, len = records.length; i < len; i++){
16538             var d = this.prepareData(records[i].data, i, records[i]);
16539             if(n){
16540                 this.tpl.insertBefore(n, d);
16541             }else{
16542                 
16543                 this.tpl.append(this.el, d);
16544             }
16545         }
16546         this.updateIndexes(index);
16547     },
16548
16549     onRemove : function(ds, record, index){
16550        // Roo.log('onRemove');
16551         this.clearSelections();
16552         var el = this.dataName  ?
16553             this.el.child('.roo-tpl-' + this.dataName) :
16554             this.el; 
16555         
16556         el.dom.removeChild(this.nodes[index]);
16557         this.updateIndexes(index);
16558     },
16559
16560     /**
16561      * Refresh an individual node.
16562      * @param {Number} index
16563      */
16564     refreshNode : function(index){
16565         this.onUpdate(this.store, this.store.getAt(index));
16566     },
16567
16568     updateIndexes : function(startIndex, endIndex){
16569         var ns = this.nodes;
16570         startIndex = startIndex || 0;
16571         endIndex = endIndex || ns.length - 1;
16572         for(var i = startIndex; i <= endIndex; i++){
16573             ns[i].nodeIndex = i;
16574         }
16575     },
16576
16577     /**
16578      * Changes the data store this view uses and refresh the view.
16579      * @param {Store} store
16580      */
16581     setStore : function(store, initial){
16582         if(!initial && this.store){
16583             this.store.un("datachanged", this.refresh);
16584             this.store.un("add", this.onAdd);
16585             this.store.un("remove", this.onRemove);
16586             this.store.un("update", this.onUpdate);
16587             this.store.un("clear", this.refresh);
16588             this.store.un("beforeload", this.onBeforeLoad);
16589             this.store.un("load", this.onLoad);
16590             this.store.un("loadexception", this.onLoad);
16591         }
16592         if(store){
16593           
16594             store.on("datachanged", this.refresh, this);
16595             store.on("add", this.onAdd, this);
16596             store.on("remove", this.onRemove, this);
16597             store.on("update", this.onUpdate, this);
16598             store.on("clear", this.refresh, this);
16599             store.on("beforeload", this.onBeforeLoad, this);
16600             store.on("load", this.onLoad, this);
16601             store.on("loadexception", this.onLoad, this);
16602         }
16603         
16604         if(store){
16605             this.refresh();
16606         }
16607     },
16608     /**
16609      * onbeforeLoad - masks the loading area.
16610      *
16611      */
16612     onBeforeLoad : function(store,opts)
16613     {
16614          //Roo.log('onBeforeLoad');   
16615         if (!opts.add) {
16616             this.el.update("");
16617         }
16618         this.el.mask(this.mask ? this.mask : "Loading" ); 
16619     },
16620     onLoad : function ()
16621     {
16622         this.el.unmask();
16623     },
16624     
16625
16626     /**
16627      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16628      * @param {HTMLElement} node
16629      * @return {HTMLElement} The template node
16630      */
16631     findItemFromChild : function(node){
16632         var el = this.dataName  ?
16633             this.el.child('.roo-tpl-' + this.dataName,true) :
16634             this.el.dom; 
16635         
16636         if(!node || node.parentNode == el){
16637                     return node;
16638             }
16639             var p = node.parentNode;
16640             while(p && p != el){
16641             if(p.parentNode == el){
16642                 return p;
16643             }
16644             p = p.parentNode;
16645         }
16646             return null;
16647     },
16648
16649     /** @ignore */
16650     onClick : function(e){
16651         var item = this.findItemFromChild(e.getTarget());
16652         if(item){
16653             var index = this.indexOf(item);
16654             if(this.onItemClick(item, index, e) !== false){
16655                 this.fireEvent("click", this, index, item, e);
16656             }
16657         }else{
16658             this.clearSelections();
16659         }
16660     },
16661
16662     /** @ignore */
16663     onContextMenu : function(e){
16664         var item = this.findItemFromChild(e.getTarget());
16665         if(item){
16666             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16667         }
16668     },
16669
16670     /** @ignore */
16671     onDblClick : function(e){
16672         var item = this.findItemFromChild(e.getTarget());
16673         if(item){
16674             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16675         }
16676     },
16677
16678     onItemClick : function(item, index, e)
16679     {
16680         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16681             return false;
16682         }
16683         if (this.toggleSelect) {
16684             var m = this.isSelected(item) ? 'unselect' : 'select';
16685             //Roo.log(m);
16686             var _t = this;
16687             _t[m](item, true, false);
16688             return true;
16689         }
16690         if(this.multiSelect || this.singleSelect){
16691             if(this.multiSelect && e.shiftKey && this.lastSelection){
16692                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16693             }else{
16694                 this.select(item, this.multiSelect && e.ctrlKey);
16695                 this.lastSelection = item;
16696             }
16697             
16698             if(!this.tickable){
16699                 e.preventDefault();
16700             }
16701             
16702         }
16703         return true;
16704     },
16705
16706     /**
16707      * Get the number of selected nodes.
16708      * @return {Number}
16709      */
16710     getSelectionCount : function(){
16711         return this.selections.length;
16712     },
16713
16714     /**
16715      * Get the currently selected nodes.
16716      * @return {Array} An array of HTMLElements
16717      */
16718     getSelectedNodes : function(){
16719         return this.selections;
16720     },
16721
16722     /**
16723      * Get the indexes of the selected nodes.
16724      * @return {Array}
16725      */
16726     getSelectedIndexes : function(){
16727         var indexes = [], s = this.selections;
16728         for(var i = 0, len = s.length; i < len; i++){
16729             indexes.push(s[i].nodeIndex);
16730         }
16731         return indexes;
16732     },
16733
16734     /**
16735      * Clear all selections
16736      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16737      */
16738     clearSelections : function(suppressEvent){
16739         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16740             this.cmp.elements = this.selections;
16741             this.cmp.removeClass(this.selectedClass);
16742             this.selections = [];
16743             if(!suppressEvent){
16744                 this.fireEvent("selectionchange", this, this.selections);
16745             }
16746         }
16747     },
16748
16749     /**
16750      * Returns true if the passed node is selected
16751      * @param {HTMLElement/Number} node The node or node index
16752      * @return {Boolean}
16753      */
16754     isSelected : function(node){
16755         var s = this.selections;
16756         if(s.length < 1){
16757             return false;
16758         }
16759         node = this.getNode(node);
16760         return s.indexOf(node) !== -1;
16761     },
16762
16763     /**
16764      * Selects nodes.
16765      * @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
16766      * @param {Boolean} keepExisting (optional) true to keep existing selections
16767      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16768      */
16769     select : function(nodeInfo, keepExisting, suppressEvent){
16770         if(nodeInfo instanceof Array){
16771             if(!keepExisting){
16772                 this.clearSelections(true);
16773             }
16774             for(var i = 0, len = nodeInfo.length; i < len; i++){
16775                 this.select(nodeInfo[i], true, true);
16776             }
16777             return;
16778         } 
16779         var node = this.getNode(nodeInfo);
16780         if(!node || this.isSelected(node)){
16781             return; // already selected.
16782         }
16783         if(!keepExisting){
16784             this.clearSelections(true);
16785         }
16786         
16787         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16788             Roo.fly(node).addClass(this.selectedClass);
16789             this.selections.push(node);
16790             if(!suppressEvent){
16791                 this.fireEvent("selectionchange", this, this.selections);
16792             }
16793         }
16794         
16795         
16796     },
16797       /**
16798      * Unselects nodes.
16799      * @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
16800      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16801      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16802      */
16803     unselect : function(nodeInfo, keepExisting, suppressEvent)
16804     {
16805         if(nodeInfo instanceof Array){
16806             Roo.each(this.selections, function(s) {
16807                 this.unselect(s, nodeInfo);
16808             }, this);
16809             return;
16810         }
16811         var node = this.getNode(nodeInfo);
16812         if(!node || !this.isSelected(node)){
16813             //Roo.log("not selected");
16814             return; // not selected.
16815         }
16816         // fireevent???
16817         var ns = [];
16818         Roo.each(this.selections, function(s) {
16819             if (s == node ) {
16820                 Roo.fly(node).removeClass(this.selectedClass);
16821
16822                 return;
16823             }
16824             ns.push(s);
16825         },this);
16826         
16827         this.selections= ns;
16828         this.fireEvent("selectionchange", this, this.selections);
16829     },
16830
16831     /**
16832      * Gets a template node.
16833      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16834      * @return {HTMLElement} The node or null if it wasn't found
16835      */
16836     getNode : function(nodeInfo){
16837         if(typeof nodeInfo == "string"){
16838             return document.getElementById(nodeInfo);
16839         }else if(typeof nodeInfo == "number"){
16840             return this.nodes[nodeInfo];
16841         }
16842         return nodeInfo;
16843     },
16844
16845     /**
16846      * Gets a range template nodes.
16847      * @param {Number} startIndex
16848      * @param {Number} endIndex
16849      * @return {Array} An array of nodes
16850      */
16851     getNodes : function(start, end){
16852         var ns = this.nodes;
16853         start = start || 0;
16854         end = typeof end == "undefined" ? ns.length - 1 : end;
16855         var nodes = [];
16856         if(start <= end){
16857             for(var i = start; i <= end; i++){
16858                 nodes.push(ns[i]);
16859             }
16860         } else{
16861             for(var i = start; i >= end; i--){
16862                 nodes.push(ns[i]);
16863             }
16864         }
16865         return nodes;
16866     },
16867
16868     /**
16869      * Finds the index of the passed node
16870      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16871      * @return {Number} The index of the node or -1
16872      */
16873     indexOf : function(node){
16874         node = this.getNode(node);
16875         if(typeof node.nodeIndex == "number"){
16876             return node.nodeIndex;
16877         }
16878         var ns = this.nodes;
16879         for(var i = 0, len = ns.length; i < len; i++){
16880             if(ns[i] == node){
16881                 return i;
16882             }
16883         }
16884         return -1;
16885     }
16886 });
16887 /*
16888  * - LGPL
16889  *
16890  * based on jquery fullcalendar
16891  * 
16892  */
16893
16894 Roo.bootstrap = Roo.bootstrap || {};
16895 /**
16896  * @class Roo.bootstrap.Calendar
16897  * @extends Roo.bootstrap.Component
16898  * Bootstrap Calendar class
16899  * @cfg {Boolean} loadMask (true|false) default false
16900  * @cfg {Object} header generate the user specific header of the calendar, default false
16901
16902  * @constructor
16903  * Create a new Container
16904  * @param {Object} config The config object
16905  */
16906
16907
16908
16909 Roo.bootstrap.Calendar = function(config){
16910     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16911      this.addEvents({
16912         /**
16913              * @event select
16914              * Fires when a date is selected
16915              * @param {DatePicker} this
16916              * @param {Date} date The selected date
16917              */
16918         'select': true,
16919         /**
16920              * @event monthchange
16921              * Fires when the displayed month changes 
16922              * @param {DatePicker} this
16923              * @param {Date} date The selected month
16924              */
16925         'monthchange': true,
16926         /**
16927              * @event evententer
16928              * Fires when mouse over an event
16929              * @param {Calendar} this
16930              * @param {event} Event
16931              */
16932         'evententer': true,
16933         /**
16934              * @event eventleave
16935              * Fires when the mouse leaves an
16936              * @param {Calendar} this
16937              * @param {event}
16938              */
16939         'eventleave': true,
16940         /**
16941              * @event eventclick
16942              * Fires when the mouse click an
16943              * @param {Calendar} this
16944              * @param {event}
16945              */
16946         'eventclick': true
16947         
16948     });
16949
16950 };
16951
16952 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16953     
16954      /**
16955      * @cfg {Number} startDay
16956      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16957      */
16958     startDay : 0,
16959     
16960     loadMask : false,
16961     
16962     header : false,
16963       
16964     getAutoCreate : function(){
16965         
16966         
16967         var fc_button = function(name, corner, style, content ) {
16968             return Roo.apply({},{
16969                 tag : 'span',
16970                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16971                          (corner.length ?
16972                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16973                             ''
16974                         ),
16975                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16976                 unselectable: 'on'
16977             });
16978         };
16979         
16980         var header = {};
16981         
16982         if(!this.header){
16983             header = {
16984                 tag : 'table',
16985                 cls : 'fc-header',
16986                 style : 'width:100%',
16987                 cn : [
16988                     {
16989                         tag: 'tr',
16990                         cn : [
16991                             {
16992                                 tag : 'td',
16993                                 cls : 'fc-header-left',
16994                                 cn : [
16995                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16996                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16997                                     { tag: 'span', cls: 'fc-header-space' },
16998                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16999
17000
17001                                 ]
17002                             },
17003
17004                             {
17005                                 tag : 'td',
17006                                 cls : 'fc-header-center',
17007                                 cn : [
17008                                     {
17009                                         tag: 'span',
17010                                         cls: 'fc-header-title',
17011                                         cn : {
17012                                             tag: 'H2',
17013                                             html : 'month / year'
17014                                         }
17015                                     }
17016
17017                                 ]
17018                             },
17019                             {
17020                                 tag : 'td',
17021                                 cls : 'fc-header-right',
17022                                 cn : [
17023                               /*      fc_button('month', 'left', '', 'month' ),
17024                                     fc_button('week', '', '', 'week' ),
17025                                     fc_button('day', 'right', '', 'day' )
17026                                 */    
17027
17028                                 ]
17029                             }
17030
17031                         ]
17032                     }
17033                 ]
17034             };
17035         }
17036         
17037         header = this.header;
17038         
17039        
17040         var cal_heads = function() {
17041             var ret = [];
17042             // fixme - handle this.
17043             
17044             for (var i =0; i < Date.dayNames.length; i++) {
17045                 var d = Date.dayNames[i];
17046                 ret.push({
17047                     tag: 'th',
17048                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17049                     html : d.substring(0,3)
17050                 });
17051                 
17052             }
17053             ret[0].cls += ' fc-first';
17054             ret[6].cls += ' fc-last';
17055             return ret;
17056         };
17057         var cal_cell = function(n) {
17058             return  {
17059                 tag: 'td',
17060                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17061                 cn : [
17062                     {
17063                         cn : [
17064                             {
17065                                 cls: 'fc-day-number',
17066                                 html: 'D'
17067                             },
17068                             {
17069                                 cls: 'fc-day-content',
17070                              
17071                                 cn : [
17072                                      {
17073                                         style: 'position: relative;' // height: 17px;
17074                                     }
17075                                 ]
17076                             }
17077                             
17078                             
17079                         ]
17080                     }
17081                 ]
17082                 
17083             }
17084         };
17085         var cal_rows = function() {
17086             
17087             var ret = [];
17088             for (var r = 0; r < 6; r++) {
17089                 var row= {
17090                     tag : 'tr',
17091                     cls : 'fc-week',
17092                     cn : []
17093                 };
17094                 
17095                 for (var i =0; i < Date.dayNames.length; i++) {
17096                     var d = Date.dayNames[i];
17097                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17098
17099                 }
17100                 row.cn[0].cls+=' fc-first';
17101                 row.cn[0].cn[0].style = 'min-height:90px';
17102                 row.cn[6].cls+=' fc-last';
17103                 ret.push(row);
17104                 
17105             }
17106             ret[0].cls += ' fc-first';
17107             ret[4].cls += ' fc-prev-last';
17108             ret[5].cls += ' fc-last';
17109             return ret;
17110             
17111         };
17112         
17113         var cal_table = {
17114             tag: 'table',
17115             cls: 'fc-border-separate',
17116             style : 'width:100%',
17117             cellspacing  : 0,
17118             cn : [
17119                 { 
17120                     tag: 'thead',
17121                     cn : [
17122                         { 
17123                             tag: 'tr',
17124                             cls : 'fc-first fc-last',
17125                             cn : cal_heads()
17126                         }
17127                     ]
17128                 },
17129                 { 
17130                     tag: 'tbody',
17131                     cn : cal_rows()
17132                 }
17133                   
17134             ]
17135         };
17136          
17137          var cfg = {
17138             cls : 'fc fc-ltr',
17139             cn : [
17140                 header,
17141                 {
17142                     cls : 'fc-content',
17143                     style : "position: relative;",
17144                     cn : [
17145                         {
17146                             cls : 'fc-view fc-view-month fc-grid',
17147                             style : 'position: relative',
17148                             unselectable : 'on',
17149                             cn : [
17150                                 {
17151                                     cls : 'fc-event-container',
17152                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17153                                 },
17154                                 cal_table
17155                             ]
17156                         }
17157                     ]
17158     
17159                 }
17160            ] 
17161             
17162         };
17163         
17164          
17165         
17166         return cfg;
17167     },
17168     
17169     
17170     initEvents : function()
17171     {
17172         if(!this.store){
17173             throw "can not find store for calendar";
17174         }
17175         
17176         var mark = {
17177             tag: "div",
17178             cls:"x-dlg-mask",
17179             style: "text-align:center",
17180             cn: [
17181                 {
17182                     tag: "div",
17183                     style: "background-color:white;width:50%;margin:250 auto",
17184                     cn: [
17185                         {
17186                             tag: "img",
17187                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17188                         },
17189                         {
17190                             tag: "span",
17191                             html: "Loading"
17192                         }
17193                         
17194                     ]
17195                 }
17196             ]
17197         };
17198         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17199         
17200         var size = this.el.select('.fc-content', true).first().getSize();
17201         this.maskEl.setSize(size.width, size.height);
17202         this.maskEl.enableDisplayMode("block");
17203         if(!this.loadMask){
17204             this.maskEl.hide();
17205         }
17206         
17207         this.store = Roo.factory(this.store, Roo.data);
17208         this.store.on('load', this.onLoad, this);
17209         this.store.on('beforeload', this.onBeforeLoad, this);
17210         
17211         this.resize();
17212         
17213         this.cells = this.el.select('.fc-day',true);
17214         //Roo.log(this.cells);
17215         this.textNodes = this.el.query('.fc-day-number');
17216         this.cells.addClassOnOver('fc-state-hover');
17217         
17218         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17219         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17220         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17221         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17222         
17223         this.on('monthchange', this.onMonthChange, this);
17224         
17225         this.update(new Date().clearTime());
17226     },
17227     
17228     resize : function() {
17229         var sz  = this.el.getSize();
17230         
17231         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17232         this.el.select('.fc-day-content div',true).setHeight(34);
17233     },
17234     
17235     
17236     // private
17237     showPrevMonth : function(e){
17238         this.update(this.activeDate.add("mo", -1));
17239     },
17240     showToday : function(e){
17241         this.update(new Date().clearTime());
17242     },
17243     // private
17244     showNextMonth : function(e){
17245         this.update(this.activeDate.add("mo", 1));
17246     },
17247
17248     // private
17249     showPrevYear : function(){
17250         this.update(this.activeDate.add("y", -1));
17251     },
17252
17253     // private
17254     showNextYear : function(){
17255         this.update(this.activeDate.add("y", 1));
17256     },
17257
17258     
17259    // private
17260     update : function(date)
17261     {
17262         var vd = this.activeDate;
17263         this.activeDate = date;
17264 //        if(vd && this.el){
17265 //            var t = date.getTime();
17266 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17267 //                Roo.log('using add remove');
17268 //                
17269 //                this.fireEvent('monthchange', this, date);
17270 //                
17271 //                this.cells.removeClass("fc-state-highlight");
17272 //                this.cells.each(function(c){
17273 //                   if(c.dateValue == t){
17274 //                       c.addClass("fc-state-highlight");
17275 //                       setTimeout(function(){
17276 //                            try{c.dom.firstChild.focus();}catch(e){}
17277 //                       }, 50);
17278 //                       return false;
17279 //                   }
17280 //                   return true;
17281 //                });
17282 //                return;
17283 //            }
17284 //        }
17285         
17286         var days = date.getDaysInMonth();
17287         
17288         var firstOfMonth = date.getFirstDateOfMonth();
17289         var startingPos = firstOfMonth.getDay()-this.startDay;
17290         
17291         if(startingPos < this.startDay){
17292             startingPos += 7;
17293         }
17294         
17295         var pm = date.add(Date.MONTH, -1);
17296         var prevStart = pm.getDaysInMonth()-startingPos;
17297 //        
17298         this.cells = this.el.select('.fc-day',true);
17299         this.textNodes = this.el.query('.fc-day-number');
17300         this.cells.addClassOnOver('fc-state-hover');
17301         
17302         var cells = this.cells.elements;
17303         var textEls = this.textNodes;
17304         
17305         Roo.each(cells, function(cell){
17306             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17307         });
17308         
17309         days += startingPos;
17310
17311         // convert everything to numbers so it's fast
17312         var day = 86400000;
17313         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17314         //Roo.log(d);
17315         //Roo.log(pm);
17316         //Roo.log(prevStart);
17317         
17318         var today = new Date().clearTime().getTime();
17319         var sel = date.clearTime().getTime();
17320         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17321         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17322         var ddMatch = this.disabledDatesRE;
17323         var ddText = this.disabledDatesText;
17324         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17325         var ddaysText = this.disabledDaysText;
17326         var format = this.format;
17327         
17328         var setCellClass = function(cal, cell){
17329             cell.row = 0;
17330             cell.events = [];
17331             cell.more = [];
17332             //Roo.log('set Cell Class');
17333             cell.title = "";
17334             var t = d.getTime();
17335             
17336             //Roo.log(d);
17337             
17338             cell.dateValue = t;
17339             if(t == today){
17340                 cell.className += " fc-today";
17341                 cell.className += " fc-state-highlight";
17342                 cell.title = cal.todayText;
17343             }
17344             if(t == sel){
17345                 // disable highlight in other month..
17346                 //cell.className += " fc-state-highlight";
17347                 
17348             }
17349             // disabling
17350             if(t < min) {
17351                 cell.className = " fc-state-disabled";
17352                 cell.title = cal.minText;
17353                 return;
17354             }
17355             if(t > max) {
17356                 cell.className = " fc-state-disabled";
17357                 cell.title = cal.maxText;
17358                 return;
17359             }
17360             if(ddays){
17361                 if(ddays.indexOf(d.getDay()) != -1){
17362                     cell.title = ddaysText;
17363                     cell.className = " fc-state-disabled";
17364                 }
17365             }
17366             if(ddMatch && format){
17367                 var fvalue = d.dateFormat(format);
17368                 if(ddMatch.test(fvalue)){
17369                     cell.title = ddText.replace("%0", fvalue);
17370                     cell.className = " fc-state-disabled";
17371                 }
17372             }
17373             
17374             if (!cell.initialClassName) {
17375                 cell.initialClassName = cell.dom.className;
17376             }
17377             
17378             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17379         };
17380
17381         var i = 0;
17382         
17383         for(; i < startingPos; i++) {
17384             textEls[i].innerHTML = (++prevStart);
17385             d.setDate(d.getDate()+1);
17386             
17387             cells[i].className = "fc-past fc-other-month";
17388             setCellClass(this, cells[i]);
17389         }
17390         
17391         var intDay = 0;
17392         
17393         for(; i < days; i++){
17394             intDay = i - startingPos + 1;
17395             textEls[i].innerHTML = (intDay);
17396             d.setDate(d.getDate()+1);
17397             
17398             cells[i].className = ''; // "x-date-active";
17399             setCellClass(this, cells[i]);
17400         }
17401         var extraDays = 0;
17402         
17403         for(; i < 42; i++) {
17404             textEls[i].innerHTML = (++extraDays);
17405             d.setDate(d.getDate()+1);
17406             
17407             cells[i].className = "fc-future fc-other-month";
17408             setCellClass(this, cells[i]);
17409         }
17410         
17411         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17412         
17413         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17414         
17415         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17416         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17417         
17418         if(totalRows != 6){
17419             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17420             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17421         }
17422         
17423         this.fireEvent('monthchange', this, date);
17424         
17425         
17426         /*
17427         if(!this.internalRender){
17428             var main = this.el.dom.firstChild;
17429             var w = main.offsetWidth;
17430             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17431             Roo.fly(main).setWidth(w);
17432             this.internalRender = true;
17433             // opera does not respect the auto grow header center column
17434             // then, after it gets a width opera refuses to recalculate
17435             // without a second pass
17436             if(Roo.isOpera && !this.secondPass){
17437                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17438                 this.secondPass = true;
17439                 this.update.defer(10, this, [date]);
17440             }
17441         }
17442         */
17443         
17444     },
17445     
17446     findCell : function(dt) {
17447         dt = dt.clearTime().getTime();
17448         var ret = false;
17449         this.cells.each(function(c){
17450             //Roo.log("check " +c.dateValue + '?=' + dt);
17451             if(c.dateValue == dt){
17452                 ret = c;
17453                 return false;
17454             }
17455             return true;
17456         });
17457         
17458         return ret;
17459     },
17460     
17461     findCells : function(ev) {
17462         var s = ev.start.clone().clearTime().getTime();
17463        // Roo.log(s);
17464         var e= ev.end.clone().clearTime().getTime();
17465        // Roo.log(e);
17466         var ret = [];
17467         this.cells.each(function(c){
17468              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17469             
17470             if(c.dateValue > e){
17471                 return ;
17472             }
17473             if(c.dateValue < s){
17474                 return ;
17475             }
17476             ret.push(c);
17477         });
17478         
17479         return ret;    
17480     },
17481     
17482 //    findBestRow: function(cells)
17483 //    {
17484 //        var ret = 0;
17485 //        
17486 //        for (var i =0 ; i < cells.length;i++) {
17487 //            ret  = Math.max(cells[i].rows || 0,ret);
17488 //        }
17489 //        return ret;
17490 //        
17491 //    },
17492     
17493     
17494     addItem : function(ev)
17495     {
17496         // look for vertical location slot in
17497         var cells = this.findCells(ev);
17498         
17499 //        ev.row = this.findBestRow(cells);
17500         
17501         // work out the location.
17502         
17503         var crow = false;
17504         var rows = [];
17505         for(var i =0; i < cells.length; i++) {
17506             
17507             cells[i].row = cells[0].row;
17508             
17509             if(i == 0){
17510                 cells[i].row = cells[i].row + 1;
17511             }
17512             
17513             if (!crow) {
17514                 crow = {
17515                     start : cells[i],
17516                     end :  cells[i]
17517                 };
17518                 continue;
17519             }
17520             if (crow.start.getY() == cells[i].getY()) {
17521                 // on same row.
17522                 crow.end = cells[i];
17523                 continue;
17524             }
17525             // different row.
17526             rows.push(crow);
17527             crow = {
17528                 start: cells[i],
17529                 end : cells[i]
17530             };
17531             
17532         }
17533         
17534         rows.push(crow);
17535         ev.els = [];
17536         ev.rows = rows;
17537         ev.cells = cells;
17538         
17539         cells[0].events.push(ev);
17540         
17541         this.calevents.push(ev);
17542     },
17543     
17544     clearEvents: function() {
17545         
17546         if(!this.calevents){
17547             return;
17548         }
17549         
17550         Roo.each(this.cells.elements, function(c){
17551             c.row = 0;
17552             c.events = [];
17553             c.more = [];
17554         });
17555         
17556         Roo.each(this.calevents, function(e) {
17557             Roo.each(e.els, function(el) {
17558                 el.un('mouseenter' ,this.onEventEnter, this);
17559                 el.un('mouseleave' ,this.onEventLeave, this);
17560                 el.remove();
17561             },this);
17562         },this);
17563         
17564         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17565             e.remove();
17566         });
17567         
17568     },
17569     
17570     renderEvents: function()
17571     {   
17572         var _this = this;
17573         
17574         this.cells.each(function(c) {
17575             
17576             if(c.row < 5){
17577                 return;
17578             }
17579             
17580             var ev = c.events;
17581             
17582             var r = 4;
17583             if(c.row != c.events.length){
17584                 r = 4 - (4 - (c.row - c.events.length));
17585             }
17586             
17587             c.events = ev.slice(0, r);
17588             c.more = ev.slice(r);
17589             
17590             if(c.more.length && c.more.length == 1){
17591                 c.events.push(c.more.pop());
17592             }
17593             
17594             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17595             
17596         });
17597             
17598         this.cells.each(function(c) {
17599             
17600             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17601             
17602             
17603             for (var e = 0; e < c.events.length; e++){
17604                 var ev = c.events[e];
17605                 var rows = ev.rows;
17606                 
17607                 for(var i = 0; i < rows.length; i++) {
17608                 
17609                     // how many rows should it span..
17610
17611                     var  cfg = {
17612                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17613                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17614
17615                         unselectable : "on",
17616                         cn : [
17617                             {
17618                                 cls: 'fc-event-inner',
17619                                 cn : [
17620     //                                {
17621     //                                  tag:'span',
17622     //                                  cls: 'fc-event-time',
17623     //                                  html : cells.length > 1 ? '' : ev.time
17624     //                                },
17625                                     {
17626                                       tag:'span',
17627                                       cls: 'fc-event-title',
17628                                       html : String.format('{0}', ev.title)
17629                                     }
17630
17631
17632                                 ]
17633                             },
17634                             {
17635                                 cls: 'ui-resizable-handle ui-resizable-e',
17636                                 html : '&nbsp;&nbsp;&nbsp'
17637                             }
17638
17639                         ]
17640                     };
17641
17642                     if (i == 0) {
17643                         cfg.cls += ' fc-event-start';
17644                     }
17645                     if ((i+1) == rows.length) {
17646                         cfg.cls += ' fc-event-end';
17647                     }
17648
17649                     var ctr = _this.el.select('.fc-event-container',true).first();
17650                     var cg = ctr.createChild(cfg);
17651
17652                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17653                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17654
17655                     var r = (c.more.length) ? 1 : 0;
17656                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17657                     cg.setWidth(ebox.right - sbox.x -2);
17658
17659                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17660                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17661                     cg.on('click', _this.onEventClick, _this, ev);
17662
17663                     ev.els.push(cg);
17664                     
17665                 }
17666                 
17667             }
17668             
17669             
17670             if(c.more.length){
17671                 var  cfg = {
17672                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17673                     style : 'position: absolute',
17674                     unselectable : "on",
17675                     cn : [
17676                         {
17677                             cls: 'fc-event-inner',
17678                             cn : [
17679                                 {
17680                                   tag:'span',
17681                                   cls: 'fc-event-title',
17682                                   html : 'More'
17683                                 }
17684
17685
17686                             ]
17687                         },
17688                         {
17689                             cls: 'ui-resizable-handle ui-resizable-e',
17690                             html : '&nbsp;&nbsp;&nbsp'
17691                         }
17692
17693                     ]
17694                 };
17695
17696                 var ctr = _this.el.select('.fc-event-container',true).first();
17697                 var cg = ctr.createChild(cfg);
17698
17699                 var sbox = c.select('.fc-day-content',true).first().getBox();
17700                 var ebox = c.select('.fc-day-content',true).first().getBox();
17701                 //Roo.log(cg);
17702                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17703                 cg.setWidth(ebox.right - sbox.x -2);
17704
17705                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17706                 
17707             }
17708             
17709         });
17710         
17711         
17712         
17713     },
17714     
17715     onEventEnter: function (e, el,event,d) {
17716         this.fireEvent('evententer', this, el, event);
17717     },
17718     
17719     onEventLeave: function (e, el,event,d) {
17720         this.fireEvent('eventleave', this, el, event);
17721     },
17722     
17723     onEventClick: function (e, el,event,d) {
17724         this.fireEvent('eventclick', this, el, event);
17725     },
17726     
17727     onMonthChange: function () {
17728         this.store.load();
17729     },
17730     
17731     onMoreEventClick: function(e, el, more)
17732     {
17733         var _this = this;
17734         
17735         this.calpopover.placement = 'right';
17736         this.calpopover.setTitle('More');
17737         
17738         this.calpopover.setContent('');
17739         
17740         var ctr = this.calpopover.el.select('.popover-content', true).first();
17741         
17742         Roo.each(more, function(m){
17743             var cfg = {
17744                 cls : 'fc-event-hori fc-event-draggable',
17745                 html : m.title
17746             };
17747             var cg = ctr.createChild(cfg);
17748             
17749             cg.on('click', _this.onEventClick, _this, m);
17750         });
17751         
17752         this.calpopover.show(el);
17753         
17754         
17755     },
17756     
17757     onLoad: function () 
17758     {   
17759         this.calevents = [];
17760         var cal = this;
17761         
17762         if(this.store.getCount() > 0){
17763             this.store.data.each(function(d){
17764                cal.addItem({
17765                     id : d.data.id,
17766                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17767                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17768                     time : d.data.start_time,
17769                     title : d.data.title,
17770                     description : d.data.description,
17771                     venue : d.data.venue
17772                 });
17773             });
17774         }
17775         
17776         this.renderEvents();
17777         
17778         if(this.calevents.length && this.loadMask){
17779             this.maskEl.hide();
17780         }
17781     },
17782     
17783     onBeforeLoad: function()
17784     {
17785         this.clearEvents();
17786         if(this.loadMask){
17787             this.maskEl.show();
17788         }
17789     }
17790 });
17791
17792  
17793  /*
17794  * - LGPL
17795  *
17796  * element
17797  * 
17798  */
17799
17800 /**
17801  * @class Roo.bootstrap.Popover
17802  * @extends Roo.bootstrap.Component
17803  * Bootstrap Popover class
17804  * @cfg {String} html contents of the popover   (or false to use children..)
17805  * @cfg {String} title of popover (or false to hide)
17806  * @cfg {String} placement how it is placed
17807  * @cfg {String} trigger click || hover (or false to trigger manually)
17808  * @cfg {String} over what (parent or false to trigger manually.)
17809  * @cfg {Number} delay - delay before showing
17810  
17811  * @constructor
17812  * Create a new Popover
17813  * @param {Object} config The config object
17814  */
17815
17816 Roo.bootstrap.Popover = function(config){
17817     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17818     
17819     this.addEvents({
17820         // raw events
17821          /**
17822          * @event show
17823          * After the popover show
17824          * 
17825          * @param {Roo.bootstrap.Popover} this
17826          */
17827         "show" : true,
17828         /**
17829          * @event hide
17830          * After the popover hide
17831          * 
17832          * @param {Roo.bootstrap.Popover} this
17833          */
17834         "hide" : true
17835     });
17836 };
17837
17838 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17839     
17840     title: 'Fill in a title',
17841     html: false,
17842     
17843     placement : 'right',
17844     trigger : 'hover', // hover
17845     
17846     delay : 0,
17847     
17848     over: 'parent',
17849     
17850     can_build_overlaid : false,
17851     
17852     getChildContainer : function()
17853     {
17854         return this.el.select('.popover-content',true).first();
17855     },
17856     
17857     getAutoCreate : function(){
17858          
17859         var cfg = {
17860            cls : 'popover roo-dynamic',
17861            style: 'display:block',
17862            cn : [
17863                 {
17864                     cls : 'arrow'
17865                 },
17866                 {
17867                     cls : 'popover-inner',
17868                     cn : [
17869                         {
17870                             tag: 'h3',
17871                             cls: 'popover-title popover-header',
17872                             html : this.title
17873                         },
17874                         {
17875                             cls : 'popover-content popover-body',
17876                             html : this.html
17877                         }
17878                     ]
17879                     
17880                 }
17881            ]
17882         };
17883         
17884         return cfg;
17885     },
17886     setTitle: function(str)
17887     {
17888         this.title = str;
17889         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17890     },
17891     setContent: function(str)
17892     {
17893         this.html = str;
17894         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17895     },
17896     // as it get's added to the bottom of the page.
17897     onRender : function(ct, position)
17898     {
17899         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17900         if(!this.el){
17901             var cfg = Roo.apply({},  this.getAutoCreate());
17902             cfg.id = Roo.id();
17903             
17904             if (this.cls) {
17905                 cfg.cls += ' ' + this.cls;
17906             }
17907             if (this.style) {
17908                 cfg.style = this.style;
17909             }
17910             //Roo.log("adding to ");
17911             this.el = Roo.get(document.body).createChild(cfg, position);
17912 //            Roo.log(this.el);
17913         }
17914         this.initEvents();
17915     },
17916     
17917     initEvents : function()
17918     {
17919         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17920         this.el.enableDisplayMode('block');
17921         this.el.hide();
17922         if (this.over === false) {
17923             return; 
17924         }
17925         if (this.triggers === false) {
17926             return;
17927         }
17928         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17929         var triggers = this.trigger ? this.trigger.split(' ') : [];
17930         Roo.each(triggers, function(trigger) {
17931         
17932             if (trigger == 'click') {
17933                 on_el.on('click', this.toggle, this);
17934             } else if (trigger != 'manual') {
17935                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17936                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17937       
17938                 on_el.on(eventIn  ,this.enter, this);
17939                 on_el.on(eventOut, this.leave, this);
17940             }
17941         }, this);
17942         
17943     },
17944     
17945     
17946     // private
17947     timeout : null,
17948     hoverState : null,
17949     
17950     toggle : function () {
17951         this.hoverState == 'in' ? this.leave() : this.enter();
17952     },
17953     
17954     enter : function () {
17955         
17956         clearTimeout(this.timeout);
17957     
17958         this.hoverState = 'in';
17959     
17960         if (!this.delay || !this.delay.show) {
17961             this.show();
17962             return;
17963         }
17964         var _t = this;
17965         this.timeout = setTimeout(function () {
17966             if (_t.hoverState == 'in') {
17967                 _t.show();
17968             }
17969         }, this.delay.show)
17970     },
17971     
17972     leave : function() {
17973         clearTimeout(this.timeout);
17974     
17975         this.hoverState = 'out';
17976     
17977         if (!this.delay || !this.delay.hide) {
17978             this.hide();
17979             return;
17980         }
17981         var _t = this;
17982         this.timeout = setTimeout(function () {
17983             if (_t.hoverState == 'out') {
17984                 _t.hide();
17985             }
17986         }, this.delay.hide)
17987     },
17988     
17989     show : function (on_el)
17990     {
17991         if (!on_el) {
17992             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17993         }
17994         
17995         // set content.
17996         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17997         if (this.html !== false) {
17998             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17999         }
18000         this.el.removeClass([
18001             'fade','top','bottom', 'left', 'right','in',
18002             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18003         ]);
18004         if (!this.title.length) {
18005             this.el.select('.popover-title',true).hide();
18006         }
18007         
18008         var placement = typeof this.placement == 'function' ?
18009             this.placement.call(this, this.el, on_el) :
18010             this.placement;
18011             
18012         var autoToken = /\s?auto?\s?/i;
18013         var autoPlace = autoToken.test(placement);
18014         if (autoPlace) {
18015             placement = placement.replace(autoToken, '') || 'top';
18016         }
18017         
18018         //this.el.detach()
18019         //this.el.setXY([0,0]);
18020         this.el.show();
18021         this.el.dom.style.display='block';
18022         this.el.addClass(placement);
18023         
18024         //this.el.appendTo(on_el);
18025         
18026         var p = this.getPosition();
18027         var box = this.el.getBox();
18028         
18029         if (autoPlace) {
18030             // fixme..
18031         }
18032         var align = Roo.bootstrap.Popover.alignment[placement];
18033         
18034 //        Roo.log(align);
18035         this.el.alignTo(on_el, align[0],align[1]);
18036         //var arrow = this.el.select('.arrow',true).first();
18037         //arrow.set(align[2], 
18038         
18039         this.el.addClass('in');
18040         
18041         
18042         if (this.el.hasClass('fade')) {
18043             // fade it?
18044         }
18045         
18046         this.hoverState = 'in';
18047         
18048         this.fireEvent('show', this);
18049         
18050     },
18051     hide : function()
18052     {
18053         this.el.setXY([0,0]);
18054         this.el.removeClass('in');
18055         this.el.hide();
18056         this.hoverState = null;
18057         
18058         this.fireEvent('hide', this);
18059     }
18060     
18061 });
18062
18063 Roo.bootstrap.Popover.alignment = {
18064     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18065     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18066     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18067     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18068 };
18069
18070  /*
18071  * - LGPL
18072  *
18073  * Progress
18074  * 
18075  */
18076
18077 /**
18078  * @class Roo.bootstrap.Progress
18079  * @extends Roo.bootstrap.Component
18080  * Bootstrap Progress class
18081  * @cfg {Boolean} striped striped of the progress bar
18082  * @cfg {Boolean} active animated of the progress bar
18083  * 
18084  * 
18085  * @constructor
18086  * Create a new Progress
18087  * @param {Object} config The config object
18088  */
18089
18090 Roo.bootstrap.Progress = function(config){
18091     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18092 };
18093
18094 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18095     
18096     striped : false,
18097     active: false,
18098     
18099     getAutoCreate : function(){
18100         var cfg = {
18101             tag: 'div',
18102             cls: 'progress'
18103         };
18104         
18105         
18106         if(this.striped){
18107             cfg.cls += ' progress-striped';
18108         }
18109       
18110         if(this.active){
18111             cfg.cls += ' active';
18112         }
18113         
18114         
18115         return cfg;
18116     }
18117    
18118 });
18119
18120  
18121
18122  /*
18123  * - LGPL
18124  *
18125  * ProgressBar
18126  * 
18127  */
18128
18129 /**
18130  * @class Roo.bootstrap.ProgressBar
18131  * @extends Roo.bootstrap.Component
18132  * Bootstrap ProgressBar class
18133  * @cfg {Number} aria_valuenow aria-value now
18134  * @cfg {Number} aria_valuemin aria-value min
18135  * @cfg {Number} aria_valuemax aria-value max
18136  * @cfg {String} label label for the progress bar
18137  * @cfg {String} panel (success | info | warning | danger )
18138  * @cfg {String} role role of the progress bar
18139  * @cfg {String} sr_only text
18140  * 
18141  * 
18142  * @constructor
18143  * Create a new ProgressBar
18144  * @param {Object} config The config object
18145  */
18146
18147 Roo.bootstrap.ProgressBar = function(config){
18148     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18149 };
18150
18151 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18152     
18153     aria_valuenow : 0,
18154     aria_valuemin : 0,
18155     aria_valuemax : 100,
18156     label : false,
18157     panel : false,
18158     role : false,
18159     sr_only: false,
18160     
18161     getAutoCreate : function()
18162     {
18163         
18164         var cfg = {
18165             tag: 'div',
18166             cls: 'progress-bar',
18167             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18168         };
18169         
18170         if(this.sr_only){
18171             cfg.cn = {
18172                 tag: 'span',
18173                 cls: 'sr-only',
18174                 html: this.sr_only
18175             }
18176         }
18177         
18178         if(this.role){
18179             cfg.role = this.role;
18180         }
18181         
18182         if(this.aria_valuenow){
18183             cfg['aria-valuenow'] = this.aria_valuenow;
18184         }
18185         
18186         if(this.aria_valuemin){
18187             cfg['aria-valuemin'] = this.aria_valuemin;
18188         }
18189         
18190         if(this.aria_valuemax){
18191             cfg['aria-valuemax'] = this.aria_valuemax;
18192         }
18193         
18194         if(this.label && !this.sr_only){
18195             cfg.html = this.label;
18196         }
18197         
18198         if(this.panel){
18199             cfg.cls += ' progress-bar-' + this.panel;
18200         }
18201         
18202         return cfg;
18203     },
18204     
18205     update : function(aria_valuenow)
18206     {
18207         this.aria_valuenow = aria_valuenow;
18208         
18209         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18210     }
18211    
18212 });
18213
18214  
18215
18216  /*
18217  * - LGPL
18218  *
18219  * column
18220  * 
18221  */
18222
18223 /**
18224  * @class Roo.bootstrap.TabGroup
18225  * @extends Roo.bootstrap.Column
18226  * Bootstrap Column class
18227  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18228  * @cfg {Boolean} carousel true to make the group behave like a carousel
18229  * @cfg {Boolean} bullets show bullets for the panels
18230  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18231  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18232  * @cfg {Boolean} showarrow (true|false) show arrow default true
18233  * 
18234  * @constructor
18235  * Create a new TabGroup
18236  * @param {Object} config The config object
18237  */
18238
18239 Roo.bootstrap.TabGroup = function(config){
18240     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18241     if (!this.navId) {
18242         this.navId = Roo.id();
18243     }
18244     this.tabs = [];
18245     Roo.bootstrap.TabGroup.register(this);
18246     
18247 };
18248
18249 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18250     
18251     carousel : false,
18252     transition : false,
18253     bullets : 0,
18254     timer : 0,
18255     autoslide : false,
18256     slideFn : false,
18257     slideOnTouch : false,
18258     showarrow : true,
18259     
18260     getAutoCreate : function()
18261     {
18262         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18263         
18264         cfg.cls += ' tab-content';
18265         
18266         if (this.carousel) {
18267             cfg.cls += ' carousel slide';
18268             
18269             cfg.cn = [{
18270                cls : 'carousel-inner',
18271                cn : []
18272             }];
18273         
18274             if(this.bullets  && !Roo.isTouch){
18275                 
18276                 var bullets = {
18277                     cls : 'carousel-bullets',
18278                     cn : []
18279                 };
18280                
18281                 if(this.bullets_cls){
18282                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18283                 }
18284                 
18285                 bullets.cn.push({
18286                     cls : 'clear'
18287                 });
18288                 
18289                 cfg.cn[0].cn.push(bullets);
18290             }
18291             
18292             if(this.showarrow){
18293                 cfg.cn[0].cn.push({
18294                     tag : 'div',
18295                     class : 'carousel-arrow',
18296                     cn : [
18297                         {
18298                             tag : 'div',
18299                             class : 'carousel-prev',
18300                             cn : [
18301                                 {
18302                                     tag : 'i',
18303                                     class : 'fa fa-chevron-left'
18304                                 }
18305                             ]
18306                         },
18307                         {
18308                             tag : 'div',
18309                             class : 'carousel-next',
18310                             cn : [
18311                                 {
18312                                     tag : 'i',
18313                                     class : 'fa fa-chevron-right'
18314                                 }
18315                             ]
18316                         }
18317                     ]
18318                 });
18319             }
18320             
18321         }
18322         
18323         return cfg;
18324     },
18325     
18326     initEvents:  function()
18327     {
18328 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18329 //            this.el.on("touchstart", this.onTouchStart, this);
18330 //        }
18331         
18332         if(this.autoslide){
18333             var _this = this;
18334             
18335             this.slideFn = window.setInterval(function() {
18336                 _this.showPanelNext();
18337             }, this.timer);
18338         }
18339         
18340         if(this.showarrow){
18341             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18342             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18343         }
18344         
18345         
18346     },
18347     
18348 //    onTouchStart : function(e, el, o)
18349 //    {
18350 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18351 //            return;
18352 //        }
18353 //        
18354 //        this.showPanelNext();
18355 //    },
18356     
18357     
18358     getChildContainer : function()
18359     {
18360         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18361     },
18362     
18363     /**
18364     * register a Navigation item
18365     * @param {Roo.bootstrap.NavItem} the navitem to add
18366     */
18367     register : function(item)
18368     {
18369         this.tabs.push( item);
18370         item.navId = this.navId; // not really needed..
18371         this.addBullet();
18372     
18373     },
18374     
18375     getActivePanel : function()
18376     {
18377         var r = false;
18378         Roo.each(this.tabs, function(t) {
18379             if (t.active) {
18380                 r = t;
18381                 return false;
18382             }
18383             return null;
18384         });
18385         return r;
18386         
18387     },
18388     getPanelByName : function(n)
18389     {
18390         var r = false;
18391         Roo.each(this.tabs, function(t) {
18392             if (t.tabId == n) {
18393                 r = t;
18394                 return false;
18395             }
18396             return null;
18397         });
18398         return r;
18399     },
18400     indexOfPanel : function(p)
18401     {
18402         var r = false;
18403         Roo.each(this.tabs, function(t,i) {
18404             if (t.tabId == p.tabId) {
18405                 r = i;
18406                 return false;
18407             }
18408             return null;
18409         });
18410         return r;
18411     },
18412     /**
18413      * show a specific panel
18414      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18415      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18416      */
18417     showPanel : function (pan)
18418     {
18419         if(this.transition || typeof(pan) == 'undefined'){
18420             Roo.log("waiting for the transitionend");
18421             return false;
18422         }
18423         
18424         if (typeof(pan) == 'number') {
18425             pan = this.tabs[pan];
18426         }
18427         
18428         if (typeof(pan) == 'string') {
18429             pan = this.getPanelByName(pan);
18430         }
18431         
18432         var cur = this.getActivePanel();
18433         
18434         if(!pan || !cur){
18435             Roo.log('pan or acitve pan is undefined');
18436             return false;
18437         }
18438         
18439         if (pan.tabId == this.getActivePanel().tabId) {
18440             return true;
18441         }
18442         
18443         if (false === cur.fireEvent('beforedeactivate')) {
18444             return false;
18445         }
18446         
18447         if(this.bullets > 0 && !Roo.isTouch){
18448             this.setActiveBullet(this.indexOfPanel(pan));
18449         }
18450         
18451         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18452             
18453             //class="carousel-item carousel-item-next carousel-item-left"
18454             
18455             this.transition = true;
18456             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18457             var lr = dir == 'next' ? 'left' : 'right';
18458             pan.el.addClass(dir); // or prev
18459             pan.el.addClass('carousel-item-' + dir); // or prev
18460             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18461             cur.el.addClass(lr); // or right
18462             pan.el.addClass(lr);
18463             cur.el.addClass('carousel-item-' +lr); // or right
18464             pan.el.addClass('carousel-item-' +lr);
18465             
18466             
18467             var _this = this;
18468             cur.el.on('transitionend', function() {
18469                 Roo.log("trans end?");
18470                 
18471                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18472                 pan.setActive(true);
18473                 
18474                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18475                 cur.setActive(false);
18476                 
18477                 _this.transition = false;
18478                 
18479             }, this, { single:  true } );
18480             
18481             return true;
18482         }
18483         
18484         cur.setActive(false);
18485         pan.setActive(true);
18486         
18487         return true;
18488         
18489     },
18490     showPanelNext : function()
18491     {
18492         var i = this.indexOfPanel(this.getActivePanel());
18493         
18494         if (i >= this.tabs.length - 1 && !this.autoslide) {
18495             return;
18496         }
18497         
18498         if (i >= this.tabs.length - 1 && this.autoslide) {
18499             i = -1;
18500         }
18501         
18502         this.showPanel(this.tabs[i+1]);
18503     },
18504     
18505     showPanelPrev : function()
18506     {
18507         var i = this.indexOfPanel(this.getActivePanel());
18508         
18509         if (i  < 1 && !this.autoslide) {
18510             return;
18511         }
18512         
18513         if (i < 1 && this.autoslide) {
18514             i = this.tabs.length;
18515         }
18516         
18517         this.showPanel(this.tabs[i-1]);
18518     },
18519     
18520     
18521     addBullet: function()
18522     {
18523         if(!this.bullets || Roo.isTouch){
18524             return;
18525         }
18526         var ctr = this.el.select('.carousel-bullets',true).first();
18527         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18528         var bullet = ctr.createChild({
18529             cls : 'bullet bullet-' + i
18530         },ctr.dom.lastChild);
18531         
18532         
18533         var _this = this;
18534         
18535         bullet.on('click', (function(e, el, o, ii, t){
18536
18537             e.preventDefault();
18538
18539             this.showPanel(ii);
18540
18541             if(this.autoslide && this.slideFn){
18542                 clearInterval(this.slideFn);
18543                 this.slideFn = window.setInterval(function() {
18544                     _this.showPanelNext();
18545                 }, this.timer);
18546             }
18547
18548         }).createDelegate(this, [i, bullet], true));
18549                 
18550         
18551     },
18552      
18553     setActiveBullet : function(i)
18554     {
18555         if(Roo.isTouch){
18556             return;
18557         }
18558         
18559         Roo.each(this.el.select('.bullet', true).elements, function(el){
18560             el.removeClass('selected');
18561         });
18562
18563         var bullet = this.el.select('.bullet-' + i, true).first();
18564         
18565         if(!bullet){
18566             return;
18567         }
18568         
18569         bullet.addClass('selected');
18570     }
18571     
18572     
18573   
18574 });
18575
18576  
18577
18578  
18579  
18580 Roo.apply(Roo.bootstrap.TabGroup, {
18581     
18582     groups: {},
18583      /**
18584     * register a Navigation Group
18585     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18586     */
18587     register : function(navgrp)
18588     {
18589         this.groups[navgrp.navId] = navgrp;
18590         
18591     },
18592     /**
18593     * fetch a Navigation Group based on the navigation ID
18594     * if one does not exist , it will get created.
18595     * @param {string} the navgroup to add
18596     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18597     */
18598     get: function(navId) {
18599         if (typeof(this.groups[navId]) == 'undefined') {
18600             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18601         }
18602         return this.groups[navId] ;
18603     }
18604     
18605     
18606     
18607 });
18608
18609  /*
18610  * - LGPL
18611  *
18612  * TabPanel
18613  * 
18614  */
18615
18616 /**
18617  * @class Roo.bootstrap.TabPanel
18618  * @extends Roo.bootstrap.Component
18619  * Bootstrap TabPanel class
18620  * @cfg {Boolean} active panel active
18621  * @cfg {String} html panel content
18622  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18623  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18624  * @cfg {String} href click to link..
18625  * 
18626  * 
18627  * @constructor
18628  * Create a new TabPanel
18629  * @param {Object} config The config object
18630  */
18631
18632 Roo.bootstrap.TabPanel = function(config){
18633     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18634     this.addEvents({
18635         /**
18636              * @event changed
18637              * Fires when the active status changes
18638              * @param {Roo.bootstrap.TabPanel} this
18639              * @param {Boolean} state the new state
18640             
18641          */
18642         'changed': true,
18643         /**
18644              * @event beforedeactivate
18645              * Fires before a tab is de-activated - can be used to do validation on a form.
18646              * @param {Roo.bootstrap.TabPanel} this
18647              * @return {Boolean} false if there is an error
18648             
18649          */
18650         'beforedeactivate': true
18651      });
18652     
18653     this.tabId = this.tabId || Roo.id();
18654   
18655 };
18656
18657 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18658     
18659     active: false,
18660     html: false,
18661     tabId: false,
18662     navId : false,
18663     href : '',
18664     
18665     getAutoCreate : function(){
18666         
18667         
18668         var cfg = {
18669             tag: 'div',
18670             // item is needed for carousel - not sure if it has any effect otherwise
18671             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18672             html: this.html || ''
18673         };
18674         
18675         if(this.active){
18676             cfg.cls += ' active';
18677         }
18678         
18679         if(this.tabId){
18680             cfg.tabId = this.tabId;
18681         }
18682         
18683         
18684         
18685         return cfg;
18686     },
18687     
18688     initEvents:  function()
18689     {
18690         var p = this.parent();
18691         
18692         this.navId = this.navId || p.navId;
18693         
18694         if (typeof(this.navId) != 'undefined') {
18695             // not really needed.. but just in case.. parent should be a NavGroup.
18696             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18697             
18698             tg.register(this);
18699             
18700             var i = tg.tabs.length - 1;
18701             
18702             if(this.active && tg.bullets > 0 && i < tg.bullets){
18703                 tg.setActiveBullet(i);
18704             }
18705         }
18706         
18707         this.el.on('click', this.onClick, this);
18708         
18709         if(Roo.isTouch){
18710             this.el.on("touchstart", this.onTouchStart, this);
18711             this.el.on("touchmove", this.onTouchMove, this);
18712             this.el.on("touchend", this.onTouchEnd, this);
18713         }
18714         
18715     },
18716     
18717     onRender : function(ct, position)
18718     {
18719         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18720     },
18721     
18722     setActive : function(state)
18723     {
18724         Roo.log("panel - set active " + this.tabId + "=" + state);
18725         
18726         this.active = state;
18727         if (!state) {
18728             this.el.removeClass('active');
18729             
18730         } else  if (!this.el.hasClass('active')) {
18731             this.el.addClass('active');
18732         }
18733         
18734         this.fireEvent('changed', this, state);
18735     },
18736     
18737     onClick : function(e)
18738     {
18739         e.preventDefault();
18740         
18741         if(!this.href.length){
18742             return;
18743         }
18744         
18745         window.location.href = this.href;
18746     },
18747     
18748     startX : 0,
18749     startY : 0,
18750     endX : 0,
18751     endY : 0,
18752     swiping : false,
18753     
18754     onTouchStart : function(e)
18755     {
18756         this.swiping = false;
18757         
18758         this.startX = e.browserEvent.touches[0].clientX;
18759         this.startY = e.browserEvent.touches[0].clientY;
18760     },
18761     
18762     onTouchMove : function(e)
18763     {
18764         this.swiping = true;
18765         
18766         this.endX = e.browserEvent.touches[0].clientX;
18767         this.endY = e.browserEvent.touches[0].clientY;
18768     },
18769     
18770     onTouchEnd : function(e)
18771     {
18772         if(!this.swiping){
18773             this.onClick(e);
18774             return;
18775         }
18776         
18777         var tabGroup = this.parent();
18778         
18779         if(this.endX > this.startX){ // swiping right
18780             tabGroup.showPanelPrev();
18781             return;
18782         }
18783         
18784         if(this.startX > this.endX){ // swiping left
18785             tabGroup.showPanelNext();
18786             return;
18787         }
18788     }
18789     
18790     
18791 });
18792  
18793
18794  
18795
18796  /*
18797  * - LGPL
18798  *
18799  * DateField
18800  * 
18801  */
18802
18803 /**
18804  * @class Roo.bootstrap.DateField
18805  * @extends Roo.bootstrap.Input
18806  * Bootstrap DateField class
18807  * @cfg {Number} weekStart default 0
18808  * @cfg {String} viewMode default empty, (months|years)
18809  * @cfg {String} minViewMode default empty, (months|years)
18810  * @cfg {Number} startDate default -Infinity
18811  * @cfg {Number} endDate default Infinity
18812  * @cfg {Boolean} todayHighlight default false
18813  * @cfg {Boolean} todayBtn default false
18814  * @cfg {Boolean} calendarWeeks default false
18815  * @cfg {Object} daysOfWeekDisabled default empty
18816  * @cfg {Boolean} singleMode default false (true | false)
18817  * 
18818  * @cfg {Boolean} keyboardNavigation default true
18819  * @cfg {String} language default en
18820  * 
18821  * @constructor
18822  * Create a new DateField
18823  * @param {Object} config The config object
18824  */
18825
18826 Roo.bootstrap.DateField = function(config){
18827     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18828      this.addEvents({
18829             /**
18830              * @event show
18831              * Fires when this field show.
18832              * @param {Roo.bootstrap.DateField} this
18833              * @param {Mixed} date The date value
18834              */
18835             show : true,
18836             /**
18837              * @event show
18838              * Fires when this field hide.
18839              * @param {Roo.bootstrap.DateField} this
18840              * @param {Mixed} date The date value
18841              */
18842             hide : true,
18843             /**
18844              * @event select
18845              * Fires when select a date.
18846              * @param {Roo.bootstrap.DateField} this
18847              * @param {Mixed} date The date value
18848              */
18849             select : true,
18850             /**
18851              * @event beforeselect
18852              * Fires when before select a date.
18853              * @param {Roo.bootstrap.DateField} this
18854              * @param {Mixed} date The date value
18855              */
18856             beforeselect : true
18857         });
18858 };
18859
18860 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18861     
18862     /**
18863      * @cfg {String} format
18864      * The default date format string which can be overriden for localization support.  The format must be
18865      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18866      */
18867     format : "m/d/y",
18868     /**
18869      * @cfg {String} altFormats
18870      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18871      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18872      */
18873     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18874     
18875     weekStart : 0,
18876     
18877     viewMode : '',
18878     
18879     minViewMode : '',
18880     
18881     todayHighlight : false,
18882     
18883     todayBtn: false,
18884     
18885     language: 'en',
18886     
18887     keyboardNavigation: true,
18888     
18889     calendarWeeks: false,
18890     
18891     startDate: -Infinity,
18892     
18893     endDate: Infinity,
18894     
18895     daysOfWeekDisabled: [],
18896     
18897     _events: [],
18898     
18899     singleMode : false,
18900     
18901     UTCDate: function()
18902     {
18903         return new Date(Date.UTC.apply(Date, arguments));
18904     },
18905     
18906     UTCToday: function()
18907     {
18908         var today = new Date();
18909         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18910     },
18911     
18912     getDate: function() {
18913             var d = this.getUTCDate();
18914             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18915     },
18916     
18917     getUTCDate: function() {
18918             return this.date;
18919     },
18920     
18921     setDate: function(d) {
18922             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18923     },
18924     
18925     setUTCDate: function(d) {
18926             this.date = d;
18927             this.setValue(this.formatDate(this.date));
18928     },
18929         
18930     onRender: function(ct, position)
18931     {
18932         
18933         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18934         
18935         this.language = this.language || 'en';
18936         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18937         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18938         
18939         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18940         this.format = this.format || 'm/d/y';
18941         this.isInline = false;
18942         this.isInput = true;
18943         this.component = this.el.select('.add-on', true).first() || false;
18944         this.component = (this.component && this.component.length === 0) ? false : this.component;
18945         this.hasInput = this.component && this.inputEl().length;
18946         
18947         if (typeof(this.minViewMode === 'string')) {
18948             switch (this.minViewMode) {
18949                 case 'months':
18950                     this.minViewMode = 1;
18951                     break;
18952                 case 'years':
18953                     this.minViewMode = 2;
18954                     break;
18955                 default:
18956                     this.minViewMode = 0;
18957                     break;
18958             }
18959         }
18960         
18961         if (typeof(this.viewMode === 'string')) {
18962             switch (this.viewMode) {
18963                 case 'months':
18964                     this.viewMode = 1;
18965                     break;
18966                 case 'years':
18967                     this.viewMode = 2;
18968                     break;
18969                 default:
18970                     this.viewMode = 0;
18971                     break;
18972             }
18973         }
18974                 
18975         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18976         
18977 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18978         
18979         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18980         
18981         this.picker().on('mousedown', this.onMousedown, this);
18982         this.picker().on('click', this.onClick, this);
18983         
18984         this.picker().addClass('datepicker-dropdown');
18985         
18986         this.startViewMode = this.viewMode;
18987         
18988         if(this.singleMode){
18989             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18990                 v.setVisibilityMode(Roo.Element.DISPLAY);
18991                 v.hide();
18992             });
18993             
18994             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18995                 v.setStyle('width', '189px');
18996             });
18997         }
18998         
18999         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19000             if(!this.calendarWeeks){
19001                 v.remove();
19002                 return;
19003             }
19004             
19005             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19006             v.attr('colspan', function(i, val){
19007                 return parseInt(val) + 1;
19008             });
19009         });
19010                         
19011         
19012         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19013         
19014         this.setStartDate(this.startDate);
19015         this.setEndDate(this.endDate);
19016         
19017         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19018         
19019         this.fillDow();
19020         this.fillMonths();
19021         this.update();
19022         this.showMode();
19023         
19024         if(this.isInline) {
19025             this.showPopup();
19026         }
19027     },
19028     
19029     picker : function()
19030     {
19031         return this.pickerEl;
19032 //        return this.el.select('.datepicker', true).first();
19033     },
19034     
19035     fillDow: function()
19036     {
19037         var dowCnt = this.weekStart;
19038         
19039         var dow = {
19040             tag: 'tr',
19041             cn: [
19042                 
19043             ]
19044         };
19045         
19046         if(this.calendarWeeks){
19047             dow.cn.push({
19048                 tag: 'th',
19049                 cls: 'cw',
19050                 html: '&nbsp;'
19051             })
19052         }
19053         
19054         while (dowCnt < this.weekStart + 7) {
19055             dow.cn.push({
19056                 tag: 'th',
19057                 cls: 'dow',
19058                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19059             });
19060         }
19061         
19062         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19063     },
19064     
19065     fillMonths: function()
19066     {    
19067         var i = 0;
19068         var months = this.picker().select('>.datepicker-months td', true).first();
19069         
19070         months.dom.innerHTML = '';
19071         
19072         while (i < 12) {
19073             var month = {
19074                 tag: 'span',
19075                 cls: 'month',
19076                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19077             };
19078             
19079             months.createChild(month);
19080         }
19081         
19082     },
19083     
19084     update: function()
19085     {
19086         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;
19087         
19088         if (this.date < this.startDate) {
19089             this.viewDate = new Date(this.startDate);
19090         } else if (this.date > this.endDate) {
19091             this.viewDate = new Date(this.endDate);
19092         } else {
19093             this.viewDate = new Date(this.date);
19094         }
19095         
19096         this.fill();
19097     },
19098     
19099     fill: function() 
19100     {
19101         var d = new Date(this.viewDate),
19102                 year = d.getUTCFullYear(),
19103                 month = d.getUTCMonth(),
19104                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19105                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19106                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19107                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19108                 currentDate = this.date && this.date.valueOf(),
19109                 today = this.UTCToday();
19110         
19111         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19112         
19113 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19114         
19115 //        this.picker.select('>tfoot th.today').
19116 //                                              .text(dates[this.language].today)
19117 //                                              .toggle(this.todayBtn !== false);
19118     
19119         this.updateNavArrows();
19120         this.fillMonths();
19121                                                 
19122         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19123         
19124         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19125          
19126         prevMonth.setUTCDate(day);
19127         
19128         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19129         
19130         var nextMonth = new Date(prevMonth);
19131         
19132         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19133         
19134         nextMonth = nextMonth.valueOf();
19135         
19136         var fillMonths = false;
19137         
19138         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19139         
19140         while(prevMonth.valueOf() <= nextMonth) {
19141             var clsName = '';
19142             
19143             if (prevMonth.getUTCDay() === this.weekStart) {
19144                 if(fillMonths){
19145                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19146                 }
19147                     
19148                 fillMonths = {
19149                     tag: 'tr',
19150                     cn: []
19151                 };
19152                 
19153                 if(this.calendarWeeks){
19154                     // ISO 8601: First week contains first thursday.
19155                     // ISO also states week starts on Monday, but we can be more abstract here.
19156                     var
19157                     // Start of current week: based on weekstart/current date
19158                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19159                     // Thursday of this week
19160                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19161                     // First Thursday of year, year from thursday
19162                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19163                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19164                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19165                     
19166                     fillMonths.cn.push({
19167                         tag: 'td',
19168                         cls: 'cw',
19169                         html: calWeek
19170                     });
19171                 }
19172             }
19173             
19174             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19175                 clsName += ' old';
19176             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19177                 clsName += ' new';
19178             }
19179             if (this.todayHighlight &&
19180                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19181                 prevMonth.getUTCMonth() == today.getMonth() &&
19182                 prevMonth.getUTCDate() == today.getDate()) {
19183                 clsName += ' today';
19184             }
19185             
19186             if (currentDate && prevMonth.valueOf() === currentDate) {
19187                 clsName += ' active';
19188             }
19189             
19190             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19191                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19192                     clsName += ' disabled';
19193             }
19194             
19195             fillMonths.cn.push({
19196                 tag: 'td',
19197                 cls: 'day ' + clsName,
19198                 html: prevMonth.getDate()
19199             });
19200             
19201             prevMonth.setDate(prevMonth.getDate()+1);
19202         }
19203           
19204         var currentYear = this.date && this.date.getUTCFullYear();
19205         var currentMonth = this.date && this.date.getUTCMonth();
19206         
19207         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19208         
19209         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19210             v.removeClass('active');
19211             
19212             if(currentYear === year && k === currentMonth){
19213                 v.addClass('active');
19214             }
19215             
19216             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19217                 v.addClass('disabled');
19218             }
19219             
19220         });
19221         
19222         
19223         year = parseInt(year/10, 10) * 10;
19224         
19225         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19226         
19227         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19228         
19229         year -= 1;
19230         for (var i = -1; i < 11; i++) {
19231             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19232                 tag: 'span',
19233                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19234                 html: year
19235             });
19236             
19237             year += 1;
19238         }
19239     },
19240     
19241     showMode: function(dir) 
19242     {
19243         if (dir) {
19244             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19245         }
19246         
19247         Roo.each(this.picker().select('>div',true).elements, function(v){
19248             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19249             v.hide();
19250         });
19251         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19252     },
19253     
19254     place: function()
19255     {
19256         if(this.isInline) {
19257             return;
19258         }
19259         
19260         this.picker().removeClass(['bottom', 'top']);
19261         
19262         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19263             /*
19264              * place to the top of element!
19265              *
19266              */
19267             
19268             this.picker().addClass('top');
19269             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19270             
19271             return;
19272         }
19273         
19274         this.picker().addClass('bottom');
19275         
19276         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19277     },
19278     
19279     parseDate : function(value)
19280     {
19281         if(!value || value instanceof Date){
19282             return value;
19283         }
19284         var v = Date.parseDate(value, this.format);
19285         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19286             v = Date.parseDate(value, 'Y-m-d');
19287         }
19288         if(!v && this.altFormats){
19289             if(!this.altFormatsArray){
19290                 this.altFormatsArray = this.altFormats.split("|");
19291             }
19292             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19293                 v = Date.parseDate(value, this.altFormatsArray[i]);
19294             }
19295         }
19296         return v;
19297     },
19298     
19299     formatDate : function(date, fmt)
19300     {   
19301         return (!date || !(date instanceof Date)) ?
19302         date : date.dateFormat(fmt || this.format);
19303     },
19304     
19305     onFocus : function()
19306     {
19307         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19308         this.showPopup();
19309     },
19310     
19311     onBlur : function()
19312     {
19313         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19314         
19315         var d = this.inputEl().getValue();
19316         
19317         this.setValue(d);
19318                 
19319         this.hidePopup();
19320     },
19321     
19322     showPopup : function()
19323     {
19324         this.picker().show();
19325         this.update();
19326         this.place();
19327         
19328         this.fireEvent('showpopup', this, this.date);
19329     },
19330     
19331     hidePopup : function()
19332     {
19333         if(this.isInline) {
19334             return;
19335         }
19336         this.picker().hide();
19337         this.viewMode = this.startViewMode;
19338         this.showMode();
19339         
19340         this.fireEvent('hidepopup', this, this.date);
19341         
19342     },
19343     
19344     onMousedown: function(e)
19345     {
19346         e.stopPropagation();
19347         e.preventDefault();
19348     },
19349     
19350     keyup: function(e)
19351     {
19352         Roo.bootstrap.DateField.superclass.keyup.call(this);
19353         this.update();
19354     },
19355
19356     setValue: function(v)
19357     {
19358         if(this.fireEvent('beforeselect', this, v) !== false){
19359             var d = new Date(this.parseDate(v) ).clearTime();
19360         
19361             if(isNaN(d.getTime())){
19362                 this.date = this.viewDate = '';
19363                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19364                 return;
19365             }
19366
19367             v = this.formatDate(d);
19368
19369             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19370
19371             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19372
19373             this.update();
19374
19375             this.fireEvent('select', this, this.date);
19376         }
19377     },
19378     
19379     getValue: function()
19380     {
19381         return this.formatDate(this.date);
19382     },
19383     
19384     fireKey: function(e)
19385     {
19386         if (!this.picker().isVisible()){
19387             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19388                 this.showPopup();
19389             }
19390             return;
19391         }
19392         
19393         var dateChanged = false,
19394         dir, day, month,
19395         newDate, newViewDate;
19396         
19397         switch(e.keyCode){
19398             case 27: // escape
19399                 this.hidePopup();
19400                 e.preventDefault();
19401                 break;
19402             case 37: // left
19403             case 39: // right
19404                 if (!this.keyboardNavigation) {
19405                     break;
19406                 }
19407                 dir = e.keyCode == 37 ? -1 : 1;
19408                 
19409                 if (e.ctrlKey){
19410                     newDate = this.moveYear(this.date, dir);
19411                     newViewDate = this.moveYear(this.viewDate, dir);
19412                 } else if (e.shiftKey){
19413                     newDate = this.moveMonth(this.date, dir);
19414                     newViewDate = this.moveMonth(this.viewDate, dir);
19415                 } else {
19416                     newDate = new Date(this.date);
19417                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19418                     newViewDate = new Date(this.viewDate);
19419                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19420                 }
19421                 if (this.dateWithinRange(newDate)){
19422                     this.date = newDate;
19423                     this.viewDate = newViewDate;
19424                     this.setValue(this.formatDate(this.date));
19425 //                    this.update();
19426                     e.preventDefault();
19427                     dateChanged = true;
19428                 }
19429                 break;
19430             case 38: // up
19431             case 40: // down
19432                 if (!this.keyboardNavigation) {
19433                     break;
19434                 }
19435                 dir = e.keyCode == 38 ? -1 : 1;
19436                 if (e.ctrlKey){
19437                     newDate = this.moveYear(this.date, dir);
19438                     newViewDate = this.moveYear(this.viewDate, dir);
19439                 } else if (e.shiftKey){
19440                     newDate = this.moveMonth(this.date, dir);
19441                     newViewDate = this.moveMonth(this.viewDate, dir);
19442                 } else {
19443                     newDate = new Date(this.date);
19444                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19445                     newViewDate = new Date(this.viewDate);
19446                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19447                 }
19448                 if (this.dateWithinRange(newDate)){
19449                     this.date = newDate;
19450                     this.viewDate = newViewDate;
19451                     this.setValue(this.formatDate(this.date));
19452 //                    this.update();
19453                     e.preventDefault();
19454                     dateChanged = true;
19455                 }
19456                 break;
19457             case 13: // enter
19458                 this.setValue(this.formatDate(this.date));
19459                 this.hidePopup();
19460                 e.preventDefault();
19461                 break;
19462             case 9: // tab
19463                 this.setValue(this.formatDate(this.date));
19464                 this.hidePopup();
19465                 break;
19466             case 16: // shift
19467             case 17: // ctrl
19468             case 18: // alt
19469                 break;
19470             default :
19471                 this.hidePopup();
19472                 
19473         }
19474     },
19475     
19476     
19477     onClick: function(e) 
19478     {
19479         e.stopPropagation();
19480         e.preventDefault();
19481         
19482         var target = e.getTarget();
19483         
19484         if(target.nodeName.toLowerCase() === 'i'){
19485             target = Roo.get(target).dom.parentNode;
19486         }
19487         
19488         var nodeName = target.nodeName;
19489         var className = target.className;
19490         var html = target.innerHTML;
19491         //Roo.log(nodeName);
19492         
19493         switch(nodeName.toLowerCase()) {
19494             case 'th':
19495                 switch(className) {
19496                     case 'switch':
19497                         this.showMode(1);
19498                         break;
19499                     case 'prev':
19500                     case 'next':
19501                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19502                         switch(this.viewMode){
19503                                 case 0:
19504                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19505                                         break;
19506                                 case 1:
19507                                 case 2:
19508                                         this.viewDate = this.moveYear(this.viewDate, dir);
19509                                         break;
19510                         }
19511                         this.fill();
19512                         break;
19513                     case 'today':
19514                         var date = new Date();
19515                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19516 //                        this.fill()
19517                         this.setValue(this.formatDate(this.date));
19518                         
19519                         this.hidePopup();
19520                         break;
19521                 }
19522                 break;
19523             case 'span':
19524                 if (className.indexOf('disabled') < 0) {
19525                     this.viewDate.setUTCDate(1);
19526                     if (className.indexOf('month') > -1) {
19527                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19528                     } else {
19529                         var year = parseInt(html, 10) || 0;
19530                         this.viewDate.setUTCFullYear(year);
19531                         
19532                     }
19533                     
19534                     if(this.singleMode){
19535                         this.setValue(this.formatDate(this.viewDate));
19536                         this.hidePopup();
19537                         return;
19538                     }
19539                     
19540                     this.showMode(-1);
19541                     this.fill();
19542                 }
19543                 break;
19544                 
19545             case 'td':
19546                 //Roo.log(className);
19547                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19548                     var day = parseInt(html, 10) || 1;
19549                     var year = this.viewDate.getUTCFullYear(),
19550                         month = this.viewDate.getUTCMonth();
19551
19552                     if (className.indexOf('old') > -1) {
19553                         if(month === 0 ){
19554                             month = 11;
19555                             year -= 1;
19556                         }else{
19557                             month -= 1;
19558                         }
19559                     } else if (className.indexOf('new') > -1) {
19560                         if (month == 11) {
19561                             month = 0;
19562                             year += 1;
19563                         } else {
19564                             month += 1;
19565                         }
19566                     }
19567                     //Roo.log([year,month,day]);
19568                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19569                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19570 //                    this.fill();
19571                     //Roo.log(this.formatDate(this.date));
19572                     this.setValue(this.formatDate(this.date));
19573                     this.hidePopup();
19574                 }
19575                 break;
19576         }
19577     },
19578     
19579     setStartDate: function(startDate)
19580     {
19581         this.startDate = startDate || -Infinity;
19582         if (this.startDate !== -Infinity) {
19583             this.startDate = this.parseDate(this.startDate);
19584         }
19585         this.update();
19586         this.updateNavArrows();
19587     },
19588
19589     setEndDate: function(endDate)
19590     {
19591         this.endDate = endDate || Infinity;
19592         if (this.endDate !== Infinity) {
19593             this.endDate = this.parseDate(this.endDate);
19594         }
19595         this.update();
19596         this.updateNavArrows();
19597     },
19598     
19599     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19600     {
19601         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19602         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19603             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19604         }
19605         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19606             return parseInt(d, 10);
19607         });
19608         this.update();
19609         this.updateNavArrows();
19610     },
19611     
19612     updateNavArrows: function() 
19613     {
19614         if(this.singleMode){
19615             return;
19616         }
19617         
19618         var d = new Date(this.viewDate),
19619         year = d.getUTCFullYear(),
19620         month = d.getUTCMonth();
19621         
19622         Roo.each(this.picker().select('.prev', true).elements, function(v){
19623             v.show();
19624             switch (this.viewMode) {
19625                 case 0:
19626
19627                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19628                         v.hide();
19629                     }
19630                     break;
19631                 case 1:
19632                 case 2:
19633                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19634                         v.hide();
19635                     }
19636                     break;
19637             }
19638         });
19639         
19640         Roo.each(this.picker().select('.next', true).elements, function(v){
19641             v.show();
19642             switch (this.viewMode) {
19643                 case 0:
19644
19645                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19646                         v.hide();
19647                     }
19648                     break;
19649                 case 1:
19650                 case 2:
19651                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19652                         v.hide();
19653                     }
19654                     break;
19655             }
19656         })
19657     },
19658     
19659     moveMonth: function(date, dir)
19660     {
19661         if (!dir) {
19662             return date;
19663         }
19664         var new_date = new Date(date.valueOf()),
19665         day = new_date.getUTCDate(),
19666         month = new_date.getUTCMonth(),
19667         mag = Math.abs(dir),
19668         new_month, test;
19669         dir = dir > 0 ? 1 : -1;
19670         if (mag == 1){
19671             test = dir == -1
19672             // If going back one month, make sure month is not current month
19673             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19674             ? function(){
19675                 return new_date.getUTCMonth() == month;
19676             }
19677             // If going forward one month, make sure month is as expected
19678             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19679             : function(){
19680                 return new_date.getUTCMonth() != new_month;
19681             };
19682             new_month = month + dir;
19683             new_date.setUTCMonth(new_month);
19684             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19685             if (new_month < 0 || new_month > 11) {
19686                 new_month = (new_month + 12) % 12;
19687             }
19688         } else {
19689             // For magnitudes >1, move one month at a time...
19690             for (var i=0; i<mag; i++) {
19691                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19692                 new_date = this.moveMonth(new_date, dir);
19693             }
19694             // ...then reset the day, keeping it in the new month
19695             new_month = new_date.getUTCMonth();
19696             new_date.setUTCDate(day);
19697             test = function(){
19698                 return new_month != new_date.getUTCMonth();
19699             };
19700         }
19701         // Common date-resetting loop -- if date is beyond end of month, make it
19702         // end of month
19703         while (test()){
19704             new_date.setUTCDate(--day);
19705             new_date.setUTCMonth(new_month);
19706         }
19707         return new_date;
19708     },
19709
19710     moveYear: function(date, dir)
19711     {
19712         return this.moveMonth(date, dir*12);
19713     },
19714
19715     dateWithinRange: function(date)
19716     {
19717         return date >= this.startDate && date <= this.endDate;
19718     },
19719
19720     
19721     remove: function() 
19722     {
19723         this.picker().remove();
19724     },
19725     
19726     validateValue : function(value)
19727     {
19728         if(this.getVisibilityEl().hasClass('hidden')){
19729             return true;
19730         }
19731         
19732         if(value.length < 1)  {
19733             if(this.allowBlank){
19734                 return true;
19735             }
19736             return false;
19737         }
19738         
19739         if(value.length < this.minLength){
19740             return false;
19741         }
19742         if(value.length > this.maxLength){
19743             return false;
19744         }
19745         if(this.vtype){
19746             var vt = Roo.form.VTypes;
19747             if(!vt[this.vtype](value, this)){
19748                 return false;
19749             }
19750         }
19751         if(typeof this.validator == "function"){
19752             var msg = this.validator(value);
19753             if(msg !== true){
19754                 return false;
19755             }
19756         }
19757         
19758         if(this.regex && !this.regex.test(value)){
19759             return false;
19760         }
19761         
19762         if(typeof(this.parseDate(value)) == 'undefined'){
19763             return false;
19764         }
19765         
19766         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19767             return false;
19768         }      
19769         
19770         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19771             return false;
19772         } 
19773         
19774         
19775         return true;
19776     },
19777     
19778     reset : function()
19779     {
19780         this.date = this.viewDate = '';
19781         
19782         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19783     }
19784    
19785 });
19786
19787 Roo.apply(Roo.bootstrap.DateField,  {
19788     
19789     head : {
19790         tag: 'thead',
19791         cn: [
19792         {
19793             tag: 'tr',
19794             cn: [
19795             {
19796                 tag: 'th',
19797                 cls: 'prev',
19798                 html: '<i class="fa fa-arrow-left"/>'
19799             },
19800             {
19801                 tag: 'th',
19802                 cls: 'switch',
19803                 colspan: '5'
19804             },
19805             {
19806                 tag: 'th',
19807                 cls: 'next',
19808                 html: '<i class="fa fa-arrow-right"/>'
19809             }
19810
19811             ]
19812         }
19813         ]
19814     },
19815     
19816     content : {
19817         tag: 'tbody',
19818         cn: [
19819         {
19820             tag: 'tr',
19821             cn: [
19822             {
19823                 tag: 'td',
19824                 colspan: '7'
19825             }
19826             ]
19827         }
19828         ]
19829     },
19830     
19831     footer : {
19832         tag: 'tfoot',
19833         cn: [
19834         {
19835             tag: 'tr',
19836             cn: [
19837             {
19838                 tag: 'th',
19839                 colspan: '7',
19840                 cls: 'today'
19841             }
19842                     
19843             ]
19844         }
19845         ]
19846     },
19847     
19848     dates:{
19849         en: {
19850             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19851             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19852             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19853             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19854             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19855             today: "Today"
19856         }
19857     },
19858     
19859     modes: [
19860     {
19861         clsName: 'days',
19862         navFnc: 'Month',
19863         navStep: 1
19864     },
19865     {
19866         clsName: 'months',
19867         navFnc: 'FullYear',
19868         navStep: 1
19869     },
19870     {
19871         clsName: 'years',
19872         navFnc: 'FullYear',
19873         navStep: 10
19874     }]
19875 });
19876
19877 Roo.apply(Roo.bootstrap.DateField,  {
19878   
19879     template : {
19880         tag: 'div',
19881         cls: 'datepicker dropdown-menu roo-dynamic',
19882         cn: [
19883         {
19884             tag: 'div',
19885             cls: 'datepicker-days',
19886             cn: [
19887             {
19888                 tag: 'table',
19889                 cls: 'table-condensed',
19890                 cn:[
19891                 Roo.bootstrap.DateField.head,
19892                 {
19893                     tag: 'tbody'
19894                 },
19895                 Roo.bootstrap.DateField.footer
19896                 ]
19897             }
19898             ]
19899         },
19900         {
19901             tag: 'div',
19902             cls: 'datepicker-months',
19903             cn: [
19904             {
19905                 tag: 'table',
19906                 cls: 'table-condensed',
19907                 cn:[
19908                 Roo.bootstrap.DateField.head,
19909                 Roo.bootstrap.DateField.content,
19910                 Roo.bootstrap.DateField.footer
19911                 ]
19912             }
19913             ]
19914         },
19915         {
19916             tag: 'div',
19917             cls: 'datepicker-years',
19918             cn: [
19919             {
19920                 tag: 'table',
19921                 cls: 'table-condensed',
19922                 cn:[
19923                 Roo.bootstrap.DateField.head,
19924                 Roo.bootstrap.DateField.content,
19925                 Roo.bootstrap.DateField.footer
19926                 ]
19927             }
19928             ]
19929         }
19930         ]
19931     }
19932 });
19933
19934  
19935
19936  /*
19937  * - LGPL
19938  *
19939  * TimeField
19940  * 
19941  */
19942
19943 /**
19944  * @class Roo.bootstrap.TimeField
19945  * @extends Roo.bootstrap.Input
19946  * Bootstrap DateField class
19947  * 
19948  * 
19949  * @constructor
19950  * Create a new TimeField
19951  * @param {Object} config The config object
19952  */
19953
19954 Roo.bootstrap.TimeField = function(config){
19955     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19956     this.addEvents({
19957             /**
19958              * @event show
19959              * Fires when this field show.
19960              * @param {Roo.bootstrap.DateField} thisthis
19961              * @param {Mixed} date The date value
19962              */
19963             show : true,
19964             /**
19965              * @event show
19966              * Fires when this field hide.
19967              * @param {Roo.bootstrap.DateField} this
19968              * @param {Mixed} date The date value
19969              */
19970             hide : true,
19971             /**
19972              * @event select
19973              * Fires when select a date.
19974              * @param {Roo.bootstrap.DateField} this
19975              * @param {Mixed} date The date value
19976              */
19977             select : true
19978         });
19979 };
19980
19981 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19982     
19983     /**
19984      * @cfg {String} format
19985      * The default time format string which can be overriden for localization support.  The format must be
19986      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19987      */
19988     format : "H:i",
19989        
19990     onRender: function(ct, position)
19991     {
19992         
19993         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19994                 
19995         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19996         
19997         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19998         
19999         this.pop = this.picker().select('>.datepicker-time',true).first();
20000         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20001         
20002         this.picker().on('mousedown', this.onMousedown, this);
20003         this.picker().on('click', this.onClick, this);
20004         
20005         this.picker().addClass('datepicker-dropdown');
20006     
20007         this.fillTime();
20008         this.update();
20009             
20010         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20011         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20012         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20013         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20014         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20015         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20016
20017     },
20018     
20019     fireKey: function(e){
20020         if (!this.picker().isVisible()){
20021             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20022                 this.show();
20023             }
20024             return;
20025         }
20026
20027         e.preventDefault();
20028         
20029         switch(e.keyCode){
20030             case 27: // escape
20031                 this.hide();
20032                 break;
20033             case 37: // left
20034             case 39: // right
20035                 this.onTogglePeriod();
20036                 break;
20037             case 38: // up
20038                 this.onIncrementMinutes();
20039                 break;
20040             case 40: // down
20041                 this.onDecrementMinutes();
20042                 break;
20043             case 13: // enter
20044             case 9: // tab
20045                 this.setTime();
20046                 break;
20047         }
20048     },
20049     
20050     onClick: function(e) {
20051         e.stopPropagation();
20052         e.preventDefault();
20053     },
20054     
20055     picker : function()
20056     {
20057         return this.el.select('.datepicker', true).first();
20058     },
20059     
20060     fillTime: function()
20061     {    
20062         var time = this.pop.select('tbody', true).first();
20063         
20064         time.dom.innerHTML = '';
20065         
20066         time.createChild({
20067             tag: 'tr',
20068             cn: [
20069                 {
20070                     tag: 'td',
20071                     cn: [
20072                         {
20073                             tag: 'a',
20074                             href: '#',
20075                             cls: 'btn',
20076                             cn: [
20077                                 {
20078                                     tag: 'span',
20079                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20080                                 }
20081                             ]
20082                         } 
20083                     ]
20084                 },
20085                 {
20086                     tag: 'td',
20087                     cls: 'separator'
20088                 },
20089                 {
20090                     tag: 'td',
20091                     cn: [
20092                         {
20093                             tag: 'a',
20094                             href: '#',
20095                             cls: 'btn',
20096                             cn: [
20097                                 {
20098                                     tag: 'span',
20099                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20100                                 }
20101                             ]
20102                         }
20103                     ]
20104                 },
20105                 {
20106                     tag: 'td',
20107                     cls: 'separator'
20108                 }
20109             ]
20110         });
20111         
20112         time.createChild({
20113             tag: 'tr',
20114             cn: [
20115                 {
20116                     tag: 'td',
20117                     cn: [
20118                         {
20119                             tag: 'span',
20120                             cls: 'timepicker-hour',
20121                             html: '00'
20122                         }  
20123                     ]
20124                 },
20125                 {
20126                     tag: 'td',
20127                     cls: 'separator',
20128                     html: ':'
20129                 },
20130                 {
20131                     tag: 'td',
20132                     cn: [
20133                         {
20134                             tag: 'span',
20135                             cls: 'timepicker-minute',
20136                             html: '00'
20137                         }  
20138                     ]
20139                 },
20140                 {
20141                     tag: 'td',
20142                     cls: 'separator'
20143                 },
20144                 {
20145                     tag: 'td',
20146                     cn: [
20147                         {
20148                             tag: 'button',
20149                             type: 'button',
20150                             cls: 'btn btn-primary period',
20151                             html: 'AM'
20152                             
20153                         }
20154                     ]
20155                 }
20156             ]
20157         });
20158         
20159         time.createChild({
20160             tag: 'tr',
20161             cn: [
20162                 {
20163                     tag: 'td',
20164                     cn: [
20165                         {
20166                             tag: 'a',
20167                             href: '#',
20168                             cls: 'btn',
20169                             cn: [
20170                                 {
20171                                     tag: 'span',
20172                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20173                                 }
20174                             ]
20175                         }
20176                     ]
20177                 },
20178                 {
20179                     tag: 'td',
20180                     cls: 'separator'
20181                 },
20182                 {
20183                     tag: 'td',
20184                     cn: [
20185                         {
20186                             tag: 'a',
20187                             href: '#',
20188                             cls: 'btn',
20189                             cn: [
20190                                 {
20191                                     tag: 'span',
20192                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20193                                 }
20194                             ]
20195                         }
20196                     ]
20197                 },
20198                 {
20199                     tag: 'td',
20200                     cls: 'separator'
20201                 }
20202             ]
20203         });
20204         
20205     },
20206     
20207     update: function()
20208     {
20209         
20210         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20211         
20212         this.fill();
20213     },
20214     
20215     fill: function() 
20216     {
20217         var hours = this.time.getHours();
20218         var minutes = this.time.getMinutes();
20219         var period = 'AM';
20220         
20221         if(hours > 11){
20222             period = 'PM';
20223         }
20224         
20225         if(hours == 0){
20226             hours = 12;
20227         }
20228         
20229         
20230         if(hours > 12){
20231             hours = hours - 12;
20232         }
20233         
20234         if(hours < 10){
20235             hours = '0' + hours;
20236         }
20237         
20238         if(minutes < 10){
20239             minutes = '0' + minutes;
20240         }
20241         
20242         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20243         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20244         this.pop.select('button', true).first().dom.innerHTML = period;
20245         
20246     },
20247     
20248     place: function()
20249     {   
20250         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20251         
20252         var cls = ['bottom'];
20253         
20254         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20255             cls.pop();
20256             cls.push('top');
20257         }
20258         
20259         cls.push('right');
20260         
20261         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20262             cls.pop();
20263             cls.push('left');
20264         }
20265         
20266         this.picker().addClass(cls.join('-'));
20267         
20268         var _this = this;
20269         
20270         Roo.each(cls, function(c){
20271             if(c == 'bottom'){
20272                 _this.picker().setTop(_this.inputEl().getHeight());
20273                 return;
20274             }
20275             if(c == 'top'){
20276                 _this.picker().setTop(0 - _this.picker().getHeight());
20277                 return;
20278             }
20279             
20280             if(c == 'left'){
20281                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20282                 return;
20283             }
20284             if(c == 'right'){
20285                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20286                 return;
20287             }
20288         });
20289         
20290     },
20291   
20292     onFocus : function()
20293     {
20294         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20295         this.show();
20296     },
20297     
20298     onBlur : function()
20299     {
20300         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20301         this.hide();
20302     },
20303     
20304     show : function()
20305     {
20306         this.picker().show();
20307         this.pop.show();
20308         this.update();
20309         this.place();
20310         
20311         this.fireEvent('show', this, this.date);
20312     },
20313     
20314     hide : function()
20315     {
20316         this.picker().hide();
20317         this.pop.hide();
20318         
20319         this.fireEvent('hide', this, this.date);
20320     },
20321     
20322     setTime : function()
20323     {
20324         this.hide();
20325         this.setValue(this.time.format(this.format));
20326         
20327         this.fireEvent('select', this, this.date);
20328         
20329         
20330     },
20331     
20332     onMousedown: function(e){
20333         e.stopPropagation();
20334         e.preventDefault();
20335     },
20336     
20337     onIncrementHours: function()
20338     {
20339         Roo.log('onIncrementHours');
20340         this.time = this.time.add(Date.HOUR, 1);
20341         this.update();
20342         
20343     },
20344     
20345     onDecrementHours: function()
20346     {
20347         Roo.log('onDecrementHours');
20348         this.time = this.time.add(Date.HOUR, -1);
20349         this.update();
20350     },
20351     
20352     onIncrementMinutes: function()
20353     {
20354         Roo.log('onIncrementMinutes');
20355         this.time = this.time.add(Date.MINUTE, 1);
20356         this.update();
20357     },
20358     
20359     onDecrementMinutes: function()
20360     {
20361         Roo.log('onDecrementMinutes');
20362         this.time = this.time.add(Date.MINUTE, -1);
20363         this.update();
20364     },
20365     
20366     onTogglePeriod: function()
20367     {
20368         Roo.log('onTogglePeriod');
20369         this.time = this.time.add(Date.HOUR, 12);
20370         this.update();
20371     }
20372     
20373    
20374 });
20375
20376 Roo.apply(Roo.bootstrap.TimeField,  {
20377     
20378     content : {
20379         tag: 'tbody',
20380         cn: [
20381             {
20382                 tag: 'tr',
20383                 cn: [
20384                 {
20385                     tag: 'td',
20386                     colspan: '7'
20387                 }
20388                 ]
20389             }
20390         ]
20391     },
20392     
20393     footer : {
20394         tag: 'tfoot',
20395         cn: [
20396             {
20397                 tag: 'tr',
20398                 cn: [
20399                 {
20400                     tag: 'th',
20401                     colspan: '7',
20402                     cls: '',
20403                     cn: [
20404                         {
20405                             tag: 'button',
20406                             cls: 'btn btn-info ok',
20407                             html: 'OK'
20408                         }
20409                     ]
20410                 }
20411
20412                 ]
20413             }
20414         ]
20415     }
20416 });
20417
20418 Roo.apply(Roo.bootstrap.TimeField,  {
20419   
20420     template : {
20421         tag: 'div',
20422         cls: 'datepicker dropdown-menu',
20423         cn: [
20424             {
20425                 tag: 'div',
20426                 cls: 'datepicker-time',
20427                 cn: [
20428                 {
20429                     tag: 'table',
20430                     cls: 'table-condensed',
20431                     cn:[
20432                     Roo.bootstrap.TimeField.content,
20433                     Roo.bootstrap.TimeField.footer
20434                     ]
20435                 }
20436                 ]
20437             }
20438         ]
20439     }
20440 });
20441
20442  
20443
20444  /*
20445  * - LGPL
20446  *
20447  * MonthField
20448  * 
20449  */
20450
20451 /**
20452  * @class Roo.bootstrap.MonthField
20453  * @extends Roo.bootstrap.Input
20454  * Bootstrap MonthField class
20455  * 
20456  * @cfg {String} language default en
20457  * 
20458  * @constructor
20459  * Create a new MonthField
20460  * @param {Object} config The config object
20461  */
20462
20463 Roo.bootstrap.MonthField = function(config){
20464     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20465     
20466     this.addEvents({
20467         /**
20468          * @event show
20469          * Fires when this field show.
20470          * @param {Roo.bootstrap.MonthField} this
20471          * @param {Mixed} date The date value
20472          */
20473         show : true,
20474         /**
20475          * @event show
20476          * Fires when this field hide.
20477          * @param {Roo.bootstrap.MonthField} this
20478          * @param {Mixed} date The date value
20479          */
20480         hide : true,
20481         /**
20482          * @event select
20483          * Fires when select a date.
20484          * @param {Roo.bootstrap.MonthField} this
20485          * @param {String} oldvalue The old value
20486          * @param {String} newvalue The new value
20487          */
20488         select : true
20489     });
20490 };
20491
20492 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20493     
20494     onRender: function(ct, position)
20495     {
20496         
20497         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20498         
20499         this.language = this.language || 'en';
20500         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20501         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20502         
20503         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20504         this.isInline = false;
20505         this.isInput = true;
20506         this.component = this.el.select('.add-on', true).first() || false;
20507         this.component = (this.component && this.component.length === 0) ? false : this.component;
20508         this.hasInput = this.component && this.inputEL().length;
20509         
20510         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20511         
20512         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20513         
20514         this.picker().on('mousedown', this.onMousedown, this);
20515         this.picker().on('click', this.onClick, this);
20516         
20517         this.picker().addClass('datepicker-dropdown');
20518         
20519         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20520             v.setStyle('width', '189px');
20521         });
20522         
20523         this.fillMonths();
20524         
20525         this.update();
20526         
20527         if(this.isInline) {
20528             this.show();
20529         }
20530         
20531     },
20532     
20533     setValue: function(v, suppressEvent)
20534     {   
20535         var o = this.getValue();
20536         
20537         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20538         
20539         this.update();
20540
20541         if(suppressEvent !== true){
20542             this.fireEvent('select', this, o, v);
20543         }
20544         
20545     },
20546     
20547     getValue: function()
20548     {
20549         return this.value;
20550     },
20551     
20552     onClick: function(e) 
20553     {
20554         e.stopPropagation();
20555         e.preventDefault();
20556         
20557         var target = e.getTarget();
20558         
20559         if(target.nodeName.toLowerCase() === 'i'){
20560             target = Roo.get(target).dom.parentNode;
20561         }
20562         
20563         var nodeName = target.nodeName;
20564         var className = target.className;
20565         var html = target.innerHTML;
20566         
20567         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20568             return;
20569         }
20570         
20571         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20572         
20573         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20574         
20575         this.hide();
20576                         
20577     },
20578     
20579     picker : function()
20580     {
20581         return this.pickerEl;
20582     },
20583     
20584     fillMonths: function()
20585     {    
20586         var i = 0;
20587         var months = this.picker().select('>.datepicker-months td', true).first();
20588         
20589         months.dom.innerHTML = '';
20590         
20591         while (i < 12) {
20592             var month = {
20593                 tag: 'span',
20594                 cls: 'month',
20595                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20596             };
20597             
20598             months.createChild(month);
20599         }
20600         
20601     },
20602     
20603     update: function()
20604     {
20605         var _this = this;
20606         
20607         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20608             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20609         }
20610         
20611         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20612             e.removeClass('active');
20613             
20614             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20615                 e.addClass('active');
20616             }
20617         })
20618     },
20619     
20620     place: function()
20621     {
20622         if(this.isInline) {
20623             return;
20624         }
20625         
20626         this.picker().removeClass(['bottom', 'top']);
20627         
20628         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20629             /*
20630              * place to the top of element!
20631              *
20632              */
20633             
20634             this.picker().addClass('top');
20635             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20636             
20637             return;
20638         }
20639         
20640         this.picker().addClass('bottom');
20641         
20642         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20643     },
20644     
20645     onFocus : function()
20646     {
20647         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20648         this.show();
20649     },
20650     
20651     onBlur : function()
20652     {
20653         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20654         
20655         var d = this.inputEl().getValue();
20656         
20657         this.setValue(d);
20658                 
20659         this.hide();
20660     },
20661     
20662     show : function()
20663     {
20664         this.picker().show();
20665         this.picker().select('>.datepicker-months', true).first().show();
20666         this.update();
20667         this.place();
20668         
20669         this.fireEvent('show', this, this.date);
20670     },
20671     
20672     hide : function()
20673     {
20674         if(this.isInline) {
20675             return;
20676         }
20677         this.picker().hide();
20678         this.fireEvent('hide', this, this.date);
20679         
20680     },
20681     
20682     onMousedown: function(e)
20683     {
20684         e.stopPropagation();
20685         e.preventDefault();
20686     },
20687     
20688     keyup: function(e)
20689     {
20690         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20691         this.update();
20692     },
20693
20694     fireKey: function(e)
20695     {
20696         if (!this.picker().isVisible()){
20697             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20698                 this.show();
20699             }
20700             return;
20701         }
20702         
20703         var dir;
20704         
20705         switch(e.keyCode){
20706             case 27: // escape
20707                 this.hide();
20708                 e.preventDefault();
20709                 break;
20710             case 37: // left
20711             case 39: // right
20712                 dir = e.keyCode == 37 ? -1 : 1;
20713                 
20714                 this.vIndex = this.vIndex + dir;
20715                 
20716                 if(this.vIndex < 0){
20717                     this.vIndex = 0;
20718                 }
20719                 
20720                 if(this.vIndex > 11){
20721                     this.vIndex = 11;
20722                 }
20723                 
20724                 if(isNaN(this.vIndex)){
20725                     this.vIndex = 0;
20726                 }
20727                 
20728                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20729                 
20730                 break;
20731             case 38: // up
20732             case 40: // down
20733                 
20734                 dir = e.keyCode == 38 ? -1 : 1;
20735                 
20736                 this.vIndex = this.vIndex + dir * 4;
20737                 
20738                 if(this.vIndex < 0){
20739                     this.vIndex = 0;
20740                 }
20741                 
20742                 if(this.vIndex > 11){
20743                     this.vIndex = 11;
20744                 }
20745                 
20746                 if(isNaN(this.vIndex)){
20747                     this.vIndex = 0;
20748                 }
20749                 
20750                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20751                 break;
20752                 
20753             case 13: // enter
20754                 
20755                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20756                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20757                 }
20758                 
20759                 this.hide();
20760                 e.preventDefault();
20761                 break;
20762             case 9: // tab
20763                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20764                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20765                 }
20766                 this.hide();
20767                 break;
20768             case 16: // shift
20769             case 17: // ctrl
20770             case 18: // alt
20771                 break;
20772             default :
20773                 this.hide();
20774                 
20775         }
20776     },
20777     
20778     remove: function() 
20779     {
20780         this.picker().remove();
20781     }
20782    
20783 });
20784
20785 Roo.apply(Roo.bootstrap.MonthField,  {
20786     
20787     content : {
20788         tag: 'tbody',
20789         cn: [
20790         {
20791             tag: 'tr',
20792             cn: [
20793             {
20794                 tag: 'td',
20795                 colspan: '7'
20796             }
20797             ]
20798         }
20799         ]
20800     },
20801     
20802     dates:{
20803         en: {
20804             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20805             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20806         }
20807     }
20808 });
20809
20810 Roo.apply(Roo.bootstrap.MonthField,  {
20811   
20812     template : {
20813         tag: 'div',
20814         cls: 'datepicker dropdown-menu roo-dynamic',
20815         cn: [
20816             {
20817                 tag: 'div',
20818                 cls: 'datepicker-months',
20819                 cn: [
20820                 {
20821                     tag: 'table',
20822                     cls: 'table-condensed',
20823                     cn:[
20824                         Roo.bootstrap.DateField.content
20825                     ]
20826                 }
20827                 ]
20828             }
20829         ]
20830     }
20831 });
20832
20833  
20834
20835  
20836  /*
20837  * - LGPL
20838  *
20839  * CheckBox
20840  * 
20841  */
20842
20843 /**
20844  * @class Roo.bootstrap.CheckBox
20845  * @extends Roo.bootstrap.Input
20846  * Bootstrap CheckBox class
20847  * 
20848  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20849  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20850  * @cfg {String} boxLabel The text that appears beside the checkbox
20851  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20852  * @cfg {Boolean} checked initnal the element
20853  * @cfg {Boolean} inline inline the element (default false)
20854  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20855  * @cfg {String} tooltip label tooltip
20856  * 
20857  * @constructor
20858  * Create a new CheckBox
20859  * @param {Object} config The config object
20860  */
20861
20862 Roo.bootstrap.CheckBox = function(config){
20863     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20864    
20865     this.addEvents({
20866         /**
20867         * @event check
20868         * Fires when the element is checked or unchecked.
20869         * @param {Roo.bootstrap.CheckBox} this This input
20870         * @param {Boolean} checked The new checked value
20871         */
20872        check : true,
20873        /**
20874         * @event click
20875         * Fires when the element is click.
20876         * @param {Roo.bootstrap.CheckBox} this This input
20877         */
20878        click : true
20879     });
20880     
20881 };
20882
20883 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20884   
20885     inputType: 'checkbox',
20886     inputValue: 1,
20887     valueOff: 0,
20888     boxLabel: false,
20889     checked: false,
20890     weight : false,
20891     inline: false,
20892     tooltip : '',
20893     
20894     getAutoCreate : function()
20895     {
20896         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20897         
20898         var id = Roo.id();
20899         
20900         var cfg = {};
20901         
20902         cfg.cls = 'form-group ' + this.inputType; //input-group
20903         
20904         if(this.inline){
20905             cfg.cls += ' ' + this.inputType + '-inline';
20906         }
20907         
20908         var input =  {
20909             tag: 'input',
20910             id : id,
20911             type : this.inputType,
20912             value : this.inputValue,
20913             cls : 'roo-' + this.inputType, //'form-box',
20914             placeholder : this.placeholder || ''
20915             
20916         };
20917         
20918         if(this.inputType != 'radio'){
20919             var hidden =  {
20920                 tag: 'input',
20921                 type : 'hidden',
20922                 cls : 'roo-hidden-value',
20923                 value : this.checked ? this.inputValue : this.valueOff
20924             };
20925         }
20926         
20927             
20928         if (this.weight) { // Validity check?
20929             cfg.cls += " " + this.inputType + "-" + this.weight;
20930         }
20931         
20932         if (this.disabled) {
20933             input.disabled=true;
20934         }
20935         
20936         if(this.checked){
20937             input.checked = this.checked;
20938         }
20939         
20940         if (this.name) {
20941             
20942             input.name = this.name;
20943             
20944             if(this.inputType != 'radio'){
20945                 hidden.name = this.name;
20946                 input.name = '_hidden_' + this.name;
20947             }
20948         }
20949         
20950         if (this.size) {
20951             input.cls += ' input-' + this.size;
20952         }
20953         
20954         var settings=this;
20955         
20956         ['xs','sm','md','lg'].map(function(size){
20957             if (settings[size]) {
20958                 cfg.cls += ' col-' + size + '-' + settings[size];
20959             }
20960         });
20961         
20962         var inputblock = input;
20963          
20964         if (this.before || this.after) {
20965             
20966             inputblock = {
20967                 cls : 'input-group',
20968                 cn :  [] 
20969             };
20970             
20971             if (this.before) {
20972                 inputblock.cn.push({
20973                     tag :'span',
20974                     cls : 'input-group-addon',
20975                     html : this.before
20976                 });
20977             }
20978             
20979             inputblock.cn.push(input);
20980             
20981             if(this.inputType != 'radio'){
20982                 inputblock.cn.push(hidden);
20983             }
20984             
20985             if (this.after) {
20986                 inputblock.cn.push({
20987                     tag :'span',
20988                     cls : 'input-group-addon',
20989                     html : this.after
20990                 });
20991             }
20992             
20993         }
20994         
20995         if (align ==='left' && this.fieldLabel.length) {
20996 //                Roo.log("left and has label");
20997             cfg.cn = [
20998                 {
20999                     tag: 'label',
21000                     'for' :  id,
21001                     cls : 'control-label',
21002                     html : this.fieldLabel
21003                 },
21004                 {
21005                     cls : "", 
21006                     cn: [
21007                         inputblock
21008                     ]
21009                 }
21010             ];
21011             
21012             if(this.labelWidth > 12){
21013                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21014             }
21015             
21016             if(this.labelWidth < 13 && this.labelmd == 0){
21017                 this.labelmd = this.labelWidth;
21018             }
21019             
21020             if(this.labellg > 0){
21021                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21022                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21023             }
21024             
21025             if(this.labelmd > 0){
21026                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21027                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21028             }
21029             
21030             if(this.labelsm > 0){
21031                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21032                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21033             }
21034             
21035             if(this.labelxs > 0){
21036                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21037                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21038             }
21039             
21040         } else if ( this.fieldLabel.length) {
21041 //                Roo.log(" label");
21042                 cfg.cn = [
21043                    
21044                     {
21045                         tag: this.boxLabel ? 'span' : 'label',
21046                         'for': id,
21047                         cls: 'control-label box-input-label',
21048                         //cls : 'input-group-addon',
21049                         html : this.fieldLabel
21050                     },
21051                     
21052                     inputblock
21053                     
21054                 ];
21055
21056         } else {
21057             
21058 //                Roo.log(" no label && no align");
21059                 cfg.cn = [  inputblock ] ;
21060                 
21061                 
21062         }
21063         
21064         if(this.boxLabel){
21065              var boxLabelCfg = {
21066                 tag: 'label',
21067                 //'for': id, // box label is handled by onclick - so no for...
21068                 cls: 'box-label',
21069                 html: this.boxLabel
21070             };
21071             
21072             if(this.tooltip){
21073                 boxLabelCfg.tooltip = this.tooltip;
21074             }
21075              
21076             cfg.cn.push(boxLabelCfg);
21077         }
21078         
21079         if(this.inputType != 'radio'){
21080             cfg.cn.push(hidden);
21081         }
21082         
21083         return cfg;
21084         
21085     },
21086     
21087     /**
21088      * return the real input element.
21089      */
21090     inputEl: function ()
21091     {
21092         return this.el.select('input.roo-' + this.inputType,true).first();
21093     },
21094     hiddenEl: function ()
21095     {
21096         return this.el.select('input.roo-hidden-value',true).first();
21097     },
21098     
21099     labelEl: function()
21100     {
21101         return this.el.select('label.control-label',true).first();
21102     },
21103     /* depricated... */
21104     
21105     label: function()
21106     {
21107         return this.labelEl();
21108     },
21109     
21110     boxLabelEl: function()
21111     {
21112         return this.el.select('label.box-label',true).first();
21113     },
21114     
21115     initEvents : function()
21116     {
21117 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21118         
21119         this.inputEl().on('click', this.onClick,  this);
21120         
21121         if (this.boxLabel) { 
21122             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21123         }
21124         
21125         this.startValue = this.getValue();
21126         
21127         if(this.groupId){
21128             Roo.bootstrap.CheckBox.register(this);
21129         }
21130     },
21131     
21132     onClick : function(e)
21133     {   
21134         if(this.fireEvent('click', this, e) !== false){
21135             this.setChecked(!this.checked);
21136         }
21137         
21138     },
21139     
21140     setChecked : function(state,suppressEvent)
21141     {
21142         this.startValue = this.getValue();
21143
21144         if(this.inputType == 'radio'){
21145             
21146             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21147                 e.dom.checked = false;
21148             });
21149             
21150             this.inputEl().dom.checked = true;
21151             
21152             this.inputEl().dom.value = this.inputValue;
21153             
21154             if(suppressEvent !== true){
21155                 this.fireEvent('check', this, true);
21156             }
21157             
21158             this.validate();
21159             
21160             return;
21161         }
21162         
21163         this.checked = state;
21164         
21165         this.inputEl().dom.checked = state;
21166         
21167         
21168         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21169         
21170         if(suppressEvent !== true){
21171             this.fireEvent('check', this, state);
21172         }
21173         
21174         this.validate();
21175     },
21176     
21177     getValue : function()
21178     {
21179         if(this.inputType == 'radio'){
21180             return this.getGroupValue();
21181         }
21182         
21183         return this.hiddenEl().dom.value;
21184         
21185     },
21186     
21187     getGroupValue : function()
21188     {
21189         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21190             return '';
21191         }
21192         
21193         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21194     },
21195     
21196     setValue : function(v,suppressEvent)
21197     {
21198         if(this.inputType == 'radio'){
21199             this.setGroupValue(v, suppressEvent);
21200             return;
21201         }
21202         
21203         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21204         
21205         this.validate();
21206     },
21207     
21208     setGroupValue : function(v, suppressEvent)
21209     {
21210         this.startValue = this.getValue();
21211         
21212         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21213             e.dom.checked = false;
21214             
21215             if(e.dom.value == v){
21216                 e.dom.checked = true;
21217             }
21218         });
21219         
21220         if(suppressEvent !== true){
21221             this.fireEvent('check', this, true);
21222         }
21223
21224         this.validate();
21225         
21226         return;
21227     },
21228     
21229     validate : function()
21230     {
21231         if(this.getVisibilityEl().hasClass('hidden')){
21232             return true;
21233         }
21234         
21235         if(
21236                 this.disabled || 
21237                 (this.inputType == 'radio' && this.validateRadio()) ||
21238                 (this.inputType == 'checkbox' && this.validateCheckbox())
21239         ){
21240             this.markValid();
21241             return true;
21242         }
21243         
21244         this.markInvalid();
21245         return false;
21246     },
21247     
21248     validateRadio : function()
21249     {
21250         if(this.getVisibilityEl().hasClass('hidden')){
21251             return true;
21252         }
21253         
21254         if(this.allowBlank){
21255             return true;
21256         }
21257         
21258         var valid = false;
21259         
21260         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21261             if(!e.dom.checked){
21262                 return;
21263             }
21264             
21265             valid = true;
21266             
21267             return false;
21268         });
21269         
21270         return valid;
21271     },
21272     
21273     validateCheckbox : function()
21274     {
21275         if(!this.groupId){
21276             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21277             //return (this.getValue() == this.inputValue) ? true : false;
21278         }
21279         
21280         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21281         
21282         if(!group){
21283             return false;
21284         }
21285         
21286         var r = false;
21287         
21288         for(var i in group){
21289             if(group[i].el.isVisible(true)){
21290                 r = false;
21291                 break;
21292             }
21293             
21294             r = true;
21295         }
21296         
21297         for(var i in group){
21298             if(r){
21299                 break;
21300             }
21301             
21302             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21303         }
21304         
21305         return r;
21306     },
21307     
21308     /**
21309      * Mark this field as valid
21310      */
21311     markValid : function()
21312     {
21313         var _this = this;
21314         
21315         this.fireEvent('valid', this);
21316         
21317         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21318         
21319         if(this.groupId){
21320             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21321         }
21322         
21323         if(label){
21324             label.markValid();
21325         }
21326
21327         if(this.inputType == 'radio'){
21328             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21329                 var fg = e.findParent('.form-group', false, true);
21330                 if (Roo.bootstrap.version == 3) {
21331                     fg.removeClass([_this.invalidClass, _this.validClass]);
21332                     fg.addClass(_this.validClass);
21333                 } else {
21334                     fg.removeClass(['is-valid', 'is-invalid']);
21335                     fg.addClass('is-valid');
21336                 }
21337             });
21338             
21339             return;
21340         }
21341
21342         if(!this.groupId){
21343             var fg = this.el.findParent('.form-group', false, true);
21344             if (Roo.bootstrap.version == 3) {
21345                 fg.removeClass([this.invalidClass, this.validClass]);
21346                 fg.addClass(this.validClass);
21347             } else {
21348                 fg.removeClass(['is-valid', 'is-invalid']);
21349                 fg.addClass('is-valid');
21350             }
21351             return;
21352         }
21353         
21354         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21355         
21356         if(!group){
21357             return;
21358         }
21359         
21360         for(var i in group){
21361             var fg = group[i].el.findParent('.form-group', false, true);
21362             if (Roo.bootstrap.version == 3) {
21363                 fg.removeClass([this.invalidClass, this.validClass]);
21364                 fg.addClass(this.validClass);
21365             } else {
21366                 fg.removeClass(['is-valid', 'is-invalid']);
21367                 fg.addClass('is-valid');
21368             }
21369         }
21370     },
21371     
21372      /**
21373      * Mark this field as invalid
21374      * @param {String} msg The validation message
21375      */
21376     markInvalid : function(msg)
21377     {
21378         if(this.allowBlank){
21379             return;
21380         }
21381         
21382         var _this = this;
21383         
21384         this.fireEvent('invalid', this, msg);
21385         
21386         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21387         
21388         if(this.groupId){
21389             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21390         }
21391         
21392         if(label){
21393             label.markInvalid();
21394         }
21395             
21396         if(this.inputType == 'radio'){
21397             
21398             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21399                 var fg = e.findParent('.form-group', false, true);
21400                 if (Roo.bootstrap.version == 3) {
21401                     fg.removeClass([_this.invalidClass, _this.validClass]);
21402                     fg.addClass(_this.invalidClass);
21403                 } else {
21404                     fg.removeClass(['is-invalid', 'is-valid']);
21405                     fg.addClass('is-invalid');
21406                 }
21407             });
21408             
21409             return;
21410         }
21411         
21412         if(!this.groupId){
21413             var fg = this.el.findParent('.form-group', false, true);
21414             if (Roo.bootstrap.version == 3) {
21415                 fg.removeClass([_this.invalidClass, _this.validClass]);
21416                 fg.addClass(_this.invalidClass);
21417             } else {
21418                 fg.removeClass(['is-invalid', 'is-valid']);
21419                 fg.addClass('is-invalid');
21420             }
21421             return;
21422         }
21423         
21424         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21425         
21426         if(!group){
21427             return;
21428         }
21429         
21430         for(var i in group){
21431             var fg = group[i].el.findParent('.form-group', false, true);
21432             if (Roo.bootstrap.version == 3) {
21433                 fg.removeClass([_this.invalidClass, _this.validClass]);
21434                 fg.addClass(_this.invalidClass);
21435             } else {
21436                 fg.removeClass(['is-invalid', 'is-valid']);
21437                 fg.addClass('is-invalid');
21438             }
21439         }
21440         
21441     },
21442     
21443     clearInvalid : function()
21444     {
21445         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21446         
21447         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21448         
21449         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21450         
21451         if (label && label.iconEl) {
21452             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21453             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21454         }
21455     },
21456     
21457     disable : function()
21458     {
21459         if(this.inputType != 'radio'){
21460             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21461             return;
21462         }
21463         
21464         var _this = this;
21465         
21466         if(this.rendered){
21467             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21468                 _this.getActionEl().addClass(this.disabledClass);
21469                 e.dom.disabled = true;
21470             });
21471         }
21472         
21473         this.disabled = true;
21474         this.fireEvent("disable", this);
21475         return this;
21476     },
21477
21478     enable : function()
21479     {
21480         if(this.inputType != 'radio'){
21481             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21482             return;
21483         }
21484         
21485         var _this = this;
21486         
21487         if(this.rendered){
21488             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21489                 _this.getActionEl().removeClass(this.disabledClass);
21490                 e.dom.disabled = false;
21491             });
21492         }
21493         
21494         this.disabled = false;
21495         this.fireEvent("enable", this);
21496         return this;
21497     },
21498     
21499     setBoxLabel : function(v)
21500     {
21501         this.boxLabel = v;
21502         
21503         if(this.rendered){
21504             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21505         }
21506     }
21507
21508 });
21509
21510 Roo.apply(Roo.bootstrap.CheckBox, {
21511     
21512     groups: {},
21513     
21514      /**
21515     * register a CheckBox Group
21516     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21517     */
21518     register : function(checkbox)
21519     {
21520         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21521             this.groups[checkbox.groupId] = {};
21522         }
21523         
21524         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21525             return;
21526         }
21527         
21528         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21529         
21530     },
21531     /**
21532     * fetch a CheckBox Group based on the group ID
21533     * @param {string} the group ID
21534     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21535     */
21536     get: function(groupId) {
21537         if (typeof(this.groups[groupId]) == 'undefined') {
21538             return false;
21539         }
21540         
21541         return this.groups[groupId] ;
21542     }
21543     
21544     
21545 });
21546 /*
21547  * - LGPL
21548  *
21549  * RadioItem
21550  * 
21551  */
21552
21553 /**
21554  * @class Roo.bootstrap.Radio
21555  * @extends Roo.bootstrap.Component
21556  * Bootstrap Radio class
21557  * @cfg {String} boxLabel - the label associated
21558  * @cfg {String} value - the value of radio
21559  * 
21560  * @constructor
21561  * Create a new Radio
21562  * @param {Object} config The config object
21563  */
21564 Roo.bootstrap.Radio = function(config){
21565     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21566     
21567 };
21568
21569 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21570     
21571     boxLabel : '',
21572     
21573     value : '',
21574     
21575     getAutoCreate : function()
21576     {
21577         var cfg = {
21578             tag : 'div',
21579             cls : 'form-group radio',
21580             cn : [
21581                 {
21582                     tag : 'label',
21583                     cls : 'box-label',
21584                     html : this.boxLabel
21585                 }
21586             ]
21587         };
21588         
21589         return cfg;
21590     },
21591     
21592     initEvents : function() 
21593     {
21594         this.parent().register(this);
21595         
21596         this.el.on('click', this.onClick, this);
21597         
21598     },
21599     
21600     onClick : function(e)
21601     {
21602         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21603             this.setChecked(true);
21604         }
21605     },
21606     
21607     setChecked : function(state, suppressEvent)
21608     {
21609         this.parent().setValue(this.value, suppressEvent);
21610         
21611     },
21612     
21613     setBoxLabel : function(v)
21614     {
21615         this.boxLabel = v;
21616         
21617         if(this.rendered){
21618             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21619         }
21620     }
21621     
21622 });
21623  
21624
21625  /*
21626  * - LGPL
21627  *
21628  * Input
21629  * 
21630  */
21631
21632 /**
21633  * @class Roo.bootstrap.SecurePass
21634  * @extends Roo.bootstrap.Input
21635  * Bootstrap SecurePass class
21636  *
21637  * 
21638  * @constructor
21639  * Create a new SecurePass
21640  * @param {Object} config The config object
21641  */
21642  
21643 Roo.bootstrap.SecurePass = function (config) {
21644     // these go here, so the translation tool can replace them..
21645     this.errors = {
21646         PwdEmpty: "Please type a password, and then retype it to confirm.",
21647         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21648         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21649         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21650         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21651         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21652         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21653         TooWeak: "Your password is Too Weak."
21654     },
21655     this.meterLabel = "Password strength:";
21656     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21657     this.meterClass = [
21658         "roo-password-meter-tooweak", 
21659         "roo-password-meter-weak", 
21660         "roo-password-meter-medium", 
21661         "roo-password-meter-strong", 
21662         "roo-password-meter-grey"
21663     ];
21664     
21665     this.errors = {};
21666     
21667     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21668 }
21669
21670 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21671     /**
21672      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21673      * {
21674      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21675      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21676      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21677      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21678      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21679      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21680      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21681      * })
21682      */
21683     // private
21684     
21685     meterWidth: 300,
21686     errorMsg :'',    
21687     errors: false,
21688     imageRoot: '/',
21689     /**
21690      * @cfg {String/Object} Label for the strength meter (defaults to
21691      * 'Password strength:')
21692      */
21693     // private
21694     meterLabel: '',
21695     /**
21696      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21697      * ['Weak', 'Medium', 'Strong'])
21698      */
21699     // private    
21700     pwdStrengths: false,    
21701     // private
21702     strength: 0,
21703     // private
21704     _lastPwd: null,
21705     // private
21706     kCapitalLetter: 0,
21707     kSmallLetter: 1,
21708     kDigit: 2,
21709     kPunctuation: 3,
21710     
21711     insecure: false,
21712     // private
21713     initEvents: function ()
21714     {
21715         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21716
21717         if (this.el.is('input[type=password]') && Roo.isSafari) {
21718             this.el.on('keydown', this.SafariOnKeyDown, this);
21719         }
21720
21721         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21722     },
21723     // private
21724     onRender: function (ct, position)
21725     {
21726         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21727         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21728         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21729
21730         this.trigger.createChild({
21731                    cn: [
21732                     {
21733                     //id: 'PwdMeter',
21734                     tag: 'div',
21735                     cls: 'roo-password-meter-grey col-xs-12',
21736                     style: {
21737                         //width: 0,
21738                         //width: this.meterWidth + 'px'                                                
21739                         }
21740                     },
21741                     {                            
21742                          cls: 'roo-password-meter-text'                          
21743                     }
21744                 ]            
21745         });
21746
21747          
21748         if (this.hideTrigger) {
21749             this.trigger.setDisplayed(false);
21750         }
21751         this.setSize(this.width || '', this.height || '');
21752     },
21753     // private
21754     onDestroy: function ()
21755     {
21756         if (this.trigger) {
21757             this.trigger.removeAllListeners();
21758             this.trigger.remove();
21759         }
21760         if (this.wrap) {
21761             this.wrap.remove();
21762         }
21763         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21764     },
21765     // private
21766     checkStrength: function ()
21767     {
21768         var pwd = this.inputEl().getValue();
21769         if (pwd == this._lastPwd) {
21770             return;
21771         }
21772
21773         var strength;
21774         if (this.ClientSideStrongPassword(pwd)) {
21775             strength = 3;
21776         } else if (this.ClientSideMediumPassword(pwd)) {
21777             strength = 2;
21778         } else if (this.ClientSideWeakPassword(pwd)) {
21779             strength = 1;
21780         } else {
21781             strength = 0;
21782         }
21783         
21784         Roo.log('strength1: ' + strength);
21785         
21786         //var pm = this.trigger.child('div/div/div').dom;
21787         var pm = this.trigger.child('div/div');
21788         pm.removeClass(this.meterClass);
21789         pm.addClass(this.meterClass[strength]);
21790                 
21791         
21792         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21793                 
21794         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21795         
21796         this._lastPwd = pwd;
21797     },
21798     reset: function ()
21799     {
21800         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21801         
21802         this._lastPwd = '';
21803         
21804         var pm = this.trigger.child('div/div');
21805         pm.removeClass(this.meterClass);
21806         pm.addClass('roo-password-meter-grey');        
21807         
21808         
21809         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21810         
21811         pt.innerHTML = '';
21812         this.inputEl().dom.type='password';
21813     },
21814     // private
21815     validateValue: function (value)
21816     {
21817         
21818         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21819             return false;
21820         }
21821         if (value.length == 0) {
21822             if (this.allowBlank) {
21823                 this.clearInvalid();
21824                 return true;
21825             }
21826
21827             this.markInvalid(this.errors.PwdEmpty);
21828             this.errorMsg = this.errors.PwdEmpty;
21829             return false;
21830         }
21831         
21832         if(this.insecure){
21833             return true;
21834         }
21835         
21836         if ('[\x21-\x7e]*'.match(value)) {
21837             this.markInvalid(this.errors.PwdBadChar);
21838             this.errorMsg = this.errors.PwdBadChar;
21839             return false;
21840         }
21841         if (value.length < 6) {
21842             this.markInvalid(this.errors.PwdShort);
21843             this.errorMsg = this.errors.PwdShort;
21844             return false;
21845         }
21846         if (value.length > 16) {
21847             this.markInvalid(this.errors.PwdLong);
21848             this.errorMsg = this.errors.PwdLong;
21849             return false;
21850         }
21851         var strength;
21852         if (this.ClientSideStrongPassword(value)) {
21853             strength = 3;
21854         } else if (this.ClientSideMediumPassword(value)) {
21855             strength = 2;
21856         } else if (this.ClientSideWeakPassword(value)) {
21857             strength = 1;
21858         } else {
21859             strength = 0;
21860         }
21861
21862         
21863         if (strength < 2) {
21864             //this.markInvalid(this.errors.TooWeak);
21865             this.errorMsg = this.errors.TooWeak;
21866             //return false;
21867         }
21868         
21869         
21870         console.log('strength2: ' + strength);
21871         
21872         //var pm = this.trigger.child('div/div/div').dom;
21873         
21874         var pm = this.trigger.child('div/div');
21875         pm.removeClass(this.meterClass);
21876         pm.addClass(this.meterClass[strength]);
21877                 
21878         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21879                 
21880         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21881         
21882         this.errorMsg = ''; 
21883         return true;
21884     },
21885     // private
21886     CharacterSetChecks: function (type)
21887     {
21888         this.type = type;
21889         this.fResult = false;
21890     },
21891     // private
21892     isctype: function (character, type)
21893     {
21894         switch (type) {  
21895             case this.kCapitalLetter:
21896                 if (character >= 'A' && character <= 'Z') {
21897                     return true;
21898                 }
21899                 break;
21900             
21901             case this.kSmallLetter:
21902                 if (character >= 'a' && character <= 'z') {
21903                     return true;
21904                 }
21905                 break;
21906             
21907             case this.kDigit:
21908                 if (character >= '0' && character <= '9') {
21909                     return true;
21910                 }
21911                 break;
21912             
21913             case this.kPunctuation:
21914                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21915                     return true;
21916                 }
21917                 break;
21918             
21919             default:
21920                 return false;
21921         }
21922
21923     },
21924     // private
21925     IsLongEnough: function (pwd, size)
21926     {
21927         return !(pwd == null || isNaN(size) || pwd.length < size);
21928     },
21929     // private
21930     SpansEnoughCharacterSets: function (word, nb)
21931     {
21932         if (!this.IsLongEnough(word, nb))
21933         {
21934             return false;
21935         }
21936
21937         var characterSetChecks = new Array(
21938             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21939             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21940         );
21941         
21942         for (var index = 0; index < word.length; ++index) {
21943             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21944                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21945                     characterSetChecks[nCharSet].fResult = true;
21946                     break;
21947                 }
21948             }
21949         }
21950
21951         var nCharSets = 0;
21952         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21953             if (characterSetChecks[nCharSet].fResult) {
21954                 ++nCharSets;
21955             }
21956         }
21957
21958         if (nCharSets < nb) {
21959             return false;
21960         }
21961         return true;
21962     },
21963     // private
21964     ClientSideStrongPassword: function (pwd)
21965     {
21966         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21967     },
21968     // private
21969     ClientSideMediumPassword: function (pwd)
21970     {
21971         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21972     },
21973     // private
21974     ClientSideWeakPassword: function (pwd)
21975     {
21976         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21977     }
21978           
21979 })//<script type="text/javascript">
21980
21981 /*
21982  * Based  Ext JS Library 1.1.1
21983  * Copyright(c) 2006-2007, Ext JS, LLC.
21984  * LGPL
21985  *
21986  */
21987  
21988 /**
21989  * @class Roo.HtmlEditorCore
21990  * @extends Roo.Component
21991  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21992  *
21993  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21994  */
21995
21996 Roo.HtmlEditorCore = function(config){
21997     
21998     
21999     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22000     
22001     
22002     this.addEvents({
22003         /**
22004          * @event initialize
22005          * Fires when the editor is fully initialized (including the iframe)
22006          * @param {Roo.HtmlEditorCore} this
22007          */
22008         initialize: true,
22009         /**
22010          * @event activate
22011          * Fires when the editor is first receives the focus. Any insertion must wait
22012          * until after this event.
22013          * @param {Roo.HtmlEditorCore} this
22014          */
22015         activate: true,
22016          /**
22017          * @event beforesync
22018          * Fires before the textarea is updated with content from the editor iframe. Return false
22019          * to cancel the sync.
22020          * @param {Roo.HtmlEditorCore} this
22021          * @param {String} html
22022          */
22023         beforesync: true,
22024          /**
22025          * @event beforepush
22026          * Fires before the iframe editor is updated with content from the textarea. Return false
22027          * to cancel the push.
22028          * @param {Roo.HtmlEditorCore} this
22029          * @param {String} html
22030          */
22031         beforepush: true,
22032          /**
22033          * @event sync
22034          * Fires when the textarea is updated with content from the editor iframe.
22035          * @param {Roo.HtmlEditorCore} this
22036          * @param {String} html
22037          */
22038         sync: true,
22039          /**
22040          * @event push
22041          * Fires when the iframe editor is updated with content from the textarea.
22042          * @param {Roo.HtmlEditorCore} this
22043          * @param {String} html
22044          */
22045         push: true,
22046         
22047         /**
22048          * @event editorevent
22049          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22050          * @param {Roo.HtmlEditorCore} this
22051          */
22052         editorevent: true
22053         
22054     });
22055     
22056     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22057     
22058     // defaults : white / black...
22059     this.applyBlacklists();
22060     
22061     
22062     
22063 };
22064
22065
22066 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22067
22068
22069      /**
22070      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22071      */
22072     
22073     owner : false,
22074     
22075      /**
22076      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22077      *                        Roo.resizable.
22078      */
22079     resizable : false,
22080      /**
22081      * @cfg {Number} height (in pixels)
22082      */   
22083     height: 300,
22084    /**
22085      * @cfg {Number} width (in pixels)
22086      */   
22087     width: 500,
22088     
22089     /**
22090      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22091      * 
22092      */
22093     stylesheets: false,
22094     
22095     // id of frame..
22096     frameId: false,
22097     
22098     // private properties
22099     validationEvent : false,
22100     deferHeight: true,
22101     initialized : false,
22102     activated : false,
22103     sourceEditMode : false,
22104     onFocus : Roo.emptyFn,
22105     iframePad:3,
22106     hideMode:'offsets',
22107     
22108     clearUp: true,
22109     
22110     // blacklist + whitelisted elements..
22111     black: false,
22112     white: false,
22113      
22114     bodyCls : '',
22115
22116     /**
22117      * Protected method that will not generally be called directly. It
22118      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22119      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22120      */
22121     getDocMarkup : function(){
22122         // body styles..
22123         var st = '';
22124         
22125         // inherit styels from page...?? 
22126         if (this.stylesheets === false) {
22127             
22128             Roo.get(document.head).select('style').each(function(node) {
22129                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22130             });
22131             
22132             Roo.get(document.head).select('link').each(function(node) { 
22133                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22134             });
22135             
22136         } else if (!this.stylesheets.length) {
22137                 // simple..
22138                 st = '<style type="text/css">' +
22139                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22140                    '</style>';
22141         } else { 
22142             st = '<style type="text/css">' +
22143                     this.stylesheets +
22144                 '</style>';
22145         }
22146         
22147         st +=  '<style type="text/css">' +
22148             'IMG { cursor: pointer } ' +
22149         '</style>';
22150
22151         var cls = 'roo-htmleditor-body';
22152         
22153         if(this.bodyCls.length){
22154             cls += ' ' + this.bodyCls;
22155         }
22156         
22157         return '<html><head>' + st  +
22158             //<style type="text/css">' +
22159             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22160             //'</style>' +
22161             ' </head><body class="' +  cls + '"></body></html>';
22162     },
22163
22164     // private
22165     onRender : function(ct, position)
22166     {
22167         var _t = this;
22168         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22169         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22170         
22171         
22172         this.el.dom.style.border = '0 none';
22173         this.el.dom.setAttribute('tabIndex', -1);
22174         this.el.addClass('x-hidden hide');
22175         
22176         
22177         
22178         if(Roo.isIE){ // fix IE 1px bogus margin
22179             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22180         }
22181        
22182         
22183         this.frameId = Roo.id();
22184         
22185          
22186         
22187         var iframe = this.owner.wrap.createChild({
22188             tag: 'iframe',
22189             cls: 'form-control', // bootstrap..
22190             id: this.frameId,
22191             name: this.frameId,
22192             frameBorder : 'no',
22193             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22194         }, this.el
22195         );
22196         
22197         
22198         this.iframe = iframe.dom;
22199
22200          this.assignDocWin();
22201         
22202         this.doc.designMode = 'on';
22203        
22204         this.doc.open();
22205         this.doc.write(this.getDocMarkup());
22206         this.doc.close();
22207
22208         
22209         var task = { // must defer to wait for browser to be ready
22210             run : function(){
22211                 //console.log("run task?" + this.doc.readyState);
22212                 this.assignDocWin();
22213                 if(this.doc.body || this.doc.readyState == 'complete'){
22214                     try {
22215                         this.doc.designMode="on";
22216                     } catch (e) {
22217                         return;
22218                     }
22219                     Roo.TaskMgr.stop(task);
22220                     this.initEditor.defer(10, this);
22221                 }
22222             },
22223             interval : 10,
22224             duration: 10000,
22225             scope: this
22226         };
22227         Roo.TaskMgr.start(task);
22228
22229     },
22230
22231     // private
22232     onResize : function(w, h)
22233     {
22234          Roo.log('resize: ' +w + ',' + h );
22235         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22236         if(!this.iframe){
22237             return;
22238         }
22239         if(typeof w == 'number'){
22240             
22241             this.iframe.style.width = w + 'px';
22242         }
22243         if(typeof h == 'number'){
22244             
22245             this.iframe.style.height = h + 'px';
22246             if(this.doc){
22247                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22248             }
22249         }
22250         
22251     },
22252
22253     /**
22254      * Toggles the editor between standard and source edit mode.
22255      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22256      */
22257     toggleSourceEdit : function(sourceEditMode){
22258         
22259         this.sourceEditMode = sourceEditMode === true;
22260         
22261         if(this.sourceEditMode){
22262  
22263             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22264             
22265         }else{
22266             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22267             //this.iframe.className = '';
22268             this.deferFocus();
22269         }
22270         //this.setSize(this.owner.wrap.getSize());
22271         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22272     },
22273
22274     
22275   
22276
22277     /**
22278      * Protected method that will not generally be called directly. If you need/want
22279      * custom HTML cleanup, this is the method you should override.
22280      * @param {String} html The HTML to be cleaned
22281      * return {String} The cleaned HTML
22282      */
22283     cleanHtml : function(html){
22284         html = String(html);
22285         if(html.length > 5){
22286             if(Roo.isSafari){ // strip safari nonsense
22287                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22288             }
22289         }
22290         if(html == '&nbsp;'){
22291             html = '';
22292         }
22293         return html;
22294     },
22295
22296     /**
22297      * HTML Editor -> Textarea
22298      * Protected method that will not generally be called directly. Syncs the contents
22299      * of the editor iframe with the textarea.
22300      */
22301     syncValue : function(){
22302         if(this.initialized){
22303             var bd = (this.doc.body || this.doc.documentElement);
22304             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22305             var html = bd.innerHTML;
22306             if(Roo.isSafari){
22307                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22308                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22309                 if(m && m[1]){
22310                     html = '<div style="'+m[0]+'">' + html + '</div>';
22311                 }
22312             }
22313             html = this.cleanHtml(html);
22314             // fix up the special chars.. normaly like back quotes in word...
22315             // however we do not want to do this with chinese..
22316             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22317                 var cc = b.charCodeAt();
22318                 if (
22319                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22320                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22321                     (cc >= 0xf900 && cc < 0xfb00 )
22322                 ) {
22323                         return b;
22324                 }
22325                 return "&#"+cc+";" 
22326             });
22327             if(this.owner.fireEvent('beforesync', this, html) !== false){
22328                 this.el.dom.value = html;
22329                 this.owner.fireEvent('sync', this, html);
22330             }
22331         }
22332     },
22333
22334     /**
22335      * Protected method that will not generally be called directly. Pushes the value of the textarea
22336      * into the iframe editor.
22337      */
22338     pushValue : function(){
22339         if(this.initialized){
22340             var v = this.el.dom.value.trim();
22341             
22342 //            if(v.length < 1){
22343 //                v = '&#160;';
22344 //            }
22345             
22346             if(this.owner.fireEvent('beforepush', this, v) !== false){
22347                 var d = (this.doc.body || this.doc.documentElement);
22348                 d.innerHTML = v;
22349                 this.cleanUpPaste();
22350                 this.el.dom.value = d.innerHTML;
22351                 this.owner.fireEvent('push', this, v);
22352             }
22353         }
22354     },
22355
22356     // private
22357     deferFocus : function(){
22358         this.focus.defer(10, this);
22359     },
22360
22361     // doc'ed in Field
22362     focus : function(){
22363         if(this.win && !this.sourceEditMode){
22364             this.win.focus();
22365         }else{
22366             this.el.focus();
22367         }
22368     },
22369     
22370     assignDocWin: function()
22371     {
22372         var iframe = this.iframe;
22373         
22374          if(Roo.isIE){
22375             this.doc = iframe.contentWindow.document;
22376             this.win = iframe.contentWindow;
22377         } else {
22378 //            if (!Roo.get(this.frameId)) {
22379 //                return;
22380 //            }
22381 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22382 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22383             
22384             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22385                 return;
22386             }
22387             
22388             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22389             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22390         }
22391     },
22392     
22393     // private
22394     initEditor : function(){
22395         //console.log("INIT EDITOR");
22396         this.assignDocWin();
22397         
22398         
22399         
22400         this.doc.designMode="on";
22401         this.doc.open();
22402         this.doc.write(this.getDocMarkup());
22403         this.doc.close();
22404         
22405         var dbody = (this.doc.body || this.doc.documentElement);
22406         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22407         // this copies styles from the containing element into thsi one..
22408         // not sure why we need all of this..
22409         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22410         
22411         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22412         //ss['background-attachment'] = 'fixed'; // w3c
22413         dbody.bgProperties = 'fixed'; // ie
22414         //Roo.DomHelper.applyStyles(dbody, ss);
22415         Roo.EventManager.on(this.doc, {
22416             //'mousedown': this.onEditorEvent,
22417             'mouseup': this.onEditorEvent,
22418             'dblclick': this.onEditorEvent,
22419             'click': this.onEditorEvent,
22420             'keyup': this.onEditorEvent,
22421             buffer:100,
22422             scope: this
22423         });
22424         if(Roo.isGecko){
22425             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22426         }
22427         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22428             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22429         }
22430         this.initialized = true;
22431
22432         this.owner.fireEvent('initialize', this);
22433         this.pushValue();
22434     },
22435
22436     // private
22437     onDestroy : function(){
22438         
22439         
22440         
22441         if(this.rendered){
22442             
22443             //for (var i =0; i < this.toolbars.length;i++) {
22444             //    // fixme - ask toolbars for heights?
22445             //    this.toolbars[i].onDestroy();
22446            // }
22447             
22448             //this.wrap.dom.innerHTML = '';
22449             //this.wrap.remove();
22450         }
22451     },
22452
22453     // private
22454     onFirstFocus : function(){
22455         
22456         this.assignDocWin();
22457         
22458         
22459         this.activated = true;
22460          
22461     
22462         if(Roo.isGecko){ // prevent silly gecko errors
22463             this.win.focus();
22464             var s = this.win.getSelection();
22465             if(!s.focusNode || s.focusNode.nodeType != 3){
22466                 var r = s.getRangeAt(0);
22467                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22468                 r.collapse(true);
22469                 this.deferFocus();
22470             }
22471             try{
22472                 this.execCmd('useCSS', true);
22473                 this.execCmd('styleWithCSS', false);
22474             }catch(e){}
22475         }
22476         this.owner.fireEvent('activate', this);
22477     },
22478
22479     // private
22480     adjustFont: function(btn){
22481         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22482         //if(Roo.isSafari){ // safari
22483         //    adjust *= 2;
22484        // }
22485         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22486         if(Roo.isSafari){ // safari
22487             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22488             v =  (v < 10) ? 10 : v;
22489             v =  (v > 48) ? 48 : v;
22490             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22491             
22492         }
22493         
22494         
22495         v = Math.max(1, v+adjust);
22496         
22497         this.execCmd('FontSize', v  );
22498     },
22499
22500     onEditorEvent : function(e)
22501     {
22502         this.owner.fireEvent('editorevent', this, e);
22503       //  this.updateToolbar();
22504         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22505     },
22506
22507     insertTag : function(tg)
22508     {
22509         // could be a bit smarter... -> wrap the current selected tRoo..
22510         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22511             
22512             range = this.createRange(this.getSelection());
22513             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22514             wrappingNode.appendChild(range.extractContents());
22515             range.insertNode(wrappingNode);
22516
22517             return;
22518             
22519             
22520             
22521         }
22522         this.execCmd("formatblock",   tg);
22523         
22524     },
22525     
22526     insertText : function(txt)
22527     {
22528         
22529         
22530         var range = this.createRange();
22531         range.deleteContents();
22532                //alert(Sender.getAttribute('label'));
22533                
22534         range.insertNode(this.doc.createTextNode(txt));
22535     } ,
22536     
22537      
22538
22539     /**
22540      * Executes a Midas editor command on the editor document and performs necessary focus and
22541      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22542      * @param {String} cmd The Midas command
22543      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22544      */
22545     relayCmd : function(cmd, value){
22546         this.win.focus();
22547         this.execCmd(cmd, value);
22548         this.owner.fireEvent('editorevent', this);
22549         //this.updateToolbar();
22550         this.owner.deferFocus();
22551     },
22552
22553     /**
22554      * Executes a Midas editor command directly on the editor document.
22555      * For visual commands, you should use {@link #relayCmd} instead.
22556      * <b>This should only be called after the editor is initialized.</b>
22557      * @param {String} cmd The Midas command
22558      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22559      */
22560     execCmd : function(cmd, value){
22561         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22562         this.syncValue();
22563     },
22564  
22565  
22566    
22567     /**
22568      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22569      * to insert tRoo.
22570      * @param {String} text | dom node.. 
22571      */
22572     insertAtCursor : function(text)
22573     {
22574         
22575         if(!this.activated){
22576             return;
22577         }
22578         /*
22579         if(Roo.isIE){
22580             this.win.focus();
22581             var r = this.doc.selection.createRange();
22582             if(r){
22583                 r.collapse(true);
22584                 r.pasteHTML(text);
22585                 this.syncValue();
22586                 this.deferFocus();
22587             
22588             }
22589             return;
22590         }
22591         */
22592         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22593             this.win.focus();
22594             
22595             
22596             // from jquery ui (MIT licenced)
22597             var range, node;
22598             var win = this.win;
22599             
22600             if (win.getSelection && win.getSelection().getRangeAt) {
22601                 range = win.getSelection().getRangeAt(0);
22602                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22603                 range.insertNode(node);
22604             } else if (win.document.selection && win.document.selection.createRange) {
22605                 // no firefox support
22606                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22607                 win.document.selection.createRange().pasteHTML(txt);
22608             } else {
22609                 // no firefox support
22610                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22611                 this.execCmd('InsertHTML', txt);
22612             } 
22613             
22614             this.syncValue();
22615             
22616             this.deferFocus();
22617         }
22618     },
22619  // private
22620     mozKeyPress : function(e){
22621         if(e.ctrlKey){
22622             var c = e.getCharCode(), cmd;
22623           
22624             if(c > 0){
22625                 c = String.fromCharCode(c).toLowerCase();
22626                 switch(c){
22627                     case 'b':
22628                         cmd = 'bold';
22629                         break;
22630                     case 'i':
22631                         cmd = 'italic';
22632                         break;
22633                     
22634                     case 'u':
22635                         cmd = 'underline';
22636                         break;
22637                     
22638                     case 'v':
22639                         this.cleanUpPaste.defer(100, this);
22640                         return;
22641                         
22642                 }
22643                 if(cmd){
22644                     this.win.focus();
22645                     this.execCmd(cmd);
22646                     this.deferFocus();
22647                     e.preventDefault();
22648                 }
22649                 
22650             }
22651         }
22652     },
22653
22654     // private
22655     fixKeys : function(){ // load time branching for fastest keydown performance
22656         if(Roo.isIE){
22657             return function(e){
22658                 var k = e.getKey(), r;
22659                 if(k == e.TAB){
22660                     e.stopEvent();
22661                     r = this.doc.selection.createRange();
22662                     if(r){
22663                         r.collapse(true);
22664                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22665                         this.deferFocus();
22666                     }
22667                     return;
22668                 }
22669                 
22670                 if(k == e.ENTER){
22671                     r = this.doc.selection.createRange();
22672                     if(r){
22673                         var target = r.parentElement();
22674                         if(!target || target.tagName.toLowerCase() != 'li'){
22675                             e.stopEvent();
22676                             r.pasteHTML('<br />');
22677                             r.collapse(false);
22678                             r.select();
22679                         }
22680                     }
22681                 }
22682                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22683                     this.cleanUpPaste.defer(100, this);
22684                     return;
22685                 }
22686                 
22687                 
22688             };
22689         }else if(Roo.isOpera){
22690             return function(e){
22691                 var k = e.getKey();
22692                 if(k == e.TAB){
22693                     e.stopEvent();
22694                     this.win.focus();
22695                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22696                     this.deferFocus();
22697                 }
22698                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22699                     this.cleanUpPaste.defer(100, this);
22700                     return;
22701                 }
22702                 
22703             };
22704         }else if(Roo.isSafari){
22705             return function(e){
22706                 var k = e.getKey();
22707                 
22708                 if(k == e.TAB){
22709                     e.stopEvent();
22710                     this.execCmd('InsertText','\t');
22711                     this.deferFocus();
22712                     return;
22713                 }
22714                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22715                     this.cleanUpPaste.defer(100, this);
22716                     return;
22717                 }
22718                 
22719              };
22720         }
22721     }(),
22722     
22723     getAllAncestors: function()
22724     {
22725         var p = this.getSelectedNode();
22726         var a = [];
22727         if (!p) {
22728             a.push(p); // push blank onto stack..
22729             p = this.getParentElement();
22730         }
22731         
22732         
22733         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22734             a.push(p);
22735             p = p.parentNode;
22736         }
22737         a.push(this.doc.body);
22738         return a;
22739     },
22740     lastSel : false,
22741     lastSelNode : false,
22742     
22743     
22744     getSelection : function() 
22745     {
22746         this.assignDocWin();
22747         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22748     },
22749     
22750     getSelectedNode: function() 
22751     {
22752         // this may only work on Gecko!!!
22753         
22754         // should we cache this!!!!
22755         
22756         
22757         
22758          
22759         var range = this.createRange(this.getSelection()).cloneRange();
22760         
22761         if (Roo.isIE) {
22762             var parent = range.parentElement();
22763             while (true) {
22764                 var testRange = range.duplicate();
22765                 testRange.moveToElementText(parent);
22766                 if (testRange.inRange(range)) {
22767                     break;
22768                 }
22769                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22770                     break;
22771                 }
22772                 parent = parent.parentElement;
22773             }
22774             return parent;
22775         }
22776         
22777         // is ancestor a text element.
22778         var ac =  range.commonAncestorContainer;
22779         if (ac.nodeType == 3) {
22780             ac = ac.parentNode;
22781         }
22782         
22783         var ar = ac.childNodes;
22784          
22785         var nodes = [];
22786         var other_nodes = [];
22787         var has_other_nodes = false;
22788         for (var i=0;i<ar.length;i++) {
22789             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22790                 continue;
22791             }
22792             // fullly contained node.
22793             
22794             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22795                 nodes.push(ar[i]);
22796                 continue;
22797             }
22798             
22799             // probably selected..
22800             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22801                 other_nodes.push(ar[i]);
22802                 continue;
22803             }
22804             // outer..
22805             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22806                 continue;
22807             }
22808             
22809             
22810             has_other_nodes = true;
22811         }
22812         if (!nodes.length && other_nodes.length) {
22813             nodes= other_nodes;
22814         }
22815         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22816             return false;
22817         }
22818         
22819         return nodes[0];
22820     },
22821     createRange: function(sel)
22822     {
22823         // this has strange effects when using with 
22824         // top toolbar - not sure if it's a great idea.
22825         //this.editor.contentWindow.focus();
22826         if (typeof sel != "undefined") {
22827             try {
22828                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22829             } catch(e) {
22830                 return this.doc.createRange();
22831             }
22832         } else {
22833             return this.doc.createRange();
22834         }
22835     },
22836     getParentElement: function()
22837     {
22838         
22839         this.assignDocWin();
22840         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22841         
22842         var range = this.createRange(sel);
22843          
22844         try {
22845             var p = range.commonAncestorContainer;
22846             while (p.nodeType == 3) { // text node
22847                 p = p.parentNode;
22848             }
22849             return p;
22850         } catch (e) {
22851             return null;
22852         }
22853     
22854     },
22855     /***
22856      *
22857      * Range intersection.. the hard stuff...
22858      *  '-1' = before
22859      *  '0' = hits..
22860      *  '1' = after.
22861      *         [ -- selected range --- ]
22862      *   [fail]                        [fail]
22863      *
22864      *    basically..
22865      *      if end is before start or  hits it. fail.
22866      *      if start is after end or hits it fail.
22867      *
22868      *   if either hits (but other is outside. - then it's not 
22869      *   
22870      *    
22871      **/
22872     
22873     
22874     // @see http://www.thismuchiknow.co.uk/?p=64.
22875     rangeIntersectsNode : function(range, node)
22876     {
22877         var nodeRange = node.ownerDocument.createRange();
22878         try {
22879             nodeRange.selectNode(node);
22880         } catch (e) {
22881             nodeRange.selectNodeContents(node);
22882         }
22883     
22884         var rangeStartRange = range.cloneRange();
22885         rangeStartRange.collapse(true);
22886     
22887         var rangeEndRange = range.cloneRange();
22888         rangeEndRange.collapse(false);
22889     
22890         var nodeStartRange = nodeRange.cloneRange();
22891         nodeStartRange.collapse(true);
22892     
22893         var nodeEndRange = nodeRange.cloneRange();
22894         nodeEndRange.collapse(false);
22895     
22896         return rangeStartRange.compareBoundaryPoints(
22897                  Range.START_TO_START, nodeEndRange) == -1 &&
22898                rangeEndRange.compareBoundaryPoints(
22899                  Range.START_TO_START, nodeStartRange) == 1;
22900         
22901          
22902     },
22903     rangeCompareNode : function(range, node)
22904     {
22905         var nodeRange = node.ownerDocument.createRange();
22906         try {
22907             nodeRange.selectNode(node);
22908         } catch (e) {
22909             nodeRange.selectNodeContents(node);
22910         }
22911         
22912         
22913         range.collapse(true);
22914     
22915         nodeRange.collapse(true);
22916      
22917         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22918         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22919          
22920         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22921         
22922         var nodeIsBefore   =  ss == 1;
22923         var nodeIsAfter    = ee == -1;
22924         
22925         if (nodeIsBefore && nodeIsAfter) {
22926             return 0; // outer
22927         }
22928         if (!nodeIsBefore && nodeIsAfter) {
22929             return 1; //right trailed.
22930         }
22931         
22932         if (nodeIsBefore && !nodeIsAfter) {
22933             return 2;  // left trailed.
22934         }
22935         // fully contined.
22936         return 3;
22937     },
22938
22939     // private? - in a new class?
22940     cleanUpPaste :  function()
22941     {
22942         // cleans up the whole document..
22943         Roo.log('cleanuppaste');
22944         
22945         this.cleanUpChildren(this.doc.body);
22946         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22947         if (clean != this.doc.body.innerHTML) {
22948             this.doc.body.innerHTML = clean;
22949         }
22950         
22951     },
22952     
22953     cleanWordChars : function(input) {// change the chars to hex code
22954         var he = Roo.HtmlEditorCore;
22955         
22956         var output = input;
22957         Roo.each(he.swapCodes, function(sw) { 
22958             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22959             
22960             output = output.replace(swapper, sw[1]);
22961         });
22962         
22963         return output;
22964     },
22965     
22966     
22967     cleanUpChildren : function (n)
22968     {
22969         if (!n.childNodes.length) {
22970             return;
22971         }
22972         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22973            this.cleanUpChild(n.childNodes[i]);
22974         }
22975     },
22976     
22977     
22978         
22979     
22980     cleanUpChild : function (node)
22981     {
22982         var ed = this;
22983         //console.log(node);
22984         if (node.nodeName == "#text") {
22985             // clean up silly Windows -- stuff?
22986             return; 
22987         }
22988         if (node.nodeName == "#comment") {
22989             node.parentNode.removeChild(node);
22990             // clean up silly Windows -- stuff?
22991             return; 
22992         }
22993         var lcname = node.tagName.toLowerCase();
22994         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22995         // whitelist of tags..
22996         
22997         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22998             // remove node.
22999             node.parentNode.removeChild(node);
23000             return;
23001             
23002         }
23003         
23004         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23005         
23006         // remove <a name=....> as rendering on yahoo mailer is borked with this.
23007         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23008         
23009         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23010         //    remove_keep_children = true;
23011         //}
23012         
23013         if (remove_keep_children) {
23014             this.cleanUpChildren(node);
23015             // inserts everything just before this node...
23016             while (node.childNodes.length) {
23017                 var cn = node.childNodes[0];
23018                 node.removeChild(cn);
23019                 node.parentNode.insertBefore(cn, node);
23020             }
23021             node.parentNode.removeChild(node);
23022             return;
23023         }
23024         
23025         if (!node.attributes || !node.attributes.length) {
23026             this.cleanUpChildren(node);
23027             return;
23028         }
23029         
23030         function cleanAttr(n,v)
23031         {
23032             
23033             if (v.match(/^\./) || v.match(/^\//)) {
23034                 return;
23035             }
23036             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23037                 return;
23038             }
23039             if (v.match(/^#/)) {
23040                 return;
23041             }
23042 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23043             node.removeAttribute(n);
23044             
23045         }
23046         
23047         var cwhite = this.cwhite;
23048         var cblack = this.cblack;
23049             
23050         function cleanStyle(n,v)
23051         {
23052             if (v.match(/expression/)) { //XSS?? should we even bother..
23053                 node.removeAttribute(n);
23054                 return;
23055             }
23056             
23057             var parts = v.split(/;/);
23058             var clean = [];
23059             
23060             Roo.each(parts, function(p) {
23061                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23062                 if (!p.length) {
23063                     return true;
23064                 }
23065                 var l = p.split(':').shift().replace(/\s+/g,'');
23066                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23067                 
23068                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23069 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23070                     //node.removeAttribute(n);
23071                     return true;
23072                 }
23073                 //Roo.log()
23074                 // only allow 'c whitelisted system attributes'
23075                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23076 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23077                     //node.removeAttribute(n);
23078                     return true;
23079                 }
23080                 
23081                 
23082                  
23083                 
23084                 clean.push(p);
23085                 return true;
23086             });
23087             if (clean.length) { 
23088                 node.setAttribute(n, clean.join(';'));
23089             } else {
23090                 node.removeAttribute(n);
23091             }
23092             
23093         }
23094         
23095         
23096         for (var i = node.attributes.length-1; i > -1 ; i--) {
23097             var a = node.attributes[i];
23098             //console.log(a);
23099             
23100             if (a.name.toLowerCase().substr(0,2)=='on')  {
23101                 node.removeAttribute(a.name);
23102                 continue;
23103             }
23104             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23105                 node.removeAttribute(a.name);
23106                 continue;
23107             }
23108             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23109                 cleanAttr(a.name,a.value); // fixme..
23110                 continue;
23111             }
23112             if (a.name == 'style') {
23113                 cleanStyle(a.name,a.value);
23114                 continue;
23115             }
23116             /// clean up MS crap..
23117             // tecnically this should be a list of valid class'es..
23118             
23119             
23120             if (a.name == 'class') {
23121                 if (a.value.match(/^Mso/)) {
23122                     node.className = '';
23123                 }
23124                 
23125                 if (a.value.match(/^body$/)) {
23126                     node.className = '';
23127                 }
23128                 continue;
23129             }
23130             
23131             // style cleanup!?
23132             // class cleanup?
23133             
23134         }
23135         
23136         
23137         this.cleanUpChildren(node);
23138         
23139         
23140     },
23141     
23142     /**
23143      * Clean up MS wordisms...
23144      */
23145     cleanWord : function(node)
23146     {
23147         
23148         
23149         if (!node) {
23150             this.cleanWord(this.doc.body);
23151             return;
23152         }
23153         if (node.nodeName == "#text") {
23154             // clean up silly Windows -- stuff?
23155             return; 
23156         }
23157         if (node.nodeName == "#comment") {
23158             node.parentNode.removeChild(node);
23159             // clean up silly Windows -- stuff?
23160             return; 
23161         }
23162         
23163         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23164             node.parentNode.removeChild(node);
23165             return;
23166         }
23167         
23168         // remove - but keep children..
23169         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23170             while (node.childNodes.length) {
23171                 var cn = node.childNodes[0];
23172                 node.removeChild(cn);
23173                 node.parentNode.insertBefore(cn, node);
23174             }
23175             node.parentNode.removeChild(node);
23176             this.iterateChildren(node, this.cleanWord);
23177             return;
23178         }
23179         // clean styles
23180         if (node.className.length) {
23181             
23182             var cn = node.className.split(/\W+/);
23183             var cna = [];
23184             Roo.each(cn, function(cls) {
23185                 if (cls.match(/Mso[a-zA-Z]+/)) {
23186                     return;
23187                 }
23188                 cna.push(cls);
23189             });
23190             node.className = cna.length ? cna.join(' ') : '';
23191             if (!cna.length) {
23192                 node.removeAttribute("class");
23193             }
23194         }
23195         
23196         if (node.hasAttribute("lang")) {
23197             node.removeAttribute("lang");
23198         }
23199         
23200         if (node.hasAttribute("style")) {
23201             
23202             var styles = node.getAttribute("style").split(";");
23203             var nstyle = [];
23204             Roo.each(styles, function(s) {
23205                 if (!s.match(/:/)) {
23206                     return;
23207                 }
23208                 var kv = s.split(":");
23209                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23210                     return;
23211                 }
23212                 // what ever is left... we allow.
23213                 nstyle.push(s);
23214             });
23215             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23216             if (!nstyle.length) {
23217                 node.removeAttribute('style');
23218             }
23219         }
23220         this.iterateChildren(node, this.cleanWord);
23221         
23222         
23223         
23224     },
23225     /**
23226      * iterateChildren of a Node, calling fn each time, using this as the scole..
23227      * @param {DomNode} node node to iterate children of.
23228      * @param {Function} fn method of this class to call on each item.
23229      */
23230     iterateChildren : function(node, fn)
23231     {
23232         if (!node.childNodes.length) {
23233                 return;
23234         }
23235         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23236            fn.call(this, node.childNodes[i])
23237         }
23238     },
23239     
23240     
23241     /**
23242      * cleanTableWidths.
23243      *
23244      * Quite often pasting from word etc.. results in tables with column and widths.
23245      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23246      *
23247      */
23248     cleanTableWidths : function(node)
23249     {
23250          
23251          
23252         if (!node) {
23253             this.cleanTableWidths(this.doc.body);
23254             return;
23255         }
23256         
23257         // ignore list...
23258         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23259             return; 
23260         }
23261         Roo.log(node.tagName);
23262         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23263             this.iterateChildren(node, this.cleanTableWidths);
23264             return;
23265         }
23266         if (node.hasAttribute('width')) {
23267             node.removeAttribute('width');
23268         }
23269         
23270          
23271         if (node.hasAttribute("style")) {
23272             // pretty basic...
23273             
23274             var styles = node.getAttribute("style").split(";");
23275             var nstyle = [];
23276             Roo.each(styles, function(s) {
23277                 if (!s.match(/:/)) {
23278                     return;
23279                 }
23280                 var kv = s.split(":");
23281                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23282                     return;
23283                 }
23284                 // what ever is left... we allow.
23285                 nstyle.push(s);
23286             });
23287             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23288             if (!nstyle.length) {
23289                 node.removeAttribute('style');
23290             }
23291         }
23292         
23293         this.iterateChildren(node, this.cleanTableWidths);
23294         
23295         
23296     },
23297     
23298     
23299     
23300     
23301     domToHTML : function(currentElement, depth, nopadtext) {
23302         
23303         depth = depth || 0;
23304         nopadtext = nopadtext || false;
23305     
23306         if (!currentElement) {
23307             return this.domToHTML(this.doc.body);
23308         }
23309         
23310         //Roo.log(currentElement);
23311         var j;
23312         var allText = false;
23313         var nodeName = currentElement.nodeName;
23314         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23315         
23316         if  (nodeName == '#text') {
23317             
23318             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23319         }
23320         
23321         
23322         var ret = '';
23323         if (nodeName != 'BODY') {
23324              
23325             var i = 0;
23326             // Prints the node tagName, such as <A>, <IMG>, etc
23327             if (tagName) {
23328                 var attr = [];
23329                 for(i = 0; i < currentElement.attributes.length;i++) {
23330                     // quoting?
23331                     var aname = currentElement.attributes.item(i).name;
23332                     if (!currentElement.attributes.item(i).value.length) {
23333                         continue;
23334                     }
23335                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23336                 }
23337                 
23338                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23339             } 
23340             else {
23341                 
23342                 // eack
23343             }
23344         } else {
23345             tagName = false;
23346         }
23347         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23348             return ret;
23349         }
23350         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23351             nopadtext = true;
23352         }
23353         
23354         
23355         // Traverse the tree
23356         i = 0;
23357         var currentElementChild = currentElement.childNodes.item(i);
23358         var allText = true;
23359         var innerHTML  = '';
23360         lastnode = '';
23361         while (currentElementChild) {
23362             // Formatting code (indent the tree so it looks nice on the screen)
23363             var nopad = nopadtext;
23364             if (lastnode == 'SPAN') {
23365                 nopad  = true;
23366             }
23367             // text
23368             if  (currentElementChild.nodeName == '#text') {
23369                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23370                 toadd = nopadtext ? toadd : toadd.trim();
23371                 if (!nopad && toadd.length > 80) {
23372                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23373                 }
23374                 innerHTML  += toadd;
23375                 
23376                 i++;
23377                 currentElementChild = currentElement.childNodes.item(i);
23378                 lastNode = '';
23379                 continue;
23380             }
23381             allText = false;
23382             
23383             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23384                 
23385             // Recursively traverse the tree structure of the child node
23386             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23387             lastnode = currentElementChild.nodeName;
23388             i++;
23389             currentElementChild=currentElement.childNodes.item(i);
23390         }
23391         
23392         ret += innerHTML;
23393         
23394         if (!allText) {
23395                 // The remaining code is mostly for formatting the tree
23396             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23397         }
23398         
23399         
23400         if (tagName) {
23401             ret+= "</"+tagName+">";
23402         }
23403         return ret;
23404         
23405     },
23406         
23407     applyBlacklists : function()
23408     {
23409         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23410         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23411         
23412         this.white = [];
23413         this.black = [];
23414         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23415             if (b.indexOf(tag) > -1) {
23416                 return;
23417             }
23418             this.white.push(tag);
23419             
23420         }, this);
23421         
23422         Roo.each(w, function(tag) {
23423             if (b.indexOf(tag) > -1) {
23424                 return;
23425             }
23426             if (this.white.indexOf(tag) > -1) {
23427                 return;
23428             }
23429             this.white.push(tag);
23430             
23431         }, this);
23432         
23433         
23434         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23435             if (w.indexOf(tag) > -1) {
23436                 return;
23437             }
23438             this.black.push(tag);
23439             
23440         }, this);
23441         
23442         Roo.each(b, function(tag) {
23443             if (w.indexOf(tag) > -1) {
23444                 return;
23445             }
23446             if (this.black.indexOf(tag) > -1) {
23447                 return;
23448             }
23449             this.black.push(tag);
23450             
23451         }, this);
23452         
23453         
23454         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23455         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23456         
23457         this.cwhite = [];
23458         this.cblack = [];
23459         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23460             if (b.indexOf(tag) > -1) {
23461                 return;
23462             }
23463             this.cwhite.push(tag);
23464             
23465         }, this);
23466         
23467         Roo.each(w, function(tag) {
23468             if (b.indexOf(tag) > -1) {
23469                 return;
23470             }
23471             if (this.cwhite.indexOf(tag) > -1) {
23472                 return;
23473             }
23474             this.cwhite.push(tag);
23475             
23476         }, this);
23477         
23478         
23479         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23480             if (w.indexOf(tag) > -1) {
23481                 return;
23482             }
23483             this.cblack.push(tag);
23484             
23485         }, this);
23486         
23487         Roo.each(b, function(tag) {
23488             if (w.indexOf(tag) > -1) {
23489                 return;
23490             }
23491             if (this.cblack.indexOf(tag) > -1) {
23492                 return;
23493             }
23494             this.cblack.push(tag);
23495             
23496         }, this);
23497     },
23498     
23499     setStylesheets : function(stylesheets)
23500     {
23501         if(typeof(stylesheets) == 'string'){
23502             Roo.get(this.iframe.contentDocument.head).createChild({
23503                 tag : 'link',
23504                 rel : 'stylesheet',
23505                 type : 'text/css',
23506                 href : stylesheets
23507             });
23508             
23509             return;
23510         }
23511         var _this = this;
23512      
23513         Roo.each(stylesheets, function(s) {
23514             if(!s.length){
23515                 return;
23516             }
23517             
23518             Roo.get(_this.iframe.contentDocument.head).createChild({
23519                 tag : 'link',
23520                 rel : 'stylesheet',
23521                 type : 'text/css',
23522                 href : s
23523             });
23524         });
23525
23526         
23527     },
23528     
23529     removeStylesheets : function()
23530     {
23531         var _this = this;
23532         
23533         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23534             s.remove();
23535         });
23536     },
23537     
23538     setStyle : function(style)
23539     {
23540         Roo.get(this.iframe.contentDocument.head).createChild({
23541             tag : 'style',
23542             type : 'text/css',
23543             html : style
23544         });
23545
23546         return;
23547     }
23548     
23549     // hide stuff that is not compatible
23550     /**
23551      * @event blur
23552      * @hide
23553      */
23554     /**
23555      * @event change
23556      * @hide
23557      */
23558     /**
23559      * @event focus
23560      * @hide
23561      */
23562     /**
23563      * @event specialkey
23564      * @hide
23565      */
23566     /**
23567      * @cfg {String} fieldClass @hide
23568      */
23569     /**
23570      * @cfg {String} focusClass @hide
23571      */
23572     /**
23573      * @cfg {String} autoCreate @hide
23574      */
23575     /**
23576      * @cfg {String} inputType @hide
23577      */
23578     /**
23579      * @cfg {String} invalidClass @hide
23580      */
23581     /**
23582      * @cfg {String} invalidText @hide
23583      */
23584     /**
23585      * @cfg {String} msgFx @hide
23586      */
23587     /**
23588      * @cfg {String} validateOnBlur @hide
23589      */
23590 });
23591
23592 Roo.HtmlEditorCore.white = [
23593         'area', 'br', 'img', 'input', 'hr', 'wbr',
23594         
23595        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23596        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23597        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23598        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23599        'table',   'ul',         'xmp', 
23600        
23601        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23602       'thead',   'tr', 
23603      
23604       'dir', 'menu', 'ol', 'ul', 'dl',
23605        
23606       'embed',  'object'
23607 ];
23608
23609
23610 Roo.HtmlEditorCore.black = [
23611     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23612         'applet', // 
23613         'base',   'basefont', 'bgsound', 'blink',  'body', 
23614         'frame',  'frameset', 'head',    'html',   'ilayer', 
23615         'iframe', 'layer',  'link',     'meta',    'object',   
23616         'script', 'style' ,'title',  'xml' // clean later..
23617 ];
23618 Roo.HtmlEditorCore.clean = [
23619     'script', 'style', 'title', 'xml'
23620 ];
23621 Roo.HtmlEditorCore.remove = [
23622     'font'
23623 ];
23624 // attributes..
23625
23626 Roo.HtmlEditorCore.ablack = [
23627     'on'
23628 ];
23629     
23630 Roo.HtmlEditorCore.aclean = [ 
23631     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23632 ];
23633
23634 // protocols..
23635 Roo.HtmlEditorCore.pwhite= [
23636         'http',  'https',  'mailto'
23637 ];
23638
23639 // white listed style attributes.
23640 Roo.HtmlEditorCore.cwhite= [
23641       //  'text-align', /// default is to allow most things..
23642       
23643          
23644 //        'font-size'//??
23645 ];
23646
23647 // black listed style attributes.
23648 Roo.HtmlEditorCore.cblack= [
23649       //  'font-size' -- this can be set by the project 
23650 ];
23651
23652
23653 Roo.HtmlEditorCore.swapCodes   =[ 
23654     [    8211, "--" ], 
23655     [    8212, "--" ], 
23656     [    8216,  "'" ],  
23657     [    8217, "'" ],  
23658     [    8220, '"' ],  
23659     [    8221, '"' ],  
23660     [    8226, "*" ],  
23661     [    8230, "..." ]
23662 ]; 
23663
23664     /*
23665  * - LGPL
23666  *
23667  * HtmlEditor
23668  * 
23669  */
23670
23671 /**
23672  * @class Roo.bootstrap.HtmlEditor
23673  * @extends Roo.bootstrap.TextArea
23674  * Bootstrap HtmlEditor class
23675
23676  * @constructor
23677  * Create a new HtmlEditor
23678  * @param {Object} config The config object
23679  */
23680
23681 Roo.bootstrap.HtmlEditor = function(config){
23682     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23683     if (!this.toolbars) {
23684         this.toolbars = [];
23685     }
23686     
23687     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23688     this.addEvents({
23689             /**
23690              * @event initialize
23691              * Fires when the editor is fully initialized (including the iframe)
23692              * @param {HtmlEditor} this
23693              */
23694             initialize: true,
23695             /**
23696              * @event activate
23697              * Fires when the editor is first receives the focus. Any insertion must wait
23698              * until after this event.
23699              * @param {HtmlEditor} this
23700              */
23701             activate: true,
23702              /**
23703              * @event beforesync
23704              * Fires before the textarea is updated with content from the editor iframe. Return false
23705              * to cancel the sync.
23706              * @param {HtmlEditor} this
23707              * @param {String} html
23708              */
23709             beforesync: true,
23710              /**
23711              * @event beforepush
23712              * Fires before the iframe editor is updated with content from the textarea. Return false
23713              * to cancel the push.
23714              * @param {HtmlEditor} this
23715              * @param {String} html
23716              */
23717             beforepush: true,
23718              /**
23719              * @event sync
23720              * Fires when the textarea is updated with content from the editor iframe.
23721              * @param {HtmlEditor} this
23722              * @param {String} html
23723              */
23724             sync: true,
23725              /**
23726              * @event push
23727              * Fires when the iframe editor is updated with content from the textarea.
23728              * @param {HtmlEditor} this
23729              * @param {String} html
23730              */
23731             push: true,
23732              /**
23733              * @event editmodechange
23734              * Fires when the editor switches edit modes
23735              * @param {HtmlEditor} this
23736              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23737              */
23738             editmodechange: true,
23739             /**
23740              * @event editorevent
23741              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23742              * @param {HtmlEditor} this
23743              */
23744             editorevent: true,
23745             /**
23746              * @event firstfocus
23747              * Fires when on first focus - needed by toolbars..
23748              * @param {HtmlEditor} this
23749              */
23750             firstfocus: true,
23751             /**
23752              * @event autosave
23753              * Auto save the htmlEditor value as a file into Events
23754              * @param {HtmlEditor} this
23755              */
23756             autosave: true,
23757             /**
23758              * @event savedpreview
23759              * preview the saved version of htmlEditor
23760              * @param {HtmlEditor} this
23761              */
23762             savedpreview: true
23763         });
23764 };
23765
23766
23767 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23768     
23769     
23770       /**
23771      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23772      */
23773     toolbars : false,
23774     
23775      /**
23776     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23777     */
23778     btns : [],
23779    
23780      /**
23781      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23782      *                        Roo.resizable.
23783      */
23784     resizable : false,
23785      /**
23786      * @cfg {Number} height (in pixels)
23787      */   
23788     height: 300,
23789    /**
23790      * @cfg {Number} width (in pixels)
23791      */   
23792     width: false,
23793     
23794     /**
23795      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23796      * 
23797      */
23798     stylesheets: false,
23799     
23800     // id of frame..
23801     frameId: false,
23802     
23803     // private properties
23804     validationEvent : false,
23805     deferHeight: true,
23806     initialized : false,
23807     activated : false,
23808     
23809     onFocus : Roo.emptyFn,
23810     iframePad:3,
23811     hideMode:'offsets',
23812     
23813     tbContainer : false,
23814     
23815     bodyCls : '',
23816     
23817     toolbarContainer :function() {
23818         return this.wrap.select('.x-html-editor-tb',true).first();
23819     },
23820
23821     /**
23822      * Protected method that will not generally be called directly. It
23823      * is called when the editor creates its toolbar. Override this method if you need to
23824      * add custom toolbar buttons.
23825      * @param {HtmlEditor} editor
23826      */
23827     createToolbar : function(){
23828         Roo.log('renewing');
23829         Roo.log("create toolbars");
23830         
23831         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23832         this.toolbars[0].render(this.toolbarContainer());
23833         
23834         return;
23835         
23836 //        if (!editor.toolbars || !editor.toolbars.length) {
23837 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23838 //        }
23839 //        
23840 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23841 //            editor.toolbars[i] = Roo.factory(
23842 //                    typeof(editor.toolbars[i]) == 'string' ?
23843 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23844 //                Roo.bootstrap.HtmlEditor);
23845 //            editor.toolbars[i].init(editor);
23846 //        }
23847     },
23848
23849      
23850     // private
23851     onRender : function(ct, position)
23852     {
23853        // Roo.log("Call onRender: " + this.xtype);
23854         var _t = this;
23855         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23856       
23857         this.wrap = this.inputEl().wrap({
23858             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23859         });
23860         
23861         this.editorcore.onRender(ct, position);
23862          
23863         if (this.resizable) {
23864             this.resizeEl = new Roo.Resizable(this.wrap, {
23865                 pinned : true,
23866                 wrap: true,
23867                 dynamic : true,
23868                 minHeight : this.height,
23869                 height: this.height,
23870                 handles : this.resizable,
23871                 width: this.width,
23872                 listeners : {
23873                     resize : function(r, w, h) {
23874                         _t.onResize(w,h); // -something
23875                     }
23876                 }
23877             });
23878             
23879         }
23880         this.createToolbar(this);
23881        
23882         
23883         if(!this.width && this.resizable){
23884             this.setSize(this.wrap.getSize());
23885         }
23886         if (this.resizeEl) {
23887             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23888             // should trigger onReize..
23889         }
23890         
23891     },
23892
23893     // private
23894     onResize : function(w, h)
23895     {
23896         Roo.log('resize: ' +w + ',' + h );
23897         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23898         var ew = false;
23899         var eh = false;
23900         
23901         if(this.inputEl() ){
23902             if(typeof w == 'number'){
23903                 var aw = w - this.wrap.getFrameWidth('lr');
23904                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23905                 ew = aw;
23906             }
23907             if(typeof h == 'number'){
23908                  var tbh = -11;  // fixme it needs to tool bar size!
23909                 for (var i =0; i < this.toolbars.length;i++) {
23910                     // fixme - ask toolbars for heights?
23911                     tbh += this.toolbars[i].el.getHeight();
23912                     //if (this.toolbars[i].footer) {
23913                     //    tbh += this.toolbars[i].footer.el.getHeight();
23914                     //}
23915                 }
23916               
23917                 
23918                 
23919                 
23920                 
23921                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23922                 ah -= 5; // knock a few pixes off for look..
23923                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23924                 var eh = ah;
23925             }
23926         }
23927         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23928         this.editorcore.onResize(ew,eh);
23929         
23930     },
23931
23932     /**
23933      * Toggles the editor between standard and source edit mode.
23934      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23935      */
23936     toggleSourceEdit : function(sourceEditMode)
23937     {
23938         this.editorcore.toggleSourceEdit(sourceEditMode);
23939         
23940         if(this.editorcore.sourceEditMode){
23941             Roo.log('editor - showing textarea');
23942             
23943 //            Roo.log('in');
23944 //            Roo.log(this.syncValue());
23945             this.syncValue();
23946             this.inputEl().removeClass(['hide', 'x-hidden']);
23947             this.inputEl().dom.removeAttribute('tabIndex');
23948             this.inputEl().focus();
23949         }else{
23950             Roo.log('editor - hiding textarea');
23951 //            Roo.log('out')
23952 //            Roo.log(this.pushValue()); 
23953             this.pushValue();
23954             
23955             this.inputEl().addClass(['hide', 'x-hidden']);
23956             this.inputEl().dom.setAttribute('tabIndex', -1);
23957             //this.deferFocus();
23958         }
23959          
23960         if(this.resizable){
23961             this.setSize(this.wrap.getSize());
23962         }
23963         
23964         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23965     },
23966  
23967     // private (for BoxComponent)
23968     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23969
23970     // private (for BoxComponent)
23971     getResizeEl : function(){
23972         return this.wrap;
23973     },
23974
23975     // private (for BoxComponent)
23976     getPositionEl : function(){
23977         return this.wrap;
23978     },
23979
23980     // private
23981     initEvents : function(){
23982         this.originalValue = this.getValue();
23983     },
23984
23985 //    /**
23986 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23987 //     * @method
23988 //     */
23989 //    markInvalid : Roo.emptyFn,
23990 //    /**
23991 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23992 //     * @method
23993 //     */
23994 //    clearInvalid : Roo.emptyFn,
23995
23996     setValue : function(v){
23997         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23998         this.editorcore.pushValue();
23999     },
24000
24001      
24002     // private
24003     deferFocus : function(){
24004         this.focus.defer(10, this);
24005     },
24006
24007     // doc'ed in Field
24008     focus : function(){
24009         this.editorcore.focus();
24010         
24011     },
24012       
24013
24014     // private
24015     onDestroy : function(){
24016         
24017         
24018         
24019         if(this.rendered){
24020             
24021             for (var i =0; i < this.toolbars.length;i++) {
24022                 // fixme - ask toolbars for heights?
24023                 this.toolbars[i].onDestroy();
24024             }
24025             
24026             this.wrap.dom.innerHTML = '';
24027             this.wrap.remove();
24028         }
24029     },
24030
24031     // private
24032     onFirstFocus : function(){
24033         //Roo.log("onFirstFocus");
24034         this.editorcore.onFirstFocus();
24035          for (var i =0; i < this.toolbars.length;i++) {
24036             this.toolbars[i].onFirstFocus();
24037         }
24038         
24039     },
24040     
24041     // private
24042     syncValue : function()
24043     {   
24044         this.editorcore.syncValue();
24045     },
24046     
24047     pushValue : function()
24048     {   
24049         this.editorcore.pushValue();
24050     }
24051      
24052     
24053     // hide stuff that is not compatible
24054     /**
24055      * @event blur
24056      * @hide
24057      */
24058     /**
24059      * @event change
24060      * @hide
24061      */
24062     /**
24063      * @event focus
24064      * @hide
24065      */
24066     /**
24067      * @event specialkey
24068      * @hide
24069      */
24070     /**
24071      * @cfg {String} fieldClass @hide
24072      */
24073     /**
24074      * @cfg {String} focusClass @hide
24075      */
24076     /**
24077      * @cfg {String} autoCreate @hide
24078      */
24079     /**
24080      * @cfg {String} inputType @hide
24081      */
24082      
24083     /**
24084      * @cfg {String} invalidText @hide
24085      */
24086     /**
24087      * @cfg {String} msgFx @hide
24088      */
24089     /**
24090      * @cfg {String} validateOnBlur @hide
24091      */
24092 });
24093  
24094     
24095    
24096    
24097    
24098       
24099 Roo.namespace('Roo.bootstrap.htmleditor');
24100 /**
24101  * @class Roo.bootstrap.HtmlEditorToolbar1
24102  * Basic Toolbar
24103  * 
24104  * @example
24105  * Usage:
24106  *
24107  new Roo.bootstrap.HtmlEditor({
24108     ....
24109     toolbars : [
24110         new Roo.bootstrap.HtmlEditorToolbar1({
24111             disable : { fonts: 1 , format: 1, ..., ... , ...],
24112             btns : [ .... ]
24113         })
24114     }
24115      
24116  * 
24117  * @cfg {Object} disable List of elements to disable..
24118  * @cfg {Array} btns List of additional buttons.
24119  * 
24120  * 
24121  * NEEDS Extra CSS? 
24122  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24123  */
24124  
24125 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24126 {
24127     
24128     Roo.apply(this, config);
24129     
24130     // default disabled, based on 'good practice'..
24131     this.disable = this.disable || {};
24132     Roo.applyIf(this.disable, {
24133         fontSize : true,
24134         colors : true,
24135         specialElements : true
24136     });
24137     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24138     
24139     this.editor = config.editor;
24140     this.editorcore = config.editor.editorcore;
24141     
24142     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24143     
24144     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24145     // dont call parent... till later.
24146 }
24147 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24148      
24149     bar : true,
24150     
24151     editor : false,
24152     editorcore : false,
24153     
24154     
24155     formats : [
24156         "p" ,  
24157         "h1","h2","h3","h4","h5","h6", 
24158         "pre", "code", 
24159         "abbr", "acronym", "address", "cite", "samp", "var",
24160         'div','span'
24161     ],
24162     
24163     onRender : function(ct, position)
24164     {
24165        // Roo.log("Call onRender: " + this.xtype);
24166         
24167        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24168        Roo.log(this.el);
24169        this.el.dom.style.marginBottom = '0';
24170        var _this = this;
24171        var editorcore = this.editorcore;
24172        var editor= this.editor;
24173        
24174        var children = [];
24175        var btn = function(id,cmd , toggle, handler, html){
24176        
24177             var  event = toggle ? 'toggle' : 'click';
24178        
24179             var a = {
24180                 size : 'sm',
24181                 xtype: 'Button',
24182                 xns: Roo.bootstrap,
24183                 //glyphicon : id,
24184                 fa: id,
24185                 cmd : id || cmd,
24186                 enableToggle:toggle !== false,
24187                 html : html || '',
24188                 pressed : toggle ? false : null,
24189                 listeners : {}
24190             };
24191             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24192                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24193             };
24194             children.push(a);
24195             return a;
24196        }
24197        
24198     //    var cb_box = function...
24199         
24200         var style = {
24201                 xtype: 'Button',
24202                 size : 'sm',
24203                 xns: Roo.bootstrap,
24204                 fa : 'font',
24205                 //html : 'submit'
24206                 menu : {
24207                     xtype: 'Menu',
24208                     xns: Roo.bootstrap,
24209                     items:  []
24210                 }
24211         };
24212         Roo.each(this.formats, function(f) {
24213             style.menu.items.push({
24214                 xtype :'MenuItem',
24215                 xns: Roo.bootstrap,
24216                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24217                 tagname : f,
24218                 listeners : {
24219                     click : function()
24220                     {
24221                         editorcore.insertTag(this.tagname);
24222                         editor.focus();
24223                     }
24224                 }
24225                 
24226             });
24227         });
24228         children.push(style);   
24229         
24230         btn('bold',false,true);
24231         btn('italic',false,true);
24232         btn('align-left', 'justifyleft',true);
24233         btn('align-center', 'justifycenter',true);
24234         btn('align-right' , 'justifyright',true);
24235         btn('link', false, false, function(btn) {
24236             //Roo.log("create link?");
24237             var url = prompt(this.createLinkText, this.defaultLinkValue);
24238             if(url && url != 'http:/'+'/'){
24239                 this.editorcore.relayCmd('createlink', url);
24240             }
24241         }),
24242         btn('list','insertunorderedlist',true);
24243         btn('pencil', false,true, function(btn){
24244                 Roo.log(this);
24245                 this.toggleSourceEdit(btn.pressed);
24246         });
24247         
24248         if (this.editor.btns.length > 0) {
24249             for (var i = 0; i<this.editor.btns.length; i++) {
24250                 children.push(this.editor.btns[i]);
24251             }
24252         }
24253         
24254         /*
24255         var cog = {
24256                 xtype: 'Button',
24257                 size : 'sm',
24258                 xns: Roo.bootstrap,
24259                 glyphicon : 'cog',
24260                 //html : 'submit'
24261                 menu : {
24262                     xtype: 'Menu',
24263                     xns: Roo.bootstrap,
24264                     items:  []
24265                 }
24266         };
24267         
24268         cog.menu.items.push({
24269             xtype :'MenuItem',
24270             xns: Roo.bootstrap,
24271             html : Clean styles,
24272             tagname : f,
24273             listeners : {
24274                 click : function()
24275                 {
24276                     editorcore.insertTag(this.tagname);
24277                     editor.focus();
24278                 }
24279             }
24280             
24281         });
24282        */
24283         
24284          
24285        this.xtype = 'NavSimplebar';
24286         
24287         for(var i=0;i< children.length;i++) {
24288             
24289             this.buttons.add(this.addxtypeChild(children[i]));
24290             
24291         }
24292         
24293         editor.on('editorevent', this.updateToolbar, this);
24294     },
24295     onBtnClick : function(id)
24296     {
24297        this.editorcore.relayCmd(id);
24298        this.editorcore.focus();
24299     },
24300     
24301     /**
24302      * Protected method that will not generally be called directly. It triggers
24303      * a toolbar update by reading the markup state of the current selection in the editor.
24304      */
24305     updateToolbar: function(){
24306
24307         if(!this.editorcore.activated){
24308             this.editor.onFirstFocus(); // is this neeed?
24309             return;
24310         }
24311
24312         var btns = this.buttons; 
24313         var doc = this.editorcore.doc;
24314         btns.get('bold').setActive(doc.queryCommandState('bold'));
24315         btns.get('italic').setActive(doc.queryCommandState('italic'));
24316         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24317         
24318         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24319         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24320         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24321         
24322         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24323         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24324          /*
24325         
24326         var ans = this.editorcore.getAllAncestors();
24327         if (this.formatCombo) {
24328             
24329             
24330             var store = this.formatCombo.store;
24331             this.formatCombo.setValue("");
24332             for (var i =0; i < ans.length;i++) {
24333                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24334                     // select it..
24335                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24336                     break;
24337                 }
24338             }
24339         }
24340         
24341         
24342         
24343         // hides menus... - so this cant be on a menu...
24344         Roo.bootstrap.MenuMgr.hideAll();
24345         */
24346         Roo.bootstrap.MenuMgr.hideAll();
24347         //this.editorsyncValue();
24348     },
24349     onFirstFocus: function() {
24350         this.buttons.each(function(item){
24351            item.enable();
24352         });
24353     },
24354     toggleSourceEdit : function(sourceEditMode){
24355         
24356           
24357         if(sourceEditMode){
24358             Roo.log("disabling buttons");
24359            this.buttons.each( function(item){
24360                 if(item.cmd != 'pencil'){
24361                     item.disable();
24362                 }
24363             });
24364           
24365         }else{
24366             Roo.log("enabling buttons");
24367             if(this.editorcore.initialized){
24368                 this.buttons.each( function(item){
24369                     item.enable();
24370                 });
24371             }
24372             
24373         }
24374         Roo.log("calling toggole on editor");
24375         // tell the editor that it's been pressed..
24376         this.editor.toggleSourceEdit(sourceEditMode);
24377        
24378     }
24379 });
24380
24381
24382
24383
24384
24385 /**
24386  * @class Roo.bootstrap.Table.AbstractSelectionModel
24387  * @extends Roo.util.Observable
24388  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24389  * implemented by descendant classes.  This class should not be directly instantiated.
24390  * @constructor
24391  */
24392 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24393     this.locked = false;
24394     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24395 };
24396
24397
24398 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24399     /** @ignore Called by the grid automatically. Do not call directly. */
24400     init : function(grid){
24401         this.grid = grid;
24402         this.initEvents();
24403     },
24404
24405     /**
24406      * Locks the selections.
24407      */
24408     lock : function(){
24409         this.locked = true;
24410     },
24411
24412     /**
24413      * Unlocks the selections.
24414      */
24415     unlock : function(){
24416         this.locked = false;
24417     },
24418
24419     /**
24420      * Returns true if the selections are locked.
24421      * @return {Boolean}
24422      */
24423     isLocked : function(){
24424         return this.locked;
24425     }
24426 });
24427 /**
24428  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24429  * @class Roo.bootstrap.Table.RowSelectionModel
24430  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24431  * It supports multiple selections and keyboard selection/navigation. 
24432  * @constructor
24433  * @param {Object} config
24434  */
24435
24436 Roo.bootstrap.Table.RowSelectionModel = function(config){
24437     Roo.apply(this, config);
24438     this.selections = new Roo.util.MixedCollection(false, function(o){
24439         return o.id;
24440     });
24441
24442     this.last = false;
24443     this.lastActive = false;
24444
24445     this.addEvents({
24446         /**
24447              * @event selectionchange
24448              * Fires when the selection changes
24449              * @param {SelectionModel} this
24450              */
24451             "selectionchange" : true,
24452         /**
24453              * @event afterselectionchange
24454              * Fires after the selection changes (eg. by key press or clicking)
24455              * @param {SelectionModel} this
24456              */
24457             "afterselectionchange" : true,
24458         /**
24459              * @event beforerowselect
24460              * Fires when a row is selected being selected, return false to cancel.
24461              * @param {SelectionModel} this
24462              * @param {Number} rowIndex The selected index
24463              * @param {Boolean} keepExisting False if other selections will be cleared
24464              */
24465             "beforerowselect" : true,
24466         /**
24467              * @event rowselect
24468              * Fires when a row is selected.
24469              * @param {SelectionModel} this
24470              * @param {Number} rowIndex The selected index
24471              * @param {Roo.data.Record} r The record
24472              */
24473             "rowselect" : true,
24474         /**
24475              * @event rowdeselect
24476              * Fires when a row is deselected.
24477              * @param {SelectionModel} this
24478              * @param {Number} rowIndex The selected index
24479              */
24480         "rowdeselect" : true
24481     });
24482     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24483     this.locked = false;
24484  };
24485
24486 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24487     /**
24488      * @cfg {Boolean} singleSelect
24489      * True to allow selection of only one row at a time (defaults to false)
24490      */
24491     singleSelect : false,
24492
24493     // private
24494     initEvents : function()
24495     {
24496
24497         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24498         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24499         //}else{ // allow click to work like normal
24500          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24501         //}
24502         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24503         this.grid.on("rowclick", this.handleMouseDown, this);
24504         
24505         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24506             "up" : function(e){
24507                 if(!e.shiftKey){
24508                     this.selectPrevious(e.shiftKey);
24509                 }else if(this.last !== false && this.lastActive !== false){
24510                     var last = this.last;
24511                     this.selectRange(this.last,  this.lastActive-1);
24512                     this.grid.getView().focusRow(this.lastActive);
24513                     if(last !== false){
24514                         this.last = last;
24515                     }
24516                 }else{
24517                     this.selectFirstRow();
24518                 }
24519                 this.fireEvent("afterselectionchange", this);
24520             },
24521             "down" : function(e){
24522                 if(!e.shiftKey){
24523                     this.selectNext(e.shiftKey);
24524                 }else if(this.last !== false && this.lastActive !== false){
24525                     var last = this.last;
24526                     this.selectRange(this.last,  this.lastActive+1);
24527                     this.grid.getView().focusRow(this.lastActive);
24528                     if(last !== false){
24529                         this.last = last;
24530                     }
24531                 }else{
24532                     this.selectFirstRow();
24533                 }
24534                 this.fireEvent("afterselectionchange", this);
24535             },
24536             scope: this
24537         });
24538         this.grid.store.on('load', function(){
24539             this.selections.clear();
24540         },this);
24541         /*
24542         var view = this.grid.view;
24543         view.on("refresh", this.onRefresh, this);
24544         view.on("rowupdated", this.onRowUpdated, this);
24545         view.on("rowremoved", this.onRemove, this);
24546         */
24547     },
24548
24549     // private
24550     onRefresh : function()
24551     {
24552         var ds = this.grid.store, i, v = this.grid.view;
24553         var s = this.selections;
24554         s.each(function(r){
24555             if((i = ds.indexOfId(r.id)) != -1){
24556                 v.onRowSelect(i);
24557             }else{
24558                 s.remove(r);
24559             }
24560         });
24561     },
24562
24563     // private
24564     onRemove : function(v, index, r){
24565         this.selections.remove(r);
24566     },
24567
24568     // private
24569     onRowUpdated : function(v, index, r){
24570         if(this.isSelected(r)){
24571             v.onRowSelect(index);
24572         }
24573     },
24574
24575     /**
24576      * Select records.
24577      * @param {Array} records The records to select
24578      * @param {Boolean} keepExisting (optional) True to keep existing selections
24579      */
24580     selectRecords : function(records, keepExisting)
24581     {
24582         if(!keepExisting){
24583             this.clearSelections();
24584         }
24585             var ds = this.grid.store;
24586         for(var i = 0, len = records.length; i < len; i++){
24587             this.selectRow(ds.indexOf(records[i]), true);
24588         }
24589     },
24590
24591     /**
24592      * Gets the number of selected rows.
24593      * @return {Number}
24594      */
24595     getCount : function(){
24596         return this.selections.length;
24597     },
24598
24599     /**
24600      * Selects the first row in the grid.
24601      */
24602     selectFirstRow : function(){
24603         this.selectRow(0);
24604     },
24605
24606     /**
24607      * Select the last row.
24608      * @param {Boolean} keepExisting (optional) True to keep existing selections
24609      */
24610     selectLastRow : function(keepExisting){
24611         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24612         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24613     },
24614
24615     /**
24616      * Selects the row immediately following the last selected row.
24617      * @param {Boolean} keepExisting (optional) True to keep existing selections
24618      */
24619     selectNext : function(keepExisting)
24620     {
24621             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24622             this.selectRow(this.last+1, keepExisting);
24623             this.grid.getView().focusRow(this.last);
24624         }
24625     },
24626
24627     /**
24628      * Selects the row that precedes the last selected row.
24629      * @param {Boolean} keepExisting (optional) True to keep existing selections
24630      */
24631     selectPrevious : function(keepExisting){
24632         if(this.last){
24633             this.selectRow(this.last-1, keepExisting);
24634             this.grid.getView().focusRow(this.last);
24635         }
24636     },
24637
24638     /**
24639      * Returns the selected records
24640      * @return {Array} Array of selected records
24641      */
24642     getSelections : function(){
24643         return [].concat(this.selections.items);
24644     },
24645
24646     /**
24647      * Returns the first selected record.
24648      * @return {Record}
24649      */
24650     getSelected : function(){
24651         return this.selections.itemAt(0);
24652     },
24653
24654
24655     /**
24656      * Clears all selections.
24657      */
24658     clearSelections : function(fast)
24659     {
24660         if(this.locked) {
24661             return;
24662         }
24663         if(fast !== true){
24664                 var ds = this.grid.store;
24665             var s = this.selections;
24666             s.each(function(r){
24667                 this.deselectRow(ds.indexOfId(r.id));
24668             }, this);
24669             s.clear();
24670         }else{
24671             this.selections.clear();
24672         }
24673         this.last = false;
24674     },
24675
24676
24677     /**
24678      * Selects all rows.
24679      */
24680     selectAll : function(){
24681         if(this.locked) {
24682             return;
24683         }
24684         this.selections.clear();
24685         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24686             this.selectRow(i, true);
24687         }
24688     },
24689
24690     /**
24691      * Returns True if there is a selection.
24692      * @return {Boolean}
24693      */
24694     hasSelection : function(){
24695         return this.selections.length > 0;
24696     },
24697
24698     /**
24699      * Returns True if the specified row is selected.
24700      * @param {Number/Record} record The record or index of the record to check
24701      * @return {Boolean}
24702      */
24703     isSelected : function(index){
24704             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24705         return (r && this.selections.key(r.id) ? true : false);
24706     },
24707
24708     /**
24709      * Returns True if the specified record id is selected.
24710      * @param {String} id The id of record to check
24711      * @return {Boolean}
24712      */
24713     isIdSelected : function(id){
24714         return (this.selections.key(id) ? true : false);
24715     },
24716
24717
24718     // private
24719     handleMouseDBClick : function(e, t){
24720         
24721     },
24722     // private
24723     handleMouseDown : function(e, t)
24724     {
24725             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24726         if(this.isLocked() || rowIndex < 0 ){
24727             return;
24728         };
24729         if(e.shiftKey && this.last !== false){
24730             var last = this.last;
24731             this.selectRange(last, rowIndex, e.ctrlKey);
24732             this.last = last; // reset the last
24733             t.focus();
24734     
24735         }else{
24736             var isSelected = this.isSelected(rowIndex);
24737             //Roo.log("select row:" + rowIndex);
24738             if(isSelected){
24739                 this.deselectRow(rowIndex);
24740             } else {
24741                         this.selectRow(rowIndex, true);
24742             }
24743     
24744             /*
24745                 if(e.button !== 0 && isSelected){
24746                 alert('rowIndex 2: ' + rowIndex);
24747                     view.focusRow(rowIndex);
24748                 }else if(e.ctrlKey && isSelected){
24749                     this.deselectRow(rowIndex);
24750                 }else if(!isSelected){
24751                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24752                     view.focusRow(rowIndex);
24753                 }
24754             */
24755         }
24756         this.fireEvent("afterselectionchange", this);
24757     },
24758     // private
24759     handleDragableRowClick :  function(grid, rowIndex, e) 
24760     {
24761         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24762             this.selectRow(rowIndex, false);
24763             grid.view.focusRow(rowIndex);
24764              this.fireEvent("afterselectionchange", this);
24765         }
24766     },
24767     
24768     /**
24769      * Selects multiple rows.
24770      * @param {Array} rows Array of the indexes of the row to select
24771      * @param {Boolean} keepExisting (optional) True to keep existing selections
24772      */
24773     selectRows : function(rows, keepExisting){
24774         if(!keepExisting){
24775             this.clearSelections();
24776         }
24777         for(var i = 0, len = rows.length; i < len; i++){
24778             this.selectRow(rows[i], true);
24779         }
24780     },
24781
24782     /**
24783      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24784      * @param {Number} startRow The index of the first row in the range
24785      * @param {Number} endRow The index of the last row in the range
24786      * @param {Boolean} keepExisting (optional) True to retain existing selections
24787      */
24788     selectRange : function(startRow, endRow, keepExisting){
24789         if(this.locked) {
24790             return;
24791         }
24792         if(!keepExisting){
24793             this.clearSelections();
24794         }
24795         if(startRow <= endRow){
24796             for(var i = startRow; i <= endRow; i++){
24797                 this.selectRow(i, true);
24798             }
24799         }else{
24800             for(var i = startRow; i >= endRow; i--){
24801                 this.selectRow(i, true);
24802             }
24803         }
24804     },
24805
24806     /**
24807      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24808      * @param {Number} startRow The index of the first row in the range
24809      * @param {Number} endRow The index of the last row in the range
24810      */
24811     deselectRange : function(startRow, endRow, preventViewNotify){
24812         if(this.locked) {
24813             return;
24814         }
24815         for(var i = startRow; i <= endRow; i++){
24816             this.deselectRow(i, preventViewNotify);
24817         }
24818     },
24819
24820     /**
24821      * Selects a row.
24822      * @param {Number} row The index of the row to select
24823      * @param {Boolean} keepExisting (optional) True to keep existing selections
24824      */
24825     selectRow : function(index, keepExisting, preventViewNotify)
24826     {
24827             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24828             return;
24829         }
24830         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24831             if(!keepExisting || this.singleSelect){
24832                 this.clearSelections();
24833             }
24834             
24835             var r = this.grid.store.getAt(index);
24836             //console.log('selectRow - record id :' + r.id);
24837             
24838             this.selections.add(r);
24839             this.last = this.lastActive = index;
24840             if(!preventViewNotify){
24841                 var proxy = new Roo.Element(
24842                                 this.grid.getRowDom(index)
24843                 );
24844                 proxy.addClass('bg-info info');
24845             }
24846             this.fireEvent("rowselect", this, index, r);
24847             this.fireEvent("selectionchange", this);
24848         }
24849     },
24850
24851     /**
24852      * Deselects a row.
24853      * @param {Number} row The index of the row to deselect
24854      */
24855     deselectRow : function(index, preventViewNotify)
24856     {
24857         if(this.locked) {
24858             return;
24859         }
24860         if(this.last == index){
24861             this.last = false;
24862         }
24863         if(this.lastActive == index){
24864             this.lastActive = false;
24865         }
24866         
24867         var r = this.grid.store.getAt(index);
24868         if (!r) {
24869             return;
24870         }
24871         
24872         this.selections.remove(r);
24873         //.console.log('deselectRow - record id :' + r.id);
24874         if(!preventViewNotify){
24875         
24876             var proxy = new Roo.Element(
24877                 this.grid.getRowDom(index)
24878             );
24879             proxy.removeClass('bg-info info');
24880         }
24881         this.fireEvent("rowdeselect", this, index);
24882         this.fireEvent("selectionchange", this);
24883     },
24884
24885     // private
24886     restoreLast : function(){
24887         if(this._last){
24888             this.last = this._last;
24889         }
24890     },
24891
24892     // private
24893     acceptsNav : function(row, col, cm){
24894         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24895     },
24896
24897     // private
24898     onEditorKey : function(field, e){
24899         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24900         if(k == e.TAB){
24901             e.stopEvent();
24902             ed.completeEdit();
24903             if(e.shiftKey){
24904                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24905             }else{
24906                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24907             }
24908         }else if(k == e.ENTER && !e.ctrlKey){
24909             e.stopEvent();
24910             ed.completeEdit();
24911             if(e.shiftKey){
24912                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24913             }else{
24914                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24915             }
24916         }else if(k == e.ESC){
24917             ed.cancelEdit();
24918         }
24919         if(newCell){
24920             g.startEditing(newCell[0], newCell[1]);
24921         }
24922     }
24923 });
24924 /*
24925  * Based on:
24926  * Ext JS Library 1.1.1
24927  * Copyright(c) 2006-2007, Ext JS, LLC.
24928  *
24929  * Originally Released Under LGPL - original licence link has changed is not relivant.
24930  *
24931  * Fork - LGPL
24932  * <script type="text/javascript">
24933  */
24934  
24935 /**
24936  * @class Roo.bootstrap.PagingToolbar
24937  * @extends Roo.bootstrap.NavSimplebar
24938  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24939  * @constructor
24940  * Create a new PagingToolbar
24941  * @param {Object} config The config object
24942  * @param {Roo.data.Store} store
24943  */
24944 Roo.bootstrap.PagingToolbar = function(config)
24945 {
24946     // old args format still supported... - xtype is prefered..
24947         // created from xtype...
24948     
24949     this.ds = config.dataSource;
24950     
24951     if (config.store && !this.ds) {
24952         this.store= Roo.factory(config.store, Roo.data);
24953         this.ds = this.store;
24954         this.ds.xmodule = this.xmodule || false;
24955     }
24956     
24957     this.toolbarItems = [];
24958     if (config.items) {
24959         this.toolbarItems = config.items;
24960     }
24961     
24962     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24963     
24964     this.cursor = 0;
24965     
24966     if (this.ds) { 
24967         this.bind(this.ds);
24968     }
24969     
24970     if (Roo.bootstrap.version == 4) {
24971         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24972     } else {
24973         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24974     }
24975     
24976 };
24977
24978 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24979     /**
24980      * @cfg {Roo.data.Store} dataSource
24981      * The underlying data store providing the paged data
24982      */
24983     /**
24984      * @cfg {String/HTMLElement/Element} container
24985      * container The id or element that will contain the toolbar
24986      */
24987     /**
24988      * @cfg {Boolean} displayInfo
24989      * True to display the displayMsg (defaults to false)
24990      */
24991     /**
24992      * @cfg {Number} pageSize
24993      * The number of records to display per page (defaults to 20)
24994      */
24995     pageSize: 20,
24996     /**
24997      * @cfg {String} displayMsg
24998      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24999      */
25000     displayMsg : 'Displaying {0} - {1} of {2}',
25001     /**
25002      * @cfg {String} emptyMsg
25003      * The message to display when no records are found (defaults to "No data to display")
25004      */
25005     emptyMsg : 'No data to display',
25006     /**
25007      * Customizable piece of the default paging text (defaults to "Page")
25008      * @type String
25009      */
25010     beforePageText : "Page",
25011     /**
25012      * Customizable piece of the default paging text (defaults to "of %0")
25013      * @type String
25014      */
25015     afterPageText : "of {0}",
25016     /**
25017      * Customizable piece of the default paging text (defaults to "First Page")
25018      * @type String
25019      */
25020     firstText : "First Page",
25021     /**
25022      * Customizable piece of the default paging text (defaults to "Previous Page")
25023      * @type String
25024      */
25025     prevText : "Previous Page",
25026     /**
25027      * Customizable piece of the default paging text (defaults to "Next Page")
25028      * @type String
25029      */
25030     nextText : "Next Page",
25031     /**
25032      * Customizable piece of the default paging text (defaults to "Last Page")
25033      * @type String
25034      */
25035     lastText : "Last Page",
25036     /**
25037      * Customizable piece of the default paging text (defaults to "Refresh")
25038      * @type String
25039      */
25040     refreshText : "Refresh",
25041
25042     buttons : false,
25043     // private
25044     onRender : function(ct, position) 
25045     {
25046         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25047         this.navgroup.parentId = this.id;
25048         this.navgroup.onRender(this.el, null);
25049         // add the buttons to the navgroup
25050         
25051         if(this.displayInfo){
25052             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25053             this.displayEl = this.el.select('.x-paging-info', true).first();
25054 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25055 //            this.displayEl = navel.el.select('span',true).first();
25056         }
25057         
25058         var _this = this;
25059         
25060         if(this.buttons){
25061             Roo.each(_this.buttons, function(e){ // this might need to use render????
25062                Roo.factory(e).render(_this.el);
25063             });
25064         }
25065             
25066         Roo.each(_this.toolbarItems, function(e) {
25067             _this.navgroup.addItem(e);
25068         });
25069         
25070         
25071         this.first = this.navgroup.addItem({
25072             tooltip: this.firstText,
25073             cls: "prev btn-outline-secondary",
25074             html : ' <i class="fa fa-step-backward"></i>',
25075             disabled: true,
25076             preventDefault: true,
25077             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25078         });
25079         
25080         this.prev =  this.navgroup.addItem({
25081             tooltip: this.prevText,
25082             cls: "prev btn-outline-secondary",
25083             html : ' <i class="fa fa-backward"></i>',
25084             disabled: true,
25085             preventDefault: true,
25086             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25087         });
25088     //this.addSeparator();
25089         
25090         
25091         var field = this.navgroup.addItem( {
25092             tagtype : 'span',
25093             cls : 'x-paging-position  btn-outline-secondary',
25094              disabled: true,
25095             html : this.beforePageText  +
25096                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25097                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25098          } ); //?? escaped?
25099         
25100         this.field = field.el.select('input', true).first();
25101         this.field.on("keydown", this.onPagingKeydown, this);
25102         this.field.on("focus", function(){this.dom.select();});
25103     
25104     
25105         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25106         //this.field.setHeight(18);
25107         //this.addSeparator();
25108         this.next = this.navgroup.addItem({
25109             tooltip: this.nextText,
25110             cls: "next btn-outline-secondary",
25111             html : ' <i class="fa fa-forward"></i>',
25112             disabled: true,
25113             preventDefault: true,
25114             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25115         });
25116         this.last = this.navgroup.addItem({
25117             tooltip: this.lastText,
25118             html : ' <i class="fa fa-step-forward"></i>',
25119             cls: "next btn-outline-secondary",
25120             disabled: true,
25121             preventDefault: true,
25122             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25123         });
25124     //this.addSeparator();
25125         this.loading = this.navgroup.addItem({
25126             tooltip: this.refreshText,
25127             cls: "btn-outline-secondary",
25128             html : ' <i class="fa fa-refresh"></i>',
25129             preventDefault: true,
25130             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25131         });
25132         
25133     },
25134
25135     // private
25136     updateInfo : function(){
25137         if(this.displayEl){
25138             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25139             var msg = count == 0 ?
25140                 this.emptyMsg :
25141                 String.format(
25142                     this.displayMsg,
25143                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25144                 );
25145             this.displayEl.update(msg);
25146         }
25147     },
25148
25149     // private
25150     onLoad : function(ds, r, o)
25151     {
25152         this.cursor = o.params.start ? o.params.start : 0;
25153         
25154         var d = this.getPageData(),
25155             ap = d.activePage,
25156             ps = d.pages;
25157         
25158         
25159         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25160         this.field.dom.value = ap;
25161         this.first.setDisabled(ap == 1);
25162         this.prev.setDisabled(ap == 1);
25163         this.next.setDisabled(ap == ps);
25164         this.last.setDisabled(ap == ps);
25165         this.loading.enable();
25166         this.updateInfo();
25167     },
25168
25169     // private
25170     getPageData : function(){
25171         var total = this.ds.getTotalCount();
25172         return {
25173             total : total,
25174             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25175             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25176         };
25177     },
25178
25179     // private
25180     onLoadError : function(){
25181         this.loading.enable();
25182     },
25183
25184     // private
25185     onPagingKeydown : function(e){
25186         var k = e.getKey();
25187         var d = this.getPageData();
25188         if(k == e.RETURN){
25189             var v = this.field.dom.value, pageNum;
25190             if(!v || isNaN(pageNum = parseInt(v, 10))){
25191                 this.field.dom.value = d.activePage;
25192                 return;
25193             }
25194             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25195             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25196             e.stopEvent();
25197         }
25198         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))
25199         {
25200           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25201           this.field.dom.value = pageNum;
25202           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25203           e.stopEvent();
25204         }
25205         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25206         {
25207           var v = this.field.dom.value, pageNum; 
25208           var increment = (e.shiftKey) ? 10 : 1;
25209           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25210                 increment *= -1;
25211           }
25212           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25213             this.field.dom.value = d.activePage;
25214             return;
25215           }
25216           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25217           {
25218             this.field.dom.value = parseInt(v, 10) + increment;
25219             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25220             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25221           }
25222           e.stopEvent();
25223         }
25224     },
25225
25226     // private
25227     beforeLoad : function(){
25228         if(this.loading){
25229             this.loading.disable();
25230         }
25231     },
25232
25233     // private
25234     onClick : function(which){
25235         
25236         var ds = this.ds;
25237         if (!ds) {
25238             return;
25239         }
25240         
25241         switch(which){
25242             case "first":
25243                 ds.load({params:{start: 0, limit: this.pageSize}});
25244             break;
25245             case "prev":
25246                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25247             break;
25248             case "next":
25249                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25250             break;
25251             case "last":
25252                 var total = ds.getTotalCount();
25253                 var extra = total % this.pageSize;
25254                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25255                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25256             break;
25257             case "refresh":
25258                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25259             break;
25260         }
25261     },
25262
25263     /**
25264      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25265      * @param {Roo.data.Store} store The data store to unbind
25266      */
25267     unbind : function(ds){
25268         ds.un("beforeload", this.beforeLoad, this);
25269         ds.un("load", this.onLoad, this);
25270         ds.un("loadexception", this.onLoadError, this);
25271         ds.un("remove", this.updateInfo, this);
25272         ds.un("add", this.updateInfo, this);
25273         this.ds = undefined;
25274     },
25275
25276     /**
25277      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25278      * @param {Roo.data.Store} store The data store to bind
25279      */
25280     bind : function(ds){
25281         ds.on("beforeload", this.beforeLoad, this);
25282         ds.on("load", this.onLoad, this);
25283         ds.on("loadexception", this.onLoadError, this);
25284         ds.on("remove", this.updateInfo, this);
25285         ds.on("add", this.updateInfo, this);
25286         this.ds = ds;
25287     }
25288 });/*
25289  * - LGPL
25290  *
25291  * element
25292  * 
25293  */
25294
25295 /**
25296  * @class Roo.bootstrap.MessageBar
25297  * @extends Roo.bootstrap.Component
25298  * Bootstrap MessageBar class
25299  * @cfg {String} html contents of the MessageBar
25300  * @cfg {String} weight (info | success | warning | danger) default info
25301  * @cfg {String} beforeClass insert the bar before the given class
25302  * @cfg {Boolean} closable (true | false) default false
25303  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25304  * 
25305  * @constructor
25306  * Create a new Element
25307  * @param {Object} config The config object
25308  */
25309
25310 Roo.bootstrap.MessageBar = function(config){
25311     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25312 };
25313
25314 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25315     
25316     html: '',
25317     weight: 'info',
25318     closable: false,
25319     fixed: false,
25320     beforeClass: 'bootstrap-sticky-wrap',
25321     
25322     getAutoCreate : function(){
25323         
25324         var cfg = {
25325             tag: 'div',
25326             cls: 'alert alert-dismissable alert-' + this.weight,
25327             cn: [
25328                 {
25329                     tag: 'span',
25330                     cls: 'message',
25331                     html: this.html || ''
25332                 }
25333             ]
25334         };
25335         
25336         if(this.fixed){
25337             cfg.cls += ' alert-messages-fixed';
25338         }
25339         
25340         if(this.closable){
25341             cfg.cn.push({
25342                 tag: 'button',
25343                 cls: 'close',
25344                 html: 'x'
25345             });
25346         }
25347         
25348         return cfg;
25349     },
25350     
25351     onRender : function(ct, position)
25352     {
25353         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25354         
25355         if(!this.el){
25356             var cfg = Roo.apply({},  this.getAutoCreate());
25357             cfg.id = Roo.id();
25358             
25359             if (this.cls) {
25360                 cfg.cls += ' ' + this.cls;
25361             }
25362             if (this.style) {
25363                 cfg.style = this.style;
25364             }
25365             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25366             
25367             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25368         }
25369         
25370         this.el.select('>button.close').on('click', this.hide, this);
25371         
25372     },
25373     
25374     show : function()
25375     {
25376         if (!this.rendered) {
25377             this.render();
25378         }
25379         
25380         this.el.show();
25381         
25382         this.fireEvent('show', this);
25383         
25384     },
25385     
25386     hide : function()
25387     {
25388         if (!this.rendered) {
25389             this.render();
25390         }
25391         
25392         this.el.hide();
25393         
25394         this.fireEvent('hide', this);
25395     },
25396     
25397     update : function()
25398     {
25399 //        var e = this.el.dom.firstChild;
25400 //        
25401 //        if(this.closable){
25402 //            e = e.nextSibling;
25403 //        }
25404 //        
25405 //        e.data = this.html || '';
25406
25407         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25408     }
25409    
25410 });
25411
25412  
25413
25414      /*
25415  * - LGPL
25416  *
25417  * Graph
25418  * 
25419  */
25420
25421
25422 /**
25423  * @class Roo.bootstrap.Graph
25424  * @extends Roo.bootstrap.Component
25425  * Bootstrap Graph class
25426 > Prameters
25427  -sm {number} sm 4
25428  -md {number} md 5
25429  @cfg {String} graphtype  bar | vbar | pie
25430  @cfg {number} g_x coodinator | centre x (pie)
25431  @cfg {number} g_y coodinator | centre y (pie)
25432  @cfg {number} g_r radius (pie)
25433  @cfg {number} g_height height of the chart (respected by all elements in the set)
25434  @cfg {number} g_width width of the chart (respected by all elements in the set)
25435  @cfg {Object} title The title of the chart
25436     
25437  -{Array}  values
25438  -opts (object) options for the chart 
25439      o {
25440      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25441      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25442      o vgutter (number)
25443      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.
25444      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25445      o to
25446      o stretch (boolean)
25447      o }
25448  -opts (object) options for the pie
25449      o{
25450      o cut
25451      o startAngle (number)
25452      o endAngle (number)
25453      } 
25454  *
25455  * @constructor
25456  * Create a new Input
25457  * @param {Object} config The config object
25458  */
25459
25460 Roo.bootstrap.Graph = function(config){
25461     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25462     
25463     this.addEvents({
25464         // img events
25465         /**
25466          * @event click
25467          * The img click event for the img.
25468          * @param {Roo.EventObject} e
25469          */
25470         "click" : true
25471     });
25472 };
25473
25474 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25475     
25476     sm: 4,
25477     md: 5,
25478     graphtype: 'bar',
25479     g_height: 250,
25480     g_width: 400,
25481     g_x: 50,
25482     g_y: 50,
25483     g_r: 30,
25484     opts:{
25485         //g_colors: this.colors,
25486         g_type: 'soft',
25487         g_gutter: '20%'
25488
25489     },
25490     title : false,
25491
25492     getAutoCreate : function(){
25493         
25494         var cfg = {
25495             tag: 'div',
25496             html : null
25497         };
25498         
25499         
25500         return  cfg;
25501     },
25502
25503     onRender : function(ct,position){
25504         
25505         
25506         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25507         
25508         if (typeof(Raphael) == 'undefined') {
25509             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25510             return;
25511         }
25512         
25513         this.raphael = Raphael(this.el.dom);
25514         
25515                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25516                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25517                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25518                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25519                 /*
25520                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25521                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25522                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25523                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25524                 
25525                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25526                 r.barchart(330, 10, 300, 220, data1);
25527                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25528                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25529                 */
25530                 
25531                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25532                 // r.barchart(30, 30, 560, 250,  xdata, {
25533                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25534                 //     axis : "0 0 1 1",
25535                 //     axisxlabels :  xdata
25536                 //     //yvalues : cols,
25537                    
25538                 // });
25539 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25540 //        
25541 //        this.load(null,xdata,{
25542 //                axis : "0 0 1 1",
25543 //                axisxlabels :  xdata
25544 //                });
25545
25546     },
25547
25548     load : function(graphtype,xdata,opts)
25549     {
25550         this.raphael.clear();
25551         if(!graphtype) {
25552             graphtype = this.graphtype;
25553         }
25554         if(!opts){
25555             opts = this.opts;
25556         }
25557         var r = this.raphael,
25558             fin = function () {
25559                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25560             },
25561             fout = function () {
25562                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25563             },
25564             pfin = function() {
25565                 this.sector.stop();
25566                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25567
25568                 if (this.label) {
25569                     this.label[0].stop();
25570                     this.label[0].attr({ r: 7.5 });
25571                     this.label[1].attr({ "font-weight": 800 });
25572                 }
25573             },
25574             pfout = function() {
25575                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25576
25577                 if (this.label) {
25578                     this.label[0].animate({ r: 5 }, 500, "bounce");
25579                     this.label[1].attr({ "font-weight": 400 });
25580                 }
25581             };
25582
25583         switch(graphtype){
25584             case 'bar':
25585                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25586                 break;
25587             case 'hbar':
25588                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25589                 break;
25590             case 'pie':
25591 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25592 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25593 //            
25594                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25595                 
25596                 break;
25597
25598         }
25599         
25600         if(this.title){
25601             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25602         }
25603         
25604     },
25605     
25606     setTitle: function(o)
25607     {
25608         this.title = o;
25609     },
25610     
25611     initEvents: function() {
25612         
25613         if(!this.href){
25614             this.el.on('click', this.onClick, this);
25615         }
25616     },
25617     
25618     onClick : function(e)
25619     {
25620         Roo.log('img onclick');
25621         this.fireEvent('click', this, e);
25622     }
25623    
25624 });
25625
25626  
25627 /*
25628  * - LGPL
25629  *
25630  * numberBox
25631  * 
25632  */
25633 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25634
25635 /**
25636  * @class Roo.bootstrap.dash.NumberBox
25637  * @extends Roo.bootstrap.Component
25638  * Bootstrap NumberBox class
25639  * @cfg {String} headline Box headline
25640  * @cfg {String} content Box content
25641  * @cfg {String} icon Box icon
25642  * @cfg {String} footer Footer text
25643  * @cfg {String} fhref Footer href
25644  * 
25645  * @constructor
25646  * Create a new NumberBox
25647  * @param {Object} config The config object
25648  */
25649
25650
25651 Roo.bootstrap.dash.NumberBox = function(config){
25652     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25653     
25654 };
25655
25656 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25657     
25658     headline : '',
25659     content : '',
25660     icon : '',
25661     footer : '',
25662     fhref : '',
25663     ficon : '',
25664     
25665     getAutoCreate : function(){
25666         
25667         var cfg = {
25668             tag : 'div',
25669             cls : 'small-box ',
25670             cn : [
25671                 {
25672                     tag : 'div',
25673                     cls : 'inner',
25674                     cn :[
25675                         {
25676                             tag : 'h3',
25677                             cls : 'roo-headline',
25678                             html : this.headline
25679                         },
25680                         {
25681                             tag : 'p',
25682                             cls : 'roo-content',
25683                             html : this.content
25684                         }
25685                     ]
25686                 }
25687             ]
25688         };
25689         
25690         if(this.icon){
25691             cfg.cn.push({
25692                 tag : 'div',
25693                 cls : 'icon',
25694                 cn :[
25695                     {
25696                         tag : 'i',
25697                         cls : 'ion ' + this.icon
25698                     }
25699                 ]
25700             });
25701         }
25702         
25703         if(this.footer){
25704             var footer = {
25705                 tag : 'a',
25706                 cls : 'small-box-footer',
25707                 href : this.fhref || '#',
25708                 html : this.footer
25709             };
25710             
25711             cfg.cn.push(footer);
25712             
25713         }
25714         
25715         return  cfg;
25716     },
25717
25718     onRender : function(ct,position){
25719         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25720
25721
25722        
25723                 
25724     },
25725
25726     setHeadline: function (value)
25727     {
25728         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25729     },
25730     
25731     setFooter: function (value, href)
25732     {
25733         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25734         
25735         if(href){
25736             this.el.select('a.small-box-footer',true).first().attr('href', href);
25737         }
25738         
25739     },
25740
25741     setContent: function (value)
25742     {
25743         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25744     },
25745
25746     initEvents: function() 
25747     {   
25748         
25749     }
25750     
25751 });
25752
25753  
25754 /*
25755  * - LGPL
25756  *
25757  * TabBox
25758  * 
25759  */
25760 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25761
25762 /**
25763  * @class Roo.bootstrap.dash.TabBox
25764  * @extends Roo.bootstrap.Component
25765  * Bootstrap TabBox class
25766  * @cfg {String} title Title of the TabBox
25767  * @cfg {String} icon Icon of the TabBox
25768  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25769  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25770  * 
25771  * @constructor
25772  * Create a new TabBox
25773  * @param {Object} config The config object
25774  */
25775
25776
25777 Roo.bootstrap.dash.TabBox = function(config){
25778     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25779     this.addEvents({
25780         // raw events
25781         /**
25782          * @event addpane
25783          * When a pane is added
25784          * @param {Roo.bootstrap.dash.TabPane} pane
25785          */
25786         "addpane" : true,
25787         /**
25788          * @event activatepane
25789          * When a pane is activated
25790          * @param {Roo.bootstrap.dash.TabPane} pane
25791          */
25792         "activatepane" : true
25793         
25794          
25795     });
25796     
25797     this.panes = [];
25798 };
25799
25800 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25801
25802     title : '',
25803     icon : false,
25804     showtabs : true,
25805     tabScrollable : false,
25806     
25807     getChildContainer : function()
25808     {
25809         return this.el.select('.tab-content', true).first();
25810     },
25811     
25812     getAutoCreate : function(){
25813         
25814         var header = {
25815             tag: 'li',
25816             cls: 'pull-left header',
25817             html: this.title,
25818             cn : []
25819         };
25820         
25821         if(this.icon){
25822             header.cn.push({
25823                 tag: 'i',
25824                 cls: 'fa ' + this.icon
25825             });
25826         }
25827         
25828         var h = {
25829             tag: 'ul',
25830             cls: 'nav nav-tabs pull-right',
25831             cn: [
25832                 header
25833             ]
25834         };
25835         
25836         if(this.tabScrollable){
25837             h = {
25838                 tag: 'div',
25839                 cls: 'tab-header',
25840                 cn: [
25841                     {
25842                         tag: 'ul',
25843                         cls: 'nav nav-tabs pull-right',
25844                         cn: [
25845                             header
25846                         ]
25847                     }
25848                 ]
25849             };
25850         }
25851         
25852         var cfg = {
25853             tag: 'div',
25854             cls: 'nav-tabs-custom',
25855             cn: [
25856                 h,
25857                 {
25858                     tag: 'div',
25859                     cls: 'tab-content no-padding',
25860                     cn: []
25861                 }
25862             ]
25863         };
25864
25865         return  cfg;
25866     },
25867     initEvents : function()
25868     {
25869         //Roo.log('add add pane handler');
25870         this.on('addpane', this.onAddPane, this);
25871     },
25872      /**
25873      * Updates the box title
25874      * @param {String} html to set the title to.
25875      */
25876     setTitle : function(value)
25877     {
25878         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25879     },
25880     onAddPane : function(pane)
25881     {
25882         this.panes.push(pane);
25883         //Roo.log('addpane');
25884         //Roo.log(pane);
25885         // tabs are rendere left to right..
25886         if(!this.showtabs){
25887             return;
25888         }
25889         
25890         var ctr = this.el.select('.nav-tabs', true).first();
25891          
25892          
25893         var existing = ctr.select('.nav-tab',true);
25894         var qty = existing.getCount();;
25895         
25896         
25897         var tab = ctr.createChild({
25898             tag : 'li',
25899             cls : 'nav-tab' + (qty ? '' : ' active'),
25900             cn : [
25901                 {
25902                     tag : 'a',
25903                     href:'#',
25904                     html : pane.title
25905                 }
25906             ]
25907         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25908         pane.tab = tab;
25909         
25910         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25911         if (!qty) {
25912             pane.el.addClass('active');
25913         }
25914         
25915                 
25916     },
25917     onTabClick : function(ev,un,ob,pane)
25918     {
25919         //Roo.log('tab - prev default');
25920         ev.preventDefault();
25921         
25922         
25923         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25924         pane.tab.addClass('active');
25925         //Roo.log(pane.title);
25926         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25927         // technically we should have a deactivate event.. but maybe add later.
25928         // and it should not de-activate the selected tab...
25929         this.fireEvent('activatepane', pane);
25930         pane.el.addClass('active');
25931         pane.fireEvent('activate');
25932         
25933         
25934     },
25935     
25936     getActivePane : function()
25937     {
25938         var r = false;
25939         Roo.each(this.panes, function(p) {
25940             if(p.el.hasClass('active')){
25941                 r = p;
25942                 return false;
25943             }
25944             
25945             return;
25946         });
25947         
25948         return r;
25949     }
25950     
25951     
25952 });
25953
25954  
25955 /*
25956  * - LGPL
25957  *
25958  * Tab pane
25959  * 
25960  */
25961 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25962 /**
25963  * @class Roo.bootstrap.TabPane
25964  * @extends Roo.bootstrap.Component
25965  * Bootstrap TabPane class
25966  * @cfg {Boolean} active (false | true) Default false
25967  * @cfg {String} title title of panel
25968
25969  * 
25970  * @constructor
25971  * Create a new TabPane
25972  * @param {Object} config The config object
25973  */
25974
25975 Roo.bootstrap.dash.TabPane = function(config){
25976     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25977     
25978     this.addEvents({
25979         // raw events
25980         /**
25981          * @event activate
25982          * When a pane is activated
25983          * @param {Roo.bootstrap.dash.TabPane} pane
25984          */
25985         "activate" : true
25986          
25987     });
25988 };
25989
25990 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25991     
25992     active : false,
25993     title : '',
25994     
25995     // the tabBox that this is attached to.
25996     tab : false,
25997      
25998     getAutoCreate : function() 
25999     {
26000         var cfg = {
26001             tag: 'div',
26002             cls: 'tab-pane'
26003         };
26004         
26005         if(this.active){
26006             cfg.cls += ' active';
26007         }
26008         
26009         return cfg;
26010     },
26011     initEvents  : function()
26012     {
26013         //Roo.log('trigger add pane handler');
26014         this.parent().fireEvent('addpane', this)
26015     },
26016     
26017      /**
26018      * Updates the tab title 
26019      * @param {String} html to set the title to.
26020      */
26021     setTitle: function(str)
26022     {
26023         if (!this.tab) {
26024             return;
26025         }
26026         this.title = str;
26027         this.tab.select('a', true).first().dom.innerHTML = str;
26028         
26029     }
26030     
26031     
26032     
26033 });
26034
26035  
26036
26037
26038  /*
26039  * - LGPL
26040  *
26041  * menu
26042  * 
26043  */
26044 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26045
26046 /**
26047  * @class Roo.bootstrap.menu.Menu
26048  * @extends Roo.bootstrap.Component
26049  * Bootstrap Menu class - container for Menu
26050  * @cfg {String} html Text of the menu
26051  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26052  * @cfg {String} icon Font awesome icon
26053  * @cfg {String} pos Menu align to (top | bottom) default bottom
26054  * 
26055  * 
26056  * @constructor
26057  * Create a new Menu
26058  * @param {Object} config The config object
26059  */
26060
26061
26062 Roo.bootstrap.menu.Menu = function(config){
26063     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26064     
26065     this.addEvents({
26066         /**
26067          * @event beforeshow
26068          * Fires before this menu is displayed
26069          * @param {Roo.bootstrap.menu.Menu} this
26070          */
26071         beforeshow : true,
26072         /**
26073          * @event beforehide
26074          * Fires before this menu is hidden
26075          * @param {Roo.bootstrap.menu.Menu} this
26076          */
26077         beforehide : true,
26078         /**
26079          * @event show
26080          * Fires after this menu is displayed
26081          * @param {Roo.bootstrap.menu.Menu} this
26082          */
26083         show : true,
26084         /**
26085          * @event hide
26086          * Fires after this menu is hidden
26087          * @param {Roo.bootstrap.menu.Menu} this
26088          */
26089         hide : true,
26090         /**
26091          * @event click
26092          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26093          * @param {Roo.bootstrap.menu.Menu} this
26094          * @param {Roo.EventObject} e
26095          */
26096         click : true
26097     });
26098     
26099 };
26100
26101 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26102     
26103     submenu : false,
26104     html : '',
26105     weight : 'default',
26106     icon : false,
26107     pos : 'bottom',
26108     
26109     
26110     getChildContainer : function() {
26111         if(this.isSubMenu){
26112             return this.el;
26113         }
26114         
26115         return this.el.select('ul.dropdown-menu', true).first();  
26116     },
26117     
26118     getAutoCreate : function()
26119     {
26120         var text = [
26121             {
26122                 tag : 'span',
26123                 cls : 'roo-menu-text',
26124                 html : this.html
26125             }
26126         ];
26127         
26128         if(this.icon){
26129             text.unshift({
26130                 tag : 'i',
26131                 cls : 'fa ' + this.icon
26132             })
26133         }
26134         
26135         
26136         var cfg = {
26137             tag : 'div',
26138             cls : 'btn-group',
26139             cn : [
26140                 {
26141                     tag : 'button',
26142                     cls : 'dropdown-button btn btn-' + this.weight,
26143                     cn : text
26144                 },
26145                 {
26146                     tag : 'button',
26147                     cls : 'dropdown-toggle btn btn-' + this.weight,
26148                     cn : [
26149                         {
26150                             tag : 'span',
26151                             cls : 'caret'
26152                         }
26153                     ]
26154                 },
26155                 {
26156                     tag : 'ul',
26157                     cls : 'dropdown-menu'
26158                 }
26159             ]
26160             
26161         };
26162         
26163         if(this.pos == 'top'){
26164             cfg.cls += ' dropup';
26165         }
26166         
26167         if(this.isSubMenu){
26168             cfg = {
26169                 tag : 'ul',
26170                 cls : 'dropdown-menu'
26171             }
26172         }
26173         
26174         return cfg;
26175     },
26176     
26177     onRender : function(ct, position)
26178     {
26179         this.isSubMenu = ct.hasClass('dropdown-submenu');
26180         
26181         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26182     },
26183     
26184     initEvents : function() 
26185     {
26186         if(this.isSubMenu){
26187             return;
26188         }
26189         
26190         this.hidden = true;
26191         
26192         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26193         this.triggerEl.on('click', this.onTriggerPress, this);
26194         
26195         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26196         this.buttonEl.on('click', this.onClick, this);
26197         
26198     },
26199     
26200     list : function()
26201     {
26202         if(this.isSubMenu){
26203             return this.el;
26204         }
26205         
26206         return this.el.select('ul.dropdown-menu', true).first();
26207     },
26208     
26209     onClick : function(e)
26210     {
26211         this.fireEvent("click", this, e);
26212     },
26213     
26214     onTriggerPress  : function(e)
26215     {   
26216         if (this.isVisible()) {
26217             this.hide();
26218         } else {
26219             this.show();
26220         }
26221     },
26222     
26223     isVisible : function(){
26224         return !this.hidden;
26225     },
26226     
26227     show : function()
26228     {
26229         this.fireEvent("beforeshow", this);
26230         
26231         this.hidden = false;
26232         this.el.addClass('open');
26233         
26234         Roo.get(document).on("mouseup", this.onMouseUp, this);
26235         
26236         this.fireEvent("show", this);
26237         
26238         
26239     },
26240     
26241     hide : function()
26242     {
26243         this.fireEvent("beforehide", this);
26244         
26245         this.hidden = true;
26246         this.el.removeClass('open');
26247         
26248         Roo.get(document).un("mouseup", this.onMouseUp);
26249         
26250         this.fireEvent("hide", this);
26251     },
26252     
26253     onMouseUp : function()
26254     {
26255         this.hide();
26256     }
26257     
26258 });
26259
26260  
26261  /*
26262  * - LGPL
26263  *
26264  * menu item
26265  * 
26266  */
26267 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26268
26269 /**
26270  * @class Roo.bootstrap.menu.Item
26271  * @extends Roo.bootstrap.Component
26272  * Bootstrap MenuItem class
26273  * @cfg {Boolean} submenu (true | false) default false
26274  * @cfg {String} html text of the item
26275  * @cfg {String} href the link
26276  * @cfg {Boolean} disable (true | false) default false
26277  * @cfg {Boolean} preventDefault (true | false) default true
26278  * @cfg {String} icon Font awesome icon
26279  * @cfg {String} pos Submenu align to (left | right) default right 
26280  * 
26281  * 
26282  * @constructor
26283  * Create a new Item
26284  * @param {Object} config The config object
26285  */
26286
26287
26288 Roo.bootstrap.menu.Item = function(config){
26289     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26290     this.addEvents({
26291         /**
26292          * @event mouseover
26293          * Fires when the mouse is hovering over this menu
26294          * @param {Roo.bootstrap.menu.Item} this
26295          * @param {Roo.EventObject} e
26296          */
26297         mouseover : true,
26298         /**
26299          * @event mouseout
26300          * Fires when the mouse exits this menu
26301          * @param {Roo.bootstrap.menu.Item} this
26302          * @param {Roo.EventObject} e
26303          */
26304         mouseout : true,
26305         // raw events
26306         /**
26307          * @event click
26308          * The raw click event for the entire grid.
26309          * @param {Roo.EventObject} e
26310          */
26311         click : true
26312     });
26313 };
26314
26315 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26316     
26317     submenu : false,
26318     href : '',
26319     html : '',
26320     preventDefault: true,
26321     disable : false,
26322     icon : false,
26323     pos : 'right',
26324     
26325     getAutoCreate : function()
26326     {
26327         var text = [
26328             {
26329                 tag : 'span',
26330                 cls : 'roo-menu-item-text',
26331                 html : this.html
26332             }
26333         ];
26334         
26335         if(this.icon){
26336             text.unshift({
26337                 tag : 'i',
26338                 cls : 'fa ' + this.icon
26339             })
26340         }
26341         
26342         var cfg = {
26343             tag : 'li',
26344             cn : [
26345                 {
26346                     tag : 'a',
26347                     href : this.href || '#',
26348                     cn : text
26349                 }
26350             ]
26351         };
26352         
26353         if(this.disable){
26354             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26355         }
26356         
26357         if(this.submenu){
26358             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26359             
26360             if(this.pos == 'left'){
26361                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26362             }
26363         }
26364         
26365         return cfg;
26366     },
26367     
26368     initEvents : function() 
26369     {
26370         this.el.on('mouseover', this.onMouseOver, this);
26371         this.el.on('mouseout', this.onMouseOut, this);
26372         
26373         this.el.select('a', true).first().on('click', this.onClick, this);
26374         
26375     },
26376     
26377     onClick : function(e)
26378     {
26379         if(this.preventDefault){
26380             e.preventDefault();
26381         }
26382         
26383         this.fireEvent("click", this, e);
26384     },
26385     
26386     onMouseOver : function(e)
26387     {
26388         if(this.submenu && this.pos == 'left'){
26389             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26390         }
26391         
26392         this.fireEvent("mouseover", this, e);
26393     },
26394     
26395     onMouseOut : function(e)
26396     {
26397         this.fireEvent("mouseout", this, e);
26398     }
26399 });
26400
26401  
26402
26403  /*
26404  * - LGPL
26405  *
26406  * menu separator
26407  * 
26408  */
26409 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26410
26411 /**
26412  * @class Roo.bootstrap.menu.Separator
26413  * @extends Roo.bootstrap.Component
26414  * Bootstrap Separator class
26415  * 
26416  * @constructor
26417  * Create a new Separator
26418  * @param {Object} config The config object
26419  */
26420
26421
26422 Roo.bootstrap.menu.Separator = function(config){
26423     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26424 };
26425
26426 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26427     
26428     getAutoCreate : function(){
26429         var cfg = {
26430             tag : 'li',
26431             cls: 'divider'
26432         };
26433         
26434         return cfg;
26435     }
26436    
26437 });
26438
26439  
26440
26441  /*
26442  * - LGPL
26443  *
26444  * Tooltip
26445  * 
26446  */
26447
26448 /**
26449  * @class Roo.bootstrap.Tooltip
26450  * Bootstrap Tooltip class
26451  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26452  * to determine which dom element triggers the tooltip.
26453  * 
26454  * It needs to add support for additional attributes like tooltip-position
26455  * 
26456  * @constructor
26457  * Create a new Toolti
26458  * @param {Object} config The config object
26459  */
26460
26461 Roo.bootstrap.Tooltip = function(config){
26462     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26463     
26464     this.alignment = Roo.bootstrap.Tooltip.alignment;
26465     
26466     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26467         this.alignment = config.alignment;
26468     }
26469     
26470 };
26471
26472 Roo.apply(Roo.bootstrap.Tooltip, {
26473     /**
26474      * @function init initialize tooltip monitoring.
26475      * @static
26476      */
26477     currentEl : false,
26478     currentTip : false,
26479     currentRegion : false,
26480     
26481     //  init : delay?
26482     
26483     init : function()
26484     {
26485         Roo.get(document).on('mouseover', this.enter ,this);
26486         Roo.get(document).on('mouseout', this.leave, this);
26487          
26488         
26489         this.currentTip = new Roo.bootstrap.Tooltip();
26490     },
26491     
26492     enter : function(ev)
26493     {
26494         var dom = ev.getTarget();
26495         
26496         //Roo.log(['enter',dom]);
26497         var el = Roo.fly(dom);
26498         if (this.currentEl) {
26499             //Roo.log(dom);
26500             //Roo.log(this.currentEl);
26501             //Roo.log(this.currentEl.contains(dom));
26502             if (this.currentEl == el) {
26503                 return;
26504             }
26505             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26506                 return;
26507             }
26508
26509         }
26510         
26511         if (this.currentTip.el) {
26512             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26513         }    
26514         //Roo.log(ev);
26515         
26516         if(!el || el.dom == document){
26517             return;
26518         }
26519         
26520         var bindEl = el;
26521         
26522         // you can not look for children, as if el is the body.. then everythign is the child..
26523         if (!el.attr('tooltip')) { //
26524             if (!el.select("[tooltip]").elements.length) {
26525                 return;
26526             }
26527             // is the mouse over this child...?
26528             bindEl = el.select("[tooltip]").first();
26529             var xy = ev.getXY();
26530             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26531                 //Roo.log("not in region.");
26532                 return;
26533             }
26534             //Roo.log("child element over..");
26535             
26536         }
26537         this.currentEl = bindEl;
26538         this.currentTip.bind(bindEl);
26539         this.currentRegion = Roo.lib.Region.getRegion(dom);
26540         this.currentTip.enter();
26541         
26542     },
26543     leave : function(ev)
26544     {
26545         var dom = ev.getTarget();
26546         //Roo.log(['leave',dom]);
26547         if (!this.currentEl) {
26548             return;
26549         }
26550         
26551         
26552         if (dom != this.currentEl.dom) {
26553             return;
26554         }
26555         var xy = ev.getXY();
26556         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26557             return;
26558         }
26559         // only activate leave if mouse cursor is outside... bounding box..
26560         
26561         
26562         
26563         
26564         if (this.currentTip) {
26565             this.currentTip.leave();
26566         }
26567         //Roo.log('clear currentEl');
26568         this.currentEl = false;
26569         
26570         
26571     },
26572     alignment : {
26573         'left' : ['r-l', [-2,0], 'right'],
26574         'right' : ['l-r', [2,0], 'left'],
26575         'bottom' : ['t-b', [0,2], 'top'],
26576         'top' : [ 'b-t', [0,-2], 'bottom']
26577     }
26578     
26579 });
26580
26581
26582 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26583     
26584     
26585     bindEl : false,
26586     
26587     delay : null, // can be { show : 300 , hide: 500}
26588     
26589     timeout : null,
26590     
26591     hoverState : null, //???
26592     
26593     placement : 'bottom', 
26594     
26595     alignment : false,
26596     
26597     getAutoCreate : function(){
26598     
26599         var cfg = {
26600            cls : 'tooltip',
26601            role : 'tooltip',
26602            cn : [
26603                 {
26604                     cls : 'tooltip-arrow'
26605                 },
26606                 {
26607                     cls : 'tooltip-inner'
26608                 }
26609            ]
26610         };
26611         
26612         return cfg;
26613     },
26614     bind : function(el)
26615     {
26616         this.bindEl = el;
26617     },
26618       
26619     
26620     enter : function () {
26621        
26622         if (this.timeout != null) {
26623             clearTimeout(this.timeout);
26624         }
26625         
26626         this.hoverState = 'in';
26627          //Roo.log("enter - show");
26628         if (!this.delay || !this.delay.show) {
26629             this.show();
26630             return;
26631         }
26632         var _t = this;
26633         this.timeout = setTimeout(function () {
26634             if (_t.hoverState == 'in') {
26635                 _t.show();
26636             }
26637         }, this.delay.show);
26638     },
26639     leave : function()
26640     {
26641         clearTimeout(this.timeout);
26642     
26643         this.hoverState = 'out';
26644          if (!this.delay || !this.delay.hide) {
26645             this.hide();
26646             return;
26647         }
26648        
26649         var _t = this;
26650         this.timeout = setTimeout(function () {
26651             //Roo.log("leave - timeout");
26652             
26653             if (_t.hoverState == 'out') {
26654                 _t.hide();
26655                 Roo.bootstrap.Tooltip.currentEl = false;
26656             }
26657         }, delay);
26658     },
26659     
26660     show : function (msg)
26661     {
26662         if (!this.el) {
26663             this.render(document.body);
26664         }
26665         // set content.
26666         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26667         
26668         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26669         
26670         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26671         
26672         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26673         
26674         var placement = typeof this.placement == 'function' ?
26675             this.placement.call(this, this.el, on_el) :
26676             this.placement;
26677             
26678         var autoToken = /\s?auto?\s?/i;
26679         var autoPlace = autoToken.test(placement);
26680         if (autoPlace) {
26681             placement = placement.replace(autoToken, '') || 'top';
26682         }
26683         
26684         //this.el.detach()
26685         //this.el.setXY([0,0]);
26686         this.el.show();
26687         //this.el.dom.style.display='block';
26688         
26689         //this.el.appendTo(on_el);
26690         
26691         var p = this.getPosition();
26692         var box = this.el.getBox();
26693         
26694         if (autoPlace) {
26695             // fixme..
26696         }
26697         
26698         var align = this.alignment[placement];
26699         
26700         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26701         
26702         if(placement == 'top' || placement == 'bottom'){
26703             if(xy[0] < 0){
26704                 placement = 'right';
26705             }
26706             
26707             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26708                 placement = 'left';
26709             }
26710             
26711             var scroll = Roo.select('body', true).first().getScroll();
26712             
26713             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26714                 placement = 'top';
26715             }
26716             
26717             align = this.alignment[placement];
26718         }
26719         
26720         this.el.alignTo(this.bindEl, align[0],align[1]);
26721         //var arrow = this.el.select('.arrow',true).first();
26722         //arrow.set(align[2], 
26723         
26724         this.el.addClass(placement);
26725         
26726         this.el.addClass('in fade');
26727         
26728         this.hoverState = null;
26729         
26730         if (this.el.hasClass('fade')) {
26731             // fade it?
26732         }
26733         
26734     },
26735     hide : function()
26736     {
26737          
26738         if (!this.el) {
26739             return;
26740         }
26741         //this.el.setXY([0,0]);
26742         this.el.removeClass('in');
26743         //this.el.hide();
26744         
26745     }
26746     
26747 });
26748  
26749
26750  /*
26751  * - LGPL
26752  *
26753  * Location Picker
26754  * 
26755  */
26756
26757 /**
26758  * @class Roo.bootstrap.LocationPicker
26759  * @extends Roo.bootstrap.Component
26760  * Bootstrap LocationPicker class
26761  * @cfg {Number} latitude Position when init default 0
26762  * @cfg {Number} longitude Position when init default 0
26763  * @cfg {Number} zoom default 15
26764  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26765  * @cfg {Boolean} mapTypeControl default false
26766  * @cfg {Boolean} disableDoubleClickZoom default false
26767  * @cfg {Boolean} scrollwheel default true
26768  * @cfg {Boolean} streetViewControl default false
26769  * @cfg {Number} radius default 0
26770  * @cfg {String} locationName
26771  * @cfg {Boolean} draggable default true
26772  * @cfg {Boolean} enableAutocomplete default false
26773  * @cfg {Boolean} enableReverseGeocode default true
26774  * @cfg {String} markerTitle
26775  * 
26776  * @constructor
26777  * Create a new LocationPicker
26778  * @param {Object} config The config object
26779  */
26780
26781
26782 Roo.bootstrap.LocationPicker = function(config){
26783     
26784     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26785     
26786     this.addEvents({
26787         /**
26788          * @event initial
26789          * Fires when the picker initialized.
26790          * @param {Roo.bootstrap.LocationPicker} this
26791          * @param {Google Location} location
26792          */
26793         initial : true,
26794         /**
26795          * @event positionchanged
26796          * Fires when the picker position changed.
26797          * @param {Roo.bootstrap.LocationPicker} this
26798          * @param {Google Location} location
26799          */
26800         positionchanged : true,
26801         /**
26802          * @event resize
26803          * Fires when the map resize.
26804          * @param {Roo.bootstrap.LocationPicker} this
26805          */
26806         resize : true,
26807         /**
26808          * @event show
26809          * Fires when the map show.
26810          * @param {Roo.bootstrap.LocationPicker} this
26811          */
26812         show : true,
26813         /**
26814          * @event hide
26815          * Fires when the map hide.
26816          * @param {Roo.bootstrap.LocationPicker} this
26817          */
26818         hide : true,
26819         /**
26820          * @event mapClick
26821          * Fires when click the map.
26822          * @param {Roo.bootstrap.LocationPicker} this
26823          * @param {Map event} e
26824          */
26825         mapClick : true,
26826         /**
26827          * @event mapRightClick
26828          * Fires when right click the map.
26829          * @param {Roo.bootstrap.LocationPicker} this
26830          * @param {Map event} e
26831          */
26832         mapRightClick : true,
26833         /**
26834          * @event markerClick
26835          * Fires when click the marker.
26836          * @param {Roo.bootstrap.LocationPicker} this
26837          * @param {Map event} e
26838          */
26839         markerClick : true,
26840         /**
26841          * @event markerRightClick
26842          * Fires when right click the marker.
26843          * @param {Roo.bootstrap.LocationPicker} this
26844          * @param {Map event} e
26845          */
26846         markerRightClick : true,
26847         /**
26848          * @event OverlayViewDraw
26849          * Fires when OverlayView Draw
26850          * @param {Roo.bootstrap.LocationPicker} this
26851          */
26852         OverlayViewDraw : true,
26853         /**
26854          * @event OverlayViewOnAdd
26855          * Fires when OverlayView Draw
26856          * @param {Roo.bootstrap.LocationPicker} this
26857          */
26858         OverlayViewOnAdd : true,
26859         /**
26860          * @event OverlayViewOnRemove
26861          * Fires when OverlayView Draw
26862          * @param {Roo.bootstrap.LocationPicker} this
26863          */
26864         OverlayViewOnRemove : true,
26865         /**
26866          * @event OverlayViewShow
26867          * Fires when OverlayView Draw
26868          * @param {Roo.bootstrap.LocationPicker} this
26869          * @param {Pixel} cpx
26870          */
26871         OverlayViewShow : true,
26872         /**
26873          * @event OverlayViewHide
26874          * Fires when OverlayView Draw
26875          * @param {Roo.bootstrap.LocationPicker} this
26876          */
26877         OverlayViewHide : true,
26878         /**
26879          * @event loadexception
26880          * Fires when load google lib failed.
26881          * @param {Roo.bootstrap.LocationPicker} this
26882          */
26883         loadexception : true
26884     });
26885         
26886 };
26887
26888 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26889     
26890     gMapContext: false,
26891     
26892     latitude: 0,
26893     longitude: 0,
26894     zoom: 15,
26895     mapTypeId: false,
26896     mapTypeControl: false,
26897     disableDoubleClickZoom: false,
26898     scrollwheel: true,
26899     streetViewControl: false,
26900     radius: 0,
26901     locationName: '',
26902     draggable: true,
26903     enableAutocomplete: false,
26904     enableReverseGeocode: true,
26905     markerTitle: '',
26906     
26907     getAutoCreate: function()
26908     {
26909
26910         var cfg = {
26911             tag: 'div',
26912             cls: 'roo-location-picker'
26913         };
26914         
26915         return cfg
26916     },
26917     
26918     initEvents: function(ct, position)
26919     {       
26920         if(!this.el.getWidth() || this.isApplied()){
26921             return;
26922         }
26923         
26924         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26925         
26926         this.initial();
26927     },
26928     
26929     initial: function()
26930     {
26931         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26932             this.fireEvent('loadexception', this);
26933             return;
26934         }
26935         
26936         if(!this.mapTypeId){
26937             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26938         }
26939         
26940         this.gMapContext = this.GMapContext();
26941         
26942         this.initOverlayView();
26943         
26944         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26945         
26946         var _this = this;
26947                 
26948         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26949             _this.setPosition(_this.gMapContext.marker.position);
26950         });
26951         
26952         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26953             _this.fireEvent('mapClick', this, event);
26954             
26955         });
26956
26957         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26958             _this.fireEvent('mapRightClick', this, event);
26959             
26960         });
26961         
26962         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26963             _this.fireEvent('markerClick', this, event);
26964             
26965         });
26966
26967         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26968             _this.fireEvent('markerRightClick', this, event);
26969             
26970         });
26971         
26972         this.setPosition(this.gMapContext.location);
26973         
26974         this.fireEvent('initial', this, this.gMapContext.location);
26975     },
26976     
26977     initOverlayView: function()
26978     {
26979         var _this = this;
26980         
26981         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26982             
26983             draw: function()
26984             {
26985                 _this.fireEvent('OverlayViewDraw', _this);
26986             },
26987             
26988             onAdd: function()
26989             {
26990                 _this.fireEvent('OverlayViewOnAdd', _this);
26991             },
26992             
26993             onRemove: function()
26994             {
26995                 _this.fireEvent('OverlayViewOnRemove', _this);
26996             },
26997             
26998             show: function(cpx)
26999             {
27000                 _this.fireEvent('OverlayViewShow', _this, cpx);
27001             },
27002             
27003             hide: function()
27004             {
27005                 _this.fireEvent('OverlayViewHide', _this);
27006             }
27007             
27008         });
27009     },
27010     
27011     fromLatLngToContainerPixel: function(event)
27012     {
27013         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27014     },
27015     
27016     isApplied: function() 
27017     {
27018         return this.getGmapContext() == false ? false : true;
27019     },
27020     
27021     getGmapContext: function() 
27022     {
27023         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27024     },
27025     
27026     GMapContext: function() 
27027     {
27028         var position = new google.maps.LatLng(this.latitude, this.longitude);
27029         
27030         var _map = new google.maps.Map(this.el.dom, {
27031             center: position,
27032             zoom: this.zoom,
27033             mapTypeId: this.mapTypeId,
27034             mapTypeControl: this.mapTypeControl,
27035             disableDoubleClickZoom: this.disableDoubleClickZoom,
27036             scrollwheel: this.scrollwheel,
27037             streetViewControl: this.streetViewControl,
27038             locationName: this.locationName,
27039             draggable: this.draggable,
27040             enableAutocomplete: this.enableAutocomplete,
27041             enableReverseGeocode: this.enableReverseGeocode
27042         });
27043         
27044         var _marker = new google.maps.Marker({
27045             position: position,
27046             map: _map,
27047             title: this.markerTitle,
27048             draggable: this.draggable
27049         });
27050         
27051         return {
27052             map: _map,
27053             marker: _marker,
27054             circle: null,
27055             location: position,
27056             radius: this.radius,
27057             locationName: this.locationName,
27058             addressComponents: {
27059                 formatted_address: null,
27060                 addressLine1: null,
27061                 addressLine2: null,
27062                 streetName: null,
27063                 streetNumber: null,
27064                 city: null,
27065                 district: null,
27066                 state: null,
27067                 stateOrProvince: null
27068             },
27069             settings: this,
27070             domContainer: this.el.dom,
27071             geodecoder: new google.maps.Geocoder()
27072         };
27073     },
27074     
27075     drawCircle: function(center, radius, options) 
27076     {
27077         if (this.gMapContext.circle != null) {
27078             this.gMapContext.circle.setMap(null);
27079         }
27080         if (radius > 0) {
27081             radius *= 1;
27082             options = Roo.apply({}, options, {
27083                 strokeColor: "#0000FF",
27084                 strokeOpacity: .35,
27085                 strokeWeight: 2,
27086                 fillColor: "#0000FF",
27087                 fillOpacity: .2
27088             });
27089             
27090             options.map = this.gMapContext.map;
27091             options.radius = radius;
27092             options.center = center;
27093             this.gMapContext.circle = new google.maps.Circle(options);
27094             return this.gMapContext.circle;
27095         }
27096         
27097         return null;
27098     },
27099     
27100     setPosition: function(location) 
27101     {
27102         this.gMapContext.location = location;
27103         this.gMapContext.marker.setPosition(location);
27104         this.gMapContext.map.panTo(location);
27105         this.drawCircle(location, this.gMapContext.radius, {});
27106         
27107         var _this = this;
27108         
27109         if (this.gMapContext.settings.enableReverseGeocode) {
27110             this.gMapContext.geodecoder.geocode({
27111                 latLng: this.gMapContext.location
27112             }, function(results, status) {
27113                 
27114                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27115                     _this.gMapContext.locationName = results[0].formatted_address;
27116                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27117                     
27118                     _this.fireEvent('positionchanged', this, location);
27119                 }
27120             });
27121             
27122             return;
27123         }
27124         
27125         this.fireEvent('positionchanged', this, location);
27126     },
27127     
27128     resize: function()
27129     {
27130         google.maps.event.trigger(this.gMapContext.map, "resize");
27131         
27132         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27133         
27134         this.fireEvent('resize', this);
27135     },
27136     
27137     setPositionByLatLng: function(latitude, longitude)
27138     {
27139         this.setPosition(new google.maps.LatLng(latitude, longitude));
27140     },
27141     
27142     getCurrentPosition: function() 
27143     {
27144         return {
27145             latitude: this.gMapContext.location.lat(),
27146             longitude: this.gMapContext.location.lng()
27147         };
27148     },
27149     
27150     getAddressName: function() 
27151     {
27152         return this.gMapContext.locationName;
27153     },
27154     
27155     getAddressComponents: function() 
27156     {
27157         return this.gMapContext.addressComponents;
27158     },
27159     
27160     address_component_from_google_geocode: function(address_components) 
27161     {
27162         var result = {};
27163         
27164         for (var i = 0; i < address_components.length; i++) {
27165             var component = address_components[i];
27166             if (component.types.indexOf("postal_code") >= 0) {
27167                 result.postalCode = component.short_name;
27168             } else if (component.types.indexOf("street_number") >= 0) {
27169                 result.streetNumber = component.short_name;
27170             } else if (component.types.indexOf("route") >= 0) {
27171                 result.streetName = component.short_name;
27172             } else if (component.types.indexOf("neighborhood") >= 0) {
27173                 result.city = component.short_name;
27174             } else if (component.types.indexOf("locality") >= 0) {
27175                 result.city = component.short_name;
27176             } else if (component.types.indexOf("sublocality") >= 0) {
27177                 result.district = component.short_name;
27178             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27179                 result.stateOrProvince = component.short_name;
27180             } else if (component.types.indexOf("country") >= 0) {
27181                 result.country = component.short_name;
27182             }
27183         }
27184         
27185         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27186         result.addressLine2 = "";
27187         return result;
27188     },
27189     
27190     setZoomLevel: function(zoom)
27191     {
27192         this.gMapContext.map.setZoom(zoom);
27193     },
27194     
27195     show: function()
27196     {
27197         if(!this.el){
27198             return;
27199         }
27200         
27201         this.el.show();
27202         
27203         this.resize();
27204         
27205         this.fireEvent('show', this);
27206     },
27207     
27208     hide: function()
27209     {
27210         if(!this.el){
27211             return;
27212         }
27213         
27214         this.el.hide();
27215         
27216         this.fireEvent('hide', this);
27217     }
27218     
27219 });
27220
27221 Roo.apply(Roo.bootstrap.LocationPicker, {
27222     
27223     OverlayView : function(map, options)
27224     {
27225         options = options || {};
27226         
27227         this.setMap(map);
27228     }
27229     
27230     
27231 });/**
27232  * @class Roo.bootstrap.Alert
27233  * @extends Roo.bootstrap.Component
27234  * Bootstrap Alert class - shows an alert area box
27235  * eg
27236  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27237   Enter a valid email address
27238 </div>
27239  * @licence LGPL
27240  * @cfg {String} title The title of alert
27241  * @cfg {String} html The content of alert
27242  * @cfg {String} weight (  success | info | warning | danger )
27243  * @cfg {String} faicon font-awesomeicon
27244  * 
27245  * @constructor
27246  * Create a new alert
27247  * @param {Object} config The config object
27248  */
27249
27250
27251 Roo.bootstrap.Alert = function(config){
27252     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27253     
27254 };
27255
27256 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27257     
27258     title: '',
27259     html: '',
27260     weight: false,
27261     faicon: false,
27262     
27263     getAutoCreate : function()
27264     {
27265         
27266         var cfg = {
27267             tag : 'div',
27268             cls : 'alert',
27269             cn : [
27270                 {
27271                     tag : 'i',
27272                     cls : 'roo-alert-icon'
27273                     
27274                 },
27275                 {
27276                     tag : 'b',
27277                     cls : 'roo-alert-title',
27278                     html : this.title
27279                 },
27280                 {
27281                     tag : 'span',
27282                     cls : 'roo-alert-text',
27283                     html : this.html
27284                 }
27285             ]
27286         };
27287         
27288         if(this.faicon){
27289             cfg.cn[0].cls += ' fa ' + this.faicon;
27290         }
27291         
27292         if(this.weight){
27293             cfg.cls += ' alert-' + this.weight;
27294         }
27295         
27296         return cfg;
27297     },
27298     
27299     initEvents: function() 
27300     {
27301         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27302     },
27303     
27304     setTitle : function(str)
27305     {
27306         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27307     },
27308     
27309     setText : function(str)
27310     {
27311         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27312     },
27313     
27314     setWeight : function(weight)
27315     {
27316         if(this.weight){
27317             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27318         }
27319         
27320         this.weight = weight;
27321         
27322         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27323     },
27324     
27325     setIcon : function(icon)
27326     {
27327         if(this.faicon){
27328             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27329         }
27330         
27331         this.faicon = icon;
27332         
27333         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27334     },
27335     
27336     hide: function() 
27337     {
27338         this.el.hide();   
27339     },
27340     
27341     show: function() 
27342     {  
27343         this.el.show();   
27344     }
27345     
27346 });
27347
27348  
27349 /*
27350 * Licence: LGPL
27351 */
27352
27353 /**
27354  * @class Roo.bootstrap.UploadCropbox
27355  * @extends Roo.bootstrap.Component
27356  * Bootstrap UploadCropbox class
27357  * @cfg {String} emptyText show when image has been loaded
27358  * @cfg {String} rotateNotify show when image too small to rotate
27359  * @cfg {Number} errorTimeout default 3000
27360  * @cfg {Number} minWidth default 300
27361  * @cfg {Number} minHeight default 300
27362  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27363  * @cfg {Boolean} isDocument (true|false) default false
27364  * @cfg {String} url action url
27365  * @cfg {String} paramName default 'imageUpload'
27366  * @cfg {String} method default POST
27367  * @cfg {Boolean} loadMask (true|false) default true
27368  * @cfg {Boolean} loadingText default 'Loading...'
27369  * 
27370  * @constructor
27371  * Create a new UploadCropbox
27372  * @param {Object} config The config object
27373  */
27374
27375 Roo.bootstrap.UploadCropbox = function(config){
27376     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27377     
27378     this.addEvents({
27379         /**
27380          * @event beforeselectfile
27381          * Fire before select file
27382          * @param {Roo.bootstrap.UploadCropbox} this
27383          */
27384         "beforeselectfile" : true,
27385         /**
27386          * @event initial
27387          * Fire after initEvent
27388          * @param {Roo.bootstrap.UploadCropbox} this
27389          */
27390         "initial" : true,
27391         /**
27392          * @event crop
27393          * Fire after initEvent
27394          * @param {Roo.bootstrap.UploadCropbox} this
27395          * @param {String} data
27396          */
27397         "crop" : true,
27398         /**
27399          * @event prepare
27400          * Fire when preparing the file data
27401          * @param {Roo.bootstrap.UploadCropbox} this
27402          * @param {Object} file
27403          */
27404         "prepare" : true,
27405         /**
27406          * @event exception
27407          * Fire when get exception
27408          * @param {Roo.bootstrap.UploadCropbox} this
27409          * @param {XMLHttpRequest} xhr
27410          */
27411         "exception" : true,
27412         /**
27413          * @event beforeloadcanvas
27414          * Fire before load the canvas
27415          * @param {Roo.bootstrap.UploadCropbox} this
27416          * @param {String} src
27417          */
27418         "beforeloadcanvas" : true,
27419         /**
27420          * @event trash
27421          * Fire when trash image
27422          * @param {Roo.bootstrap.UploadCropbox} this
27423          */
27424         "trash" : true,
27425         /**
27426          * @event download
27427          * Fire when download the image
27428          * @param {Roo.bootstrap.UploadCropbox} this
27429          */
27430         "download" : true,
27431         /**
27432          * @event footerbuttonclick
27433          * Fire when footerbuttonclick
27434          * @param {Roo.bootstrap.UploadCropbox} this
27435          * @param {String} type
27436          */
27437         "footerbuttonclick" : true,
27438         /**
27439          * @event resize
27440          * Fire when resize
27441          * @param {Roo.bootstrap.UploadCropbox} this
27442          */
27443         "resize" : true,
27444         /**
27445          * @event rotate
27446          * Fire when rotate the image
27447          * @param {Roo.bootstrap.UploadCropbox} this
27448          * @param {String} pos
27449          */
27450         "rotate" : true,
27451         /**
27452          * @event inspect
27453          * Fire when inspect the file
27454          * @param {Roo.bootstrap.UploadCropbox} this
27455          * @param {Object} file
27456          */
27457         "inspect" : true,
27458         /**
27459          * @event upload
27460          * Fire when xhr upload the file
27461          * @param {Roo.bootstrap.UploadCropbox} this
27462          * @param {Object} data
27463          */
27464         "upload" : true,
27465         /**
27466          * @event arrange
27467          * Fire when arrange the file data
27468          * @param {Roo.bootstrap.UploadCropbox} this
27469          * @param {Object} formData
27470          */
27471         "arrange" : true
27472     });
27473     
27474     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27475 };
27476
27477 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27478     
27479     emptyText : 'Click to upload image',
27480     rotateNotify : 'Image is too small to rotate',
27481     errorTimeout : 3000,
27482     scale : 0,
27483     baseScale : 1,
27484     rotate : 0,
27485     dragable : false,
27486     pinching : false,
27487     mouseX : 0,
27488     mouseY : 0,
27489     cropData : false,
27490     minWidth : 300,
27491     minHeight : 300,
27492     file : false,
27493     exif : {},
27494     baseRotate : 1,
27495     cropType : 'image/jpeg',
27496     buttons : false,
27497     canvasLoaded : false,
27498     isDocument : false,
27499     method : 'POST',
27500     paramName : 'imageUpload',
27501     loadMask : true,
27502     loadingText : 'Loading...',
27503     maskEl : false,
27504     
27505     getAutoCreate : function()
27506     {
27507         var cfg = {
27508             tag : 'div',
27509             cls : 'roo-upload-cropbox',
27510             cn : [
27511                 {
27512                     tag : 'input',
27513                     cls : 'roo-upload-cropbox-selector',
27514                     type : 'file'
27515                 },
27516                 {
27517                     tag : 'div',
27518                     cls : 'roo-upload-cropbox-body',
27519                     style : 'cursor:pointer',
27520                     cn : [
27521                         {
27522                             tag : 'div',
27523                             cls : 'roo-upload-cropbox-preview'
27524                         },
27525                         {
27526                             tag : 'div',
27527                             cls : 'roo-upload-cropbox-thumb'
27528                         },
27529                         {
27530                             tag : 'div',
27531                             cls : 'roo-upload-cropbox-empty-notify',
27532                             html : this.emptyText
27533                         },
27534                         {
27535                             tag : 'div',
27536                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27537                             html : this.rotateNotify
27538                         }
27539                     ]
27540                 },
27541                 {
27542                     tag : 'div',
27543                     cls : 'roo-upload-cropbox-footer',
27544                     cn : {
27545                         tag : 'div',
27546                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27547                         cn : []
27548                     }
27549                 }
27550             ]
27551         };
27552         
27553         return cfg;
27554     },
27555     
27556     onRender : function(ct, position)
27557     {
27558         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27559         
27560         if (this.buttons.length) {
27561             
27562             Roo.each(this.buttons, function(bb) {
27563                 
27564                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27565                 
27566                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27567                 
27568             }, this);
27569         }
27570         
27571         if(this.loadMask){
27572             this.maskEl = this.el;
27573         }
27574     },
27575     
27576     initEvents : function()
27577     {
27578         this.urlAPI = (window.createObjectURL && window) || 
27579                                 (window.URL && URL.revokeObjectURL && URL) || 
27580                                 (window.webkitURL && webkitURL);
27581                         
27582         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27583         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27584         
27585         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27586         this.selectorEl.hide();
27587         
27588         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27589         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27590         
27591         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27592         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27593         this.thumbEl.hide();
27594         
27595         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27596         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27597         
27598         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27599         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27600         this.errorEl.hide();
27601         
27602         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27603         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27604         this.footerEl.hide();
27605         
27606         this.setThumbBoxSize();
27607         
27608         this.bind();
27609         
27610         this.resize();
27611         
27612         this.fireEvent('initial', this);
27613     },
27614
27615     bind : function()
27616     {
27617         var _this = this;
27618         
27619         window.addEventListener("resize", function() { _this.resize(); } );
27620         
27621         this.bodyEl.on('click', this.beforeSelectFile, this);
27622         
27623         if(Roo.isTouch){
27624             this.bodyEl.on('touchstart', this.onTouchStart, this);
27625             this.bodyEl.on('touchmove', this.onTouchMove, this);
27626             this.bodyEl.on('touchend', this.onTouchEnd, this);
27627         }
27628         
27629         if(!Roo.isTouch){
27630             this.bodyEl.on('mousedown', this.onMouseDown, this);
27631             this.bodyEl.on('mousemove', this.onMouseMove, this);
27632             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27633             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27634             Roo.get(document).on('mouseup', this.onMouseUp, this);
27635         }
27636         
27637         this.selectorEl.on('change', this.onFileSelected, this);
27638     },
27639     
27640     reset : function()
27641     {    
27642         this.scale = 0;
27643         this.baseScale = 1;
27644         this.rotate = 0;
27645         this.baseRotate = 1;
27646         this.dragable = false;
27647         this.pinching = false;
27648         this.mouseX = 0;
27649         this.mouseY = 0;
27650         this.cropData = false;
27651         this.notifyEl.dom.innerHTML = this.emptyText;
27652         
27653         this.selectorEl.dom.value = '';
27654         
27655     },
27656     
27657     resize : function()
27658     {
27659         if(this.fireEvent('resize', this) != false){
27660             this.setThumbBoxPosition();
27661             this.setCanvasPosition();
27662         }
27663     },
27664     
27665     onFooterButtonClick : function(e, el, o, type)
27666     {
27667         switch (type) {
27668             case 'rotate-left' :
27669                 this.onRotateLeft(e);
27670                 break;
27671             case 'rotate-right' :
27672                 this.onRotateRight(e);
27673                 break;
27674             case 'picture' :
27675                 this.beforeSelectFile(e);
27676                 break;
27677             case 'trash' :
27678                 this.trash(e);
27679                 break;
27680             case 'crop' :
27681                 this.crop(e);
27682                 break;
27683             case 'download' :
27684                 this.download(e);
27685                 break;
27686             default :
27687                 break;
27688         }
27689         
27690         this.fireEvent('footerbuttonclick', this, type);
27691     },
27692     
27693     beforeSelectFile : function(e)
27694     {
27695         e.preventDefault();
27696         
27697         if(this.fireEvent('beforeselectfile', this) != false){
27698             this.selectorEl.dom.click();
27699         }
27700     },
27701     
27702     onFileSelected : function(e)
27703     {
27704         e.preventDefault();
27705         
27706         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27707             return;
27708         }
27709         
27710         var file = this.selectorEl.dom.files[0];
27711         
27712         if(this.fireEvent('inspect', this, file) != false){
27713             this.prepare(file);
27714         }
27715         
27716     },
27717     
27718     trash : function(e)
27719     {
27720         this.fireEvent('trash', this);
27721     },
27722     
27723     download : function(e)
27724     {
27725         this.fireEvent('download', this);
27726     },
27727     
27728     loadCanvas : function(src)
27729     {   
27730         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27731             
27732             this.reset();
27733             
27734             this.imageEl = document.createElement('img');
27735             
27736             var _this = this;
27737             
27738             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27739             
27740             this.imageEl.src = src;
27741         }
27742     },
27743     
27744     onLoadCanvas : function()
27745     {   
27746         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27747         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27748         
27749         this.bodyEl.un('click', this.beforeSelectFile, this);
27750         
27751         this.notifyEl.hide();
27752         this.thumbEl.show();
27753         this.footerEl.show();
27754         
27755         this.baseRotateLevel();
27756         
27757         if(this.isDocument){
27758             this.setThumbBoxSize();
27759         }
27760         
27761         this.setThumbBoxPosition();
27762         
27763         this.baseScaleLevel();
27764         
27765         this.draw();
27766         
27767         this.resize();
27768         
27769         this.canvasLoaded = true;
27770         
27771         if(this.loadMask){
27772             this.maskEl.unmask();
27773         }
27774         
27775     },
27776     
27777     setCanvasPosition : function()
27778     {   
27779         if(!this.canvasEl){
27780             return;
27781         }
27782         
27783         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27784         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27785         
27786         this.previewEl.setLeft(pw);
27787         this.previewEl.setTop(ph);
27788         
27789     },
27790     
27791     onMouseDown : function(e)
27792     {   
27793         e.stopEvent();
27794         
27795         this.dragable = true;
27796         this.pinching = false;
27797         
27798         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27799             this.dragable = false;
27800             return;
27801         }
27802         
27803         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27804         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27805         
27806     },
27807     
27808     onMouseMove : function(e)
27809     {   
27810         e.stopEvent();
27811         
27812         if(!this.canvasLoaded){
27813             return;
27814         }
27815         
27816         if (!this.dragable){
27817             return;
27818         }
27819         
27820         var minX = Math.ceil(this.thumbEl.getLeft(true));
27821         var minY = Math.ceil(this.thumbEl.getTop(true));
27822         
27823         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27824         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27825         
27826         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27827         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27828         
27829         x = x - this.mouseX;
27830         y = y - this.mouseY;
27831         
27832         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27833         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27834         
27835         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27836         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27837         
27838         this.previewEl.setLeft(bgX);
27839         this.previewEl.setTop(bgY);
27840         
27841         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27842         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27843     },
27844     
27845     onMouseUp : function(e)
27846     {   
27847         e.stopEvent();
27848         
27849         this.dragable = false;
27850     },
27851     
27852     onMouseWheel : function(e)
27853     {   
27854         e.stopEvent();
27855         
27856         this.startScale = this.scale;
27857         
27858         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27859         
27860         if(!this.zoomable()){
27861             this.scale = this.startScale;
27862             return;
27863         }
27864         
27865         this.draw();
27866         
27867         return;
27868     },
27869     
27870     zoomable : function()
27871     {
27872         var minScale = this.thumbEl.getWidth() / this.minWidth;
27873         
27874         if(this.minWidth < this.minHeight){
27875             minScale = this.thumbEl.getHeight() / this.minHeight;
27876         }
27877         
27878         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27879         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27880         
27881         if(
27882                 this.isDocument &&
27883                 (this.rotate == 0 || this.rotate == 180) && 
27884                 (
27885                     width > this.imageEl.OriginWidth || 
27886                     height > this.imageEl.OriginHeight ||
27887                     (width < this.minWidth && height < this.minHeight)
27888                 )
27889         ){
27890             return false;
27891         }
27892         
27893         if(
27894                 this.isDocument &&
27895                 (this.rotate == 90 || this.rotate == 270) && 
27896                 (
27897                     width > this.imageEl.OriginWidth || 
27898                     height > this.imageEl.OriginHeight ||
27899                     (width < this.minHeight && height < this.minWidth)
27900                 )
27901         ){
27902             return false;
27903         }
27904         
27905         if(
27906                 !this.isDocument &&
27907                 (this.rotate == 0 || this.rotate == 180) && 
27908                 (
27909                     width < this.minWidth || 
27910                     width > this.imageEl.OriginWidth || 
27911                     height < this.minHeight || 
27912                     height > this.imageEl.OriginHeight
27913                 )
27914         ){
27915             return false;
27916         }
27917         
27918         if(
27919                 !this.isDocument &&
27920                 (this.rotate == 90 || this.rotate == 270) && 
27921                 (
27922                     width < this.minHeight || 
27923                     width > this.imageEl.OriginWidth || 
27924                     height < this.minWidth || 
27925                     height > this.imageEl.OriginHeight
27926                 )
27927         ){
27928             return false;
27929         }
27930         
27931         return true;
27932         
27933     },
27934     
27935     onRotateLeft : function(e)
27936     {   
27937         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27938             
27939             var minScale = this.thumbEl.getWidth() / this.minWidth;
27940             
27941             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27942             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27943             
27944             this.startScale = this.scale;
27945             
27946             while (this.getScaleLevel() < minScale){
27947             
27948                 this.scale = this.scale + 1;
27949                 
27950                 if(!this.zoomable()){
27951                     break;
27952                 }
27953                 
27954                 if(
27955                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27956                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27957                 ){
27958                     continue;
27959                 }
27960                 
27961                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27962
27963                 this.draw();
27964                 
27965                 return;
27966             }
27967             
27968             this.scale = this.startScale;
27969             
27970             this.onRotateFail();
27971             
27972             return false;
27973         }
27974         
27975         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27976
27977         if(this.isDocument){
27978             this.setThumbBoxSize();
27979             this.setThumbBoxPosition();
27980             this.setCanvasPosition();
27981         }
27982         
27983         this.draw();
27984         
27985         this.fireEvent('rotate', this, 'left');
27986         
27987     },
27988     
27989     onRotateRight : function(e)
27990     {
27991         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27992             
27993             var minScale = this.thumbEl.getWidth() / this.minWidth;
27994         
27995             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27996             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27997             
27998             this.startScale = this.scale;
27999             
28000             while (this.getScaleLevel() < minScale){
28001             
28002                 this.scale = this.scale + 1;
28003                 
28004                 if(!this.zoomable()){
28005                     break;
28006                 }
28007                 
28008                 if(
28009                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28010                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28011                 ){
28012                     continue;
28013                 }
28014                 
28015                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28016
28017                 this.draw();
28018                 
28019                 return;
28020             }
28021             
28022             this.scale = this.startScale;
28023             
28024             this.onRotateFail();
28025             
28026             return false;
28027         }
28028         
28029         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28030
28031         if(this.isDocument){
28032             this.setThumbBoxSize();
28033             this.setThumbBoxPosition();
28034             this.setCanvasPosition();
28035         }
28036         
28037         this.draw();
28038         
28039         this.fireEvent('rotate', this, 'right');
28040     },
28041     
28042     onRotateFail : function()
28043     {
28044         this.errorEl.show(true);
28045         
28046         var _this = this;
28047         
28048         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28049     },
28050     
28051     draw : function()
28052     {
28053         this.previewEl.dom.innerHTML = '';
28054         
28055         var canvasEl = document.createElement("canvas");
28056         
28057         var contextEl = canvasEl.getContext("2d");
28058         
28059         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28060         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28061         var center = this.imageEl.OriginWidth / 2;
28062         
28063         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28064             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28065             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28066             center = this.imageEl.OriginHeight / 2;
28067         }
28068         
28069         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28070         
28071         contextEl.translate(center, center);
28072         contextEl.rotate(this.rotate * Math.PI / 180);
28073
28074         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28075         
28076         this.canvasEl = document.createElement("canvas");
28077         
28078         this.contextEl = this.canvasEl.getContext("2d");
28079         
28080         switch (this.rotate) {
28081             case 0 :
28082                 
28083                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28084                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28085                 
28086                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28087                 
28088                 break;
28089             case 90 : 
28090                 
28091                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28092                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28093                 
28094                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28095                     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);
28096                     break;
28097                 }
28098                 
28099                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28100                 
28101                 break;
28102             case 180 :
28103                 
28104                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28105                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28106                 
28107                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28108                     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);
28109                     break;
28110                 }
28111                 
28112                 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);
28113                 
28114                 break;
28115             case 270 :
28116                 
28117                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28118                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28119         
28120                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28121                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28122                     break;
28123                 }
28124                 
28125                 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);
28126                 
28127                 break;
28128             default : 
28129                 break;
28130         }
28131         
28132         this.previewEl.appendChild(this.canvasEl);
28133         
28134         this.setCanvasPosition();
28135     },
28136     
28137     crop : function()
28138     {
28139         if(!this.canvasLoaded){
28140             return;
28141         }
28142         
28143         var imageCanvas = document.createElement("canvas");
28144         
28145         var imageContext = imageCanvas.getContext("2d");
28146         
28147         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28148         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28149         
28150         var center = imageCanvas.width / 2;
28151         
28152         imageContext.translate(center, center);
28153         
28154         imageContext.rotate(this.rotate * Math.PI / 180);
28155         
28156         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28157         
28158         var canvas = document.createElement("canvas");
28159         
28160         var context = canvas.getContext("2d");
28161                 
28162         canvas.width = this.minWidth;
28163         canvas.height = this.minHeight;
28164
28165         switch (this.rotate) {
28166             case 0 :
28167                 
28168                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28169                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28170                 
28171                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28172                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28173                 
28174                 var targetWidth = this.minWidth - 2 * x;
28175                 var targetHeight = this.minHeight - 2 * y;
28176                 
28177                 var scale = 1;
28178                 
28179                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28180                     scale = targetWidth / width;
28181                 }
28182                 
28183                 if(x > 0 && y == 0){
28184                     scale = targetHeight / height;
28185                 }
28186                 
28187                 if(x > 0 && y > 0){
28188                     scale = targetWidth / width;
28189                     
28190                     if(width < height){
28191                         scale = targetHeight / height;
28192                     }
28193                 }
28194                 
28195                 context.scale(scale, scale);
28196                 
28197                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28198                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28199
28200                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28201                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28202
28203                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28204                 
28205                 break;
28206             case 90 : 
28207                 
28208                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28209                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28210                 
28211                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28212                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28213                 
28214                 var targetWidth = this.minWidth - 2 * x;
28215                 var targetHeight = this.minHeight - 2 * y;
28216                 
28217                 var scale = 1;
28218                 
28219                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28220                     scale = targetWidth / width;
28221                 }
28222                 
28223                 if(x > 0 && y == 0){
28224                     scale = targetHeight / height;
28225                 }
28226                 
28227                 if(x > 0 && y > 0){
28228                     scale = targetWidth / width;
28229                     
28230                     if(width < height){
28231                         scale = targetHeight / height;
28232                     }
28233                 }
28234                 
28235                 context.scale(scale, scale);
28236                 
28237                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28238                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28239
28240                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28241                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28242                 
28243                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28244                 
28245                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28246                 
28247                 break;
28248             case 180 :
28249                 
28250                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28251                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28252                 
28253                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28254                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28255                 
28256                 var targetWidth = this.minWidth - 2 * x;
28257                 var targetHeight = this.minHeight - 2 * y;
28258                 
28259                 var scale = 1;
28260                 
28261                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28262                     scale = targetWidth / width;
28263                 }
28264                 
28265                 if(x > 0 && y == 0){
28266                     scale = targetHeight / height;
28267                 }
28268                 
28269                 if(x > 0 && y > 0){
28270                     scale = targetWidth / width;
28271                     
28272                     if(width < height){
28273                         scale = targetHeight / height;
28274                     }
28275                 }
28276                 
28277                 context.scale(scale, scale);
28278                 
28279                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28280                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28281
28282                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28283                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28284
28285                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28286                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28287                 
28288                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28289                 
28290                 break;
28291             case 270 :
28292                 
28293                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28294                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28295                 
28296                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28297                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28298                 
28299                 var targetWidth = this.minWidth - 2 * x;
28300                 var targetHeight = this.minHeight - 2 * y;
28301                 
28302                 var scale = 1;
28303                 
28304                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28305                     scale = targetWidth / width;
28306                 }
28307                 
28308                 if(x > 0 && y == 0){
28309                     scale = targetHeight / height;
28310                 }
28311                 
28312                 if(x > 0 && y > 0){
28313                     scale = targetWidth / width;
28314                     
28315                     if(width < height){
28316                         scale = targetHeight / height;
28317                     }
28318                 }
28319                 
28320                 context.scale(scale, scale);
28321                 
28322                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28323                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28324
28325                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28326                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28327                 
28328                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28329                 
28330                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28331                 
28332                 break;
28333             default : 
28334                 break;
28335         }
28336         
28337         this.cropData = canvas.toDataURL(this.cropType);
28338         
28339         if(this.fireEvent('crop', this, this.cropData) !== false){
28340             this.process(this.file, this.cropData);
28341         }
28342         
28343         return;
28344         
28345     },
28346     
28347     setThumbBoxSize : function()
28348     {
28349         var width, height;
28350         
28351         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28352             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28353             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28354             
28355             this.minWidth = width;
28356             this.minHeight = height;
28357             
28358             if(this.rotate == 90 || this.rotate == 270){
28359                 this.minWidth = height;
28360                 this.minHeight = width;
28361             }
28362         }
28363         
28364         height = 300;
28365         width = Math.ceil(this.minWidth * height / this.minHeight);
28366         
28367         if(this.minWidth > this.minHeight){
28368             width = 300;
28369             height = Math.ceil(this.minHeight * width / this.minWidth);
28370         }
28371         
28372         this.thumbEl.setStyle({
28373             width : width + 'px',
28374             height : height + 'px'
28375         });
28376
28377         return;
28378             
28379     },
28380     
28381     setThumbBoxPosition : function()
28382     {
28383         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28384         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28385         
28386         this.thumbEl.setLeft(x);
28387         this.thumbEl.setTop(y);
28388         
28389     },
28390     
28391     baseRotateLevel : function()
28392     {
28393         this.baseRotate = 1;
28394         
28395         if(
28396                 typeof(this.exif) != 'undefined' &&
28397                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28398                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28399         ){
28400             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28401         }
28402         
28403         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28404         
28405     },
28406     
28407     baseScaleLevel : function()
28408     {
28409         var width, height;
28410         
28411         if(this.isDocument){
28412             
28413             if(this.baseRotate == 6 || this.baseRotate == 8){
28414             
28415                 height = this.thumbEl.getHeight();
28416                 this.baseScale = height / this.imageEl.OriginWidth;
28417
28418                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28419                     width = this.thumbEl.getWidth();
28420                     this.baseScale = width / this.imageEl.OriginHeight;
28421                 }
28422
28423                 return;
28424             }
28425
28426             height = this.thumbEl.getHeight();
28427             this.baseScale = height / this.imageEl.OriginHeight;
28428
28429             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28430                 width = this.thumbEl.getWidth();
28431                 this.baseScale = width / this.imageEl.OriginWidth;
28432             }
28433
28434             return;
28435         }
28436         
28437         if(this.baseRotate == 6 || this.baseRotate == 8){
28438             
28439             width = this.thumbEl.getHeight();
28440             this.baseScale = width / this.imageEl.OriginHeight;
28441             
28442             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28443                 height = this.thumbEl.getWidth();
28444                 this.baseScale = height / this.imageEl.OriginHeight;
28445             }
28446             
28447             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28448                 height = this.thumbEl.getWidth();
28449                 this.baseScale = height / this.imageEl.OriginHeight;
28450                 
28451                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28452                     width = this.thumbEl.getHeight();
28453                     this.baseScale = width / this.imageEl.OriginWidth;
28454                 }
28455             }
28456             
28457             return;
28458         }
28459         
28460         width = this.thumbEl.getWidth();
28461         this.baseScale = width / this.imageEl.OriginWidth;
28462         
28463         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28464             height = this.thumbEl.getHeight();
28465             this.baseScale = height / this.imageEl.OriginHeight;
28466         }
28467         
28468         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28469             
28470             height = this.thumbEl.getHeight();
28471             this.baseScale = height / this.imageEl.OriginHeight;
28472             
28473             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28474                 width = this.thumbEl.getWidth();
28475                 this.baseScale = width / this.imageEl.OriginWidth;
28476             }
28477             
28478         }
28479         
28480         return;
28481     },
28482     
28483     getScaleLevel : function()
28484     {
28485         return this.baseScale * Math.pow(1.1, this.scale);
28486     },
28487     
28488     onTouchStart : function(e)
28489     {
28490         if(!this.canvasLoaded){
28491             this.beforeSelectFile(e);
28492             return;
28493         }
28494         
28495         var touches = e.browserEvent.touches;
28496         
28497         if(!touches){
28498             return;
28499         }
28500         
28501         if(touches.length == 1){
28502             this.onMouseDown(e);
28503             return;
28504         }
28505         
28506         if(touches.length != 2){
28507             return;
28508         }
28509         
28510         var coords = [];
28511         
28512         for(var i = 0, finger; finger = touches[i]; i++){
28513             coords.push(finger.pageX, finger.pageY);
28514         }
28515         
28516         var x = Math.pow(coords[0] - coords[2], 2);
28517         var y = Math.pow(coords[1] - coords[3], 2);
28518         
28519         this.startDistance = Math.sqrt(x + y);
28520         
28521         this.startScale = this.scale;
28522         
28523         this.pinching = true;
28524         this.dragable = false;
28525         
28526     },
28527     
28528     onTouchMove : function(e)
28529     {
28530         if(!this.pinching && !this.dragable){
28531             return;
28532         }
28533         
28534         var touches = e.browserEvent.touches;
28535         
28536         if(!touches){
28537             return;
28538         }
28539         
28540         if(this.dragable){
28541             this.onMouseMove(e);
28542             return;
28543         }
28544         
28545         var coords = [];
28546         
28547         for(var i = 0, finger; finger = touches[i]; i++){
28548             coords.push(finger.pageX, finger.pageY);
28549         }
28550         
28551         var x = Math.pow(coords[0] - coords[2], 2);
28552         var y = Math.pow(coords[1] - coords[3], 2);
28553         
28554         this.endDistance = Math.sqrt(x + y);
28555         
28556         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28557         
28558         if(!this.zoomable()){
28559             this.scale = this.startScale;
28560             return;
28561         }
28562         
28563         this.draw();
28564         
28565     },
28566     
28567     onTouchEnd : function(e)
28568     {
28569         this.pinching = false;
28570         this.dragable = false;
28571         
28572     },
28573     
28574     process : function(file, crop)
28575     {
28576         if(this.loadMask){
28577             this.maskEl.mask(this.loadingText);
28578         }
28579         
28580         this.xhr = new XMLHttpRequest();
28581         
28582         file.xhr = this.xhr;
28583
28584         this.xhr.open(this.method, this.url, true);
28585         
28586         var headers = {
28587             "Accept": "application/json",
28588             "Cache-Control": "no-cache",
28589             "X-Requested-With": "XMLHttpRequest"
28590         };
28591         
28592         for (var headerName in headers) {
28593             var headerValue = headers[headerName];
28594             if (headerValue) {
28595                 this.xhr.setRequestHeader(headerName, headerValue);
28596             }
28597         }
28598         
28599         var _this = this;
28600         
28601         this.xhr.onload = function()
28602         {
28603             _this.xhrOnLoad(_this.xhr);
28604         }
28605         
28606         this.xhr.onerror = function()
28607         {
28608             _this.xhrOnError(_this.xhr);
28609         }
28610         
28611         var formData = new FormData();
28612
28613         formData.append('returnHTML', 'NO');
28614         
28615         if(crop){
28616             formData.append('crop', crop);
28617         }
28618         
28619         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28620             formData.append(this.paramName, file, file.name);
28621         }
28622         
28623         if(typeof(file.filename) != 'undefined'){
28624             formData.append('filename', file.filename);
28625         }
28626         
28627         if(typeof(file.mimetype) != 'undefined'){
28628             formData.append('mimetype', file.mimetype);
28629         }
28630         
28631         if(this.fireEvent('arrange', this, formData) != false){
28632             this.xhr.send(formData);
28633         };
28634     },
28635     
28636     xhrOnLoad : function(xhr)
28637     {
28638         if(this.loadMask){
28639             this.maskEl.unmask();
28640         }
28641         
28642         if (xhr.readyState !== 4) {
28643             this.fireEvent('exception', this, xhr);
28644             return;
28645         }
28646
28647         var response = Roo.decode(xhr.responseText);
28648         
28649         if(!response.success){
28650             this.fireEvent('exception', this, xhr);
28651             return;
28652         }
28653         
28654         var response = Roo.decode(xhr.responseText);
28655         
28656         this.fireEvent('upload', this, response);
28657         
28658     },
28659     
28660     xhrOnError : function()
28661     {
28662         if(this.loadMask){
28663             this.maskEl.unmask();
28664         }
28665         
28666         Roo.log('xhr on error');
28667         
28668         var response = Roo.decode(xhr.responseText);
28669           
28670         Roo.log(response);
28671         
28672     },
28673     
28674     prepare : function(file)
28675     {   
28676         if(this.loadMask){
28677             this.maskEl.mask(this.loadingText);
28678         }
28679         
28680         this.file = false;
28681         this.exif = {};
28682         
28683         if(typeof(file) === 'string'){
28684             this.loadCanvas(file);
28685             return;
28686         }
28687         
28688         if(!file || !this.urlAPI){
28689             return;
28690         }
28691         
28692         this.file = file;
28693         this.cropType = file.type;
28694         
28695         var _this = this;
28696         
28697         if(this.fireEvent('prepare', this, this.file) != false){
28698             
28699             var reader = new FileReader();
28700             
28701             reader.onload = function (e) {
28702                 if (e.target.error) {
28703                     Roo.log(e.target.error);
28704                     return;
28705                 }
28706                 
28707                 var buffer = e.target.result,
28708                     dataView = new DataView(buffer),
28709                     offset = 2,
28710                     maxOffset = dataView.byteLength - 4,
28711                     markerBytes,
28712                     markerLength;
28713                 
28714                 if (dataView.getUint16(0) === 0xffd8) {
28715                     while (offset < maxOffset) {
28716                         markerBytes = dataView.getUint16(offset);
28717                         
28718                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28719                             markerLength = dataView.getUint16(offset + 2) + 2;
28720                             if (offset + markerLength > dataView.byteLength) {
28721                                 Roo.log('Invalid meta data: Invalid segment size.');
28722                                 break;
28723                             }
28724                             
28725                             if(markerBytes == 0xffe1){
28726                                 _this.parseExifData(
28727                                     dataView,
28728                                     offset,
28729                                     markerLength
28730                                 );
28731                             }
28732                             
28733                             offset += markerLength;
28734                             
28735                             continue;
28736                         }
28737                         
28738                         break;
28739                     }
28740                     
28741                 }
28742                 
28743                 var url = _this.urlAPI.createObjectURL(_this.file);
28744                 
28745                 _this.loadCanvas(url);
28746                 
28747                 return;
28748             }
28749             
28750             reader.readAsArrayBuffer(this.file);
28751             
28752         }
28753         
28754     },
28755     
28756     parseExifData : function(dataView, offset, length)
28757     {
28758         var tiffOffset = offset + 10,
28759             littleEndian,
28760             dirOffset;
28761     
28762         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28763             // No Exif data, might be XMP data instead
28764             return;
28765         }
28766         
28767         // Check for the ASCII code for "Exif" (0x45786966):
28768         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28769             // No Exif data, might be XMP data instead
28770             return;
28771         }
28772         if (tiffOffset + 8 > dataView.byteLength) {
28773             Roo.log('Invalid Exif data: Invalid segment size.');
28774             return;
28775         }
28776         // Check for the two null bytes:
28777         if (dataView.getUint16(offset + 8) !== 0x0000) {
28778             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28779             return;
28780         }
28781         // Check the byte alignment:
28782         switch (dataView.getUint16(tiffOffset)) {
28783         case 0x4949:
28784             littleEndian = true;
28785             break;
28786         case 0x4D4D:
28787             littleEndian = false;
28788             break;
28789         default:
28790             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28791             return;
28792         }
28793         // Check for the TIFF tag marker (0x002A):
28794         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28795             Roo.log('Invalid Exif data: Missing TIFF marker.');
28796             return;
28797         }
28798         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28799         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28800         
28801         this.parseExifTags(
28802             dataView,
28803             tiffOffset,
28804             tiffOffset + dirOffset,
28805             littleEndian
28806         );
28807     },
28808     
28809     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28810     {
28811         var tagsNumber,
28812             dirEndOffset,
28813             i;
28814         if (dirOffset + 6 > dataView.byteLength) {
28815             Roo.log('Invalid Exif data: Invalid directory offset.');
28816             return;
28817         }
28818         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28819         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28820         if (dirEndOffset + 4 > dataView.byteLength) {
28821             Roo.log('Invalid Exif data: Invalid directory size.');
28822             return;
28823         }
28824         for (i = 0; i < tagsNumber; i += 1) {
28825             this.parseExifTag(
28826                 dataView,
28827                 tiffOffset,
28828                 dirOffset + 2 + 12 * i, // tag offset
28829                 littleEndian
28830             );
28831         }
28832         // Return the offset to the next directory:
28833         return dataView.getUint32(dirEndOffset, littleEndian);
28834     },
28835     
28836     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28837     {
28838         var tag = dataView.getUint16(offset, littleEndian);
28839         
28840         this.exif[tag] = this.getExifValue(
28841             dataView,
28842             tiffOffset,
28843             offset,
28844             dataView.getUint16(offset + 2, littleEndian), // tag type
28845             dataView.getUint32(offset + 4, littleEndian), // tag length
28846             littleEndian
28847         );
28848     },
28849     
28850     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28851     {
28852         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28853             tagSize,
28854             dataOffset,
28855             values,
28856             i,
28857             str,
28858             c;
28859     
28860         if (!tagType) {
28861             Roo.log('Invalid Exif data: Invalid tag type.');
28862             return;
28863         }
28864         
28865         tagSize = tagType.size * length;
28866         // Determine if the value is contained in the dataOffset bytes,
28867         // or if the value at the dataOffset is a pointer to the actual data:
28868         dataOffset = tagSize > 4 ?
28869                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28870         if (dataOffset + tagSize > dataView.byteLength) {
28871             Roo.log('Invalid Exif data: Invalid data offset.');
28872             return;
28873         }
28874         if (length === 1) {
28875             return tagType.getValue(dataView, dataOffset, littleEndian);
28876         }
28877         values = [];
28878         for (i = 0; i < length; i += 1) {
28879             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28880         }
28881         
28882         if (tagType.ascii) {
28883             str = '';
28884             // Concatenate the chars:
28885             for (i = 0; i < values.length; i += 1) {
28886                 c = values[i];
28887                 // Ignore the terminating NULL byte(s):
28888                 if (c === '\u0000') {
28889                     break;
28890                 }
28891                 str += c;
28892             }
28893             return str;
28894         }
28895         return values;
28896     }
28897     
28898 });
28899
28900 Roo.apply(Roo.bootstrap.UploadCropbox, {
28901     tags : {
28902         'Orientation': 0x0112
28903     },
28904     
28905     Orientation: {
28906             1: 0, //'top-left',
28907 //            2: 'top-right',
28908             3: 180, //'bottom-right',
28909 //            4: 'bottom-left',
28910 //            5: 'left-top',
28911             6: 90, //'right-top',
28912 //            7: 'right-bottom',
28913             8: 270 //'left-bottom'
28914     },
28915     
28916     exifTagTypes : {
28917         // byte, 8-bit unsigned int:
28918         1: {
28919             getValue: function (dataView, dataOffset) {
28920                 return dataView.getUint8(dataOffset);
28921             },
28922             size: 1
28923         },
28924         // ascii, 8-bit byte:
28925         2: {
28926             getValue: function (dataView, dataOffset) {
28927                 return String.fromCharCode(dataView.getUint8(dataOffset));
28928             },
28929             size: 1,
28930             ascii: true
28931         },
28932         // short, 16 bit int:
28933         3: {
28934             getValue: function (dataView, dataOffset, littleEndian) {
28935                 return dataView.getUint16(dataOffset, littleEndian);
28936             },
28937             size: 2
28938         },
28939         // long, 32 bit int:
28940         4: {
28941             getValue: function (dataView, dataOffset, littleEndian) {
28942                 return dataView.getUint32(dataOffset, littleEndian);
28943             },
28944             size: 4
28945         },
28946         // rational = two long values, first is numerator, second is denominator:
28947         5: {
28948             getValue: function (dataView, dataOffset, littleEndian) {
28949                 return dataView.getUint32(dataOffset, littleEndian) /
28950                     dataView.getUint32(dataOffset + 4, littleEndian);
28951             },
28952             size: 8
28953         },
28954         // slong, 32 bit signed int:
28955         9: {
28956             getValue: function (dataView, dataOffset, littleEndian) {
28957                 return dataView.getInt32(dataOffset, littleEndian);
28958             },
28959             size: 4
28960         },
28961         // srational, two slongs, first is numerator, second is denominator:
28962         10: {
28963             getValue: function (dataView, dataOffset, littleEndian) {
28964                 return dataView.getInt32(dataOffset, littleEndian) /
28965                     dataView.getInt32(dataOffset + 4, littleEndian);
28966             },
28967             size: 8
28968         }
28969     },
28970     
28971     footer : {
28972         STANDARD : [
28973             {
28974                 tag : 'div',
28975                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28976                 action : 'rotate-left',
28977                 cn : [
28978                     {
28979                         tag : 'button',
28980                         cls : 'btn btn-default',
28981                         html : '<i class="fa fa-undo"></i>'
28982                     }
28983                 ]
28984             },
28985             {
28986                 tag : 'div',
28987                 cls : 'btn-group roo-upload-cropbox-picture',
28988                 action : 'picture',
28989                 cn : [
28990                     {
28991                         tag : 'button',
28992                         cls : 'btn btn-default',
28993                         html : '<i class="fa fa-picture-o"></i>'
28994                     }
28995                 ]
28996             },
28997             {
28998                 tag : 'div',
28999                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29000                 action : 'rotate-right',
29001                 cn : [
29002                     {
29003                         tag : 'button',
29004                         cls : 'btn btn-default',
29005                         html : '<i class="fa fa-repeat"></i>'
29006                     }
29007                 ]
29008             }
29009         ],
29010         DOCUMENT : [
29011             {
29012                 tag : 'div',
29013                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29014                 action : 'rotate-left',
29015                 cn : [
29016                     {
29017                         tag : 'button',
29018                         cls : 'btn btn-default',
29019                         html : '<i class="fa fa-undo"></i>'
29020                     }
29021                 ]
29022             },
29023             {
29024                 tag : 'div',
29025                 cls : 'btn-group roo-upload-cropbox-download',
29026                 action : 'download',
29027                 cn : [
29028                     {
29029                         tag : 'button',
29030                         cls : 'btn btn-default',
29031                         html : '<i class="fa fa-download"></i>'
29032                     }
29033                 ]
29034             },
29035             {
29036                 tag : 'div',
29037                 cls : 'btn-group roo-upload-cropbox-crop',
29038                 action : 'crop',
29039                 cn : [
29040                     {
29041                         tag : 'button',
29042                         cls : 'btn btn-default',
29043                         html : '<i class="fa fa-crop"></i>'
29044                     }
29045                 ]
29046             },
29047             {
29048                 tag : 'div',
29049                 cls : 'btn-group roo-upload-cropbox-trash',
29050                 action : 'trash',
29051                 cn : [
29052                     {
29053                         tag : 'button',
29054                         cls : 'btn btn-default',
29055                         html : '<i class="fa fa-trash"></i>'
29056                     }
29057                 ]
29058             },
29059             {
29060                 tag : 'div',
29061                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29062                 action : 'rotate-right',
29063                 cn : [
29064                     {
29065                         tag : 'button',
29066                         cls : 'btn btn-default',
29067                         html : '<i class="fa fa-repeat"></i>'
29068                     }
29069                 ]
29070             }
29071         ],
29072         ROTATOR : [
29073             {
29074                 tag : 'div',
29075                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29076                 action : 'rotate-left',
29077                 cn : [
29078                     {
29079                         tag : 'button',
29080                         cls : 'btn btn-default',
29081                         html : '<i class="fa fa-undo"></i>'
29082                     }
29083                 ]
29084             },
29085             {
29086                 tag : 'div',
29087                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29088                 action : 'rotate-right',
29089                 cn : [
29090                     {
29091                         tag : 'button',
29092                         cls : 'btn btn-default',
29093                         html : '<i class="fa fa-repeat"></i>'
29094                     }
29095                 ]
29096             }
29097         ]
29098     }
29099 });
29100
29101 /*
29102 * Licence: LGPL
29103 */
29104
29105 /**
29106  * @class Roo.bootstrap.DocumentManager
29107  * @extends Roo.bootstrap.Component
29108  * Bootstrap DocumentManager class
29109  * @cfg {String} paramName default 'imageUpload'
29110  * @cfg {String} toolTipName default 'filename'
29111  * @cfg {String} method default POST
29112  * @cfg {String} url action url
29113  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29114  * @cfg {Boolean} multiple multiple upload default true
29115  * @cfg {Number} thumbSize default 300
29116  * @cfg {String} fieldLabel
29117  * @cfg {Number} labelWidth default 4
29118  * @cfg {String} labelAlign (left|top) default left
29119  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29120 * @cfg {Number} labellg set the width of label (1-12)
29121  * @cfg {Number} labelmd set the width of label (1-12)
29122  * @cfg {Number} labelsm set the width of label (1-12)
29123  * @cfg {Number} labelxs set the width of label (1-12)
29124  * 
29125  * @constructor
29126  * Create a new DocumentManager
29127  * @param {Object} config The config object
29128  */
29129
29130 Roo.bootstrap.DocumentManager = function(config){
29131     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29132     
29133     this.files = [];
29134     this.delegates = [];
29135     
29136     this.addEvents({
29137         /**
29138          * @event initial
29139          * Fire when initial the DocumentManager
29140          * @param {Roo.bootstrap.DocumentManager} this
29141          */
29142         "initial" : true,
29143         /**
29144          * @event inspect
29145          * inspect selected file
29146          * @param {Roo.bootstrap.DocumentManager} this
29147          * @param {File} file
29148          */
29149         "inspect" : true,
29150         /**
29151          * @event exception
29152          * Fire when xhr load exception
29153          * @param {Roo.bootstrap.DocumentManager} this
29154          * @param {XMLHttpRequest} xhr
29155          */
29156         "exception" : true,
29157         /**
29158          * @event afterupload
29159          * Fire when xhr load exception
29160          * @param {Roo.bootstrap.DocumentManager} this
29161          * @param {XMLHttpRequest} xhr
29162          */
29163         "afterupload" : true,
29164         /**
29165          * @event prepare
29166          * prepare the form data
29167          * @param {Roo.bootstrap.DocumentManager} this
29168          * @param {Object} formData
29169          */
29170         "prepare" : true,
29171         /**
29172          * @event remove
29173          * Fire when remove the file
29174          * @param {Roo.bootstrap.DocumentManager} this
29175          * @param {Object} file
29176          */
29177         "remove" : true,
29178         /**
29179          * @event refresh
29180          * Fire after refresh the file
29181          * @param {Roo.bootstrap.DocumentManager} this
29182          */
29183         "refresh" : true,
29184         /**
29185          * @event click
29186          * Fire after click the image
29187          * @param {Roo.bootstrap.DocumentManager} this
29188          * @param {Object} file
29189          */
29190         "click" : true,
29191         /**
29192          * @event edit
29193          * Fire when upload a image and editable set to true
29194          * @param {Roo.bootstrap.DocumentManager} this
29195          * @param {Object} file
29196          */
29197         "edit" : true,
29198         /**
29199          * @event beforeselectfile
29200          * Fire before select file
29201          * @param {Roo.bootstrap.DocumentManager} this
29202          */
29203         "beforeselectfile" : true,
29204         /**
29205          * @event process
29206          * Fire before process file
29207          * @param {Roo.bootstrap.DocumentManager} this
29208          * @param {Object} file
29209          */
29210         "process" : true,
29211         /**
29212          * @event previewrendered
29213          * Fire when preview rendered
29214          * @param {Roo.bootstrap.DocumentManager} this
29215          * @param {Object} file
29216          */
29217         "previewrendered" : true,
29218         /**
29219          */
29220         "previewResize" : true
29221         
29222     });
29223 };
29224
29225 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29226     
29227     boxes : 0,
29228     inputName : '',
29229     thumbSize : 300,
29230     multiple : true,
29231     files : false,
29232     method : 'POST',
29233     url : '',
29234     paramName : 'imageUpload',
29235     toolTipName : 'filename',
29236     fieldLabel : '',
29237     labelWidth : 4,
29238     labelAlign : 'left',
29239     editable : true,
29240     delegates : false,
29241     xhr : false, 
29242     
29243     labellg : 0,
29244     labelmd : 0,
29245     labelsm : 0,
29246     labelxs : 0,
29247     
29248     getAutoCreate : function()
29249     {   
29250         var managerWidget = {
29251             tag : 'div',
29252             cls : 'roo-document-manager',
29253             cn : [
29254                 {
29255                     tag : 'input',
29256                     cls : 'roo-document-manager-selector',
29257                     type : 'file'
29258                 },
29259                 {
29260                     tag : 'div',
29261                     cls : 'roo-document-manager-uploader',
29262                     cn : [
29263                         {
29264                             tag : 'div',
29265                             cls : 'roo-document-manager-upload-btn',
29266                             html : '<i class="fa fa-plus"></i>'
29267                         }
29268                     ]
29269                     
29270                 }
29271             ]
29272         };
29273         
29274         var content = [
29275             {
29276                 tag : 'div',
29277                 cls : 'column col-md-12',
29278                 cn : managerWidget
29279             }
29280         ];
29281         
29282         if(this.fieldLabel.length){
29283             
29284             content = [
29285                 {
29286                     tag : 'div',
29287                     cls : 'column col-md-12',
29288                     html : this.fieldLabel
29289                 },
29290                 {
29291                     tag : 'div',
29292                     cls : 'column col-md-12',
29293                     cn : managerWidget
29294                 }
29295             ];
29296
29297             if(this.labelAlign == 'left'){
29298                 content = [
29299                     {
29300                         tag : 'div',
29301                         cls : 'column',
29302                         html : this.fieldLabel
29303                     },
29304                     {
29305                         tag : 'div',
29306                         cls : 'column',
29307                         cn : managerWidget
29308                     }
29309                 ];
29310                 
29311                 if(this.labelWidth > 12){
29312                     content[0].style = "width: " + this.labelWidth + 'px';
29313                 }
29314
29315                 if(this.labelWidth < 13 && this.labelmd == 0){
29316                     this.labelmd = this.labelWidth;
29317                 }
29318
29319                 if(this.labellg > 0){
29320                     content[0].cls += ' col-lg-' + this.labellg;
29321                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29322                 }
29323
29324                 if(this.labelmd > 0){
29325                     content[0].cls += ' col-md-' + this.labelmd;
29326                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29327                 }
29328
29329                 if(this.labelsm > 0){
29330                     content[0].cls += ' col-sm-' + this.labelsm;
29331                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29332                 }
29333
29334                 if(this.labelxs > 0){
29335                     content[0].cls += ' col-xs-' + this.labelxs;
29336                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29337                 }
29338                 
29339             }
29340         }
29341         
29342         var cfg = {
29343             tag : 'div',
29344             cls : 'row clearfix',
29345             cn : content
29346         };
29347         
29348         return cfg;
29349         
29350     },
29351     
29352     initEvents : function()
29353     {
29354         this.managerEl = this.el.select('.roo-document-manager', true).first();
29355         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29356         
29357         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29358         this.selectorEl.hide();
29359         
29360         if(this.multiple){
29361             this.selectorEl.attr('multiple', 'multiple');
29362         }
29363         
29364         this.selectorEl.on('change', this.onFileSelected, this);
29365         
29366         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29367         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29368         
29369         this.uploader.on('click', this.onUploaderClick, this);
29370         
29371         this.renderProgressDialog();
29372         
29373         var _this = this;
29374         
29375         window.addEventListener("resize", function() { _this.refresh(); } );
29376         
29377         this.fireEvent('initial', this);
29378     },
29379     
29380     renderProgressDialog : function()
29381     {
29382         var _this = this;
29383         
29384         this.progressDialog = new Roo.bootstrap.Modal({
29385             cls : 'roo-document-manager-progress-dialog',
29386             allow_close : false,
29387             animate : false,
29388             title : '',
29389             buttons : [
29390                 {
29391                     name  :'cancel',
29392                     weight : 'danger',
29393                     html : 'Cancel'
29394                 }
29395             ], 
29396             listeners : { 
29397                 btnclick : function() {
29398                     _this.uploadCancel();
29399                     this.hide();
29400                 }
29401             }
29402         });
29403          
29404         this.progressDialog.render(Roo.get(document.body));
29405          
29406         this.progress = new Roo.bootstrap.Progress({
29407             cls : 'roo-document-manager-progress',
29408             active : true,
29409             striped : true
29410         });
29411         
29412         this.progress.render(this.progressDialog.getChildContainer());
29413         
29414         this.progressBar = new Roo.bootstrap.ProgressBar({
29415             cls : 'roo-document-manager-progress-bar',
29416             aria_valuenow : 0,
29417             aria_valuemin : 0,
29418             aria_valuemax : 12,
29419             panel : 'success'
29420         });
29421         
29422         this.progressBar.render(this.progress.getChildContainer());
29423     },
29424     
29425     onUploaderClick : function(e)
29426     {
29427         e.preventDefault();
29428      
29429         if(this.fireEvent('beforeselectfile', this) != false){
29430             this.selectorEl.dom.click();
29431         }
29432         
29433     },
29434     
29435     onFileSelected : function(e)
29436     {
29437         e.preventDefault();
29438         
29439         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29440             return;
29441         }
29442         
29443         Roo.each(this.selectorEl.dom.files, function(file){
29444             if(this.fireEvent('inspect', this, file) != false){
29445                 this.files.push(file);
29446             }
29447         }, this);
29448         
29449         this.queue();
29450         
29451     },
29452     
29453     queue : function()
29454     {
29455         this.selectorEl.dom.value = '';
29456         
29457         if(!this.files || !this.files.length){
29458             return;
29459         }
29460         
29461         if(this.boxes > 0 && this.files.length > this.boxes){
29462             this.files = this.files.slice(0, this.boxes);
29463         }
29464         
29465         this.uploader.show();
29466         
29467         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29468             this.uploader.hide();
29469         }
29470         
29471         var _this = this;
29472         
29473         var files = [];
29474         
29475         var docs = [];
29476         
29477         Roo.each(this.files, function(file){
29478             
29479             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29480                 var f = this.renderPreview(file);
29481                 files.push(f);
29482                 return;
29483             }
29484             
29485             if(file.type.indexOf('image') != -1){
29486                 this.delegates.push(
29487                     (function(){
29488                         _this.process(file);
29489                     }).createDelegate(this)
29490                 );
29491         
29492                 return;
29493             }
29494             
29495             docs.push(
29496                 (function(){
29497                     _this.process(file);
29498                 }).createDelegate(this)
29499             );
29500             
29501         }, this);
29502         
29503         this.files = files;
29504         
29505         this.delegates = this.delegates.concat(docs);
29506         
29507         if(!this.delegates.length){
29508             this.refresh();
29509             return;
29510         }
29511         
29512         this.progressBar.aria_valuemax = this.delegates.length;
29513         
29514         this.arrange();
29515         
29516         return;
29517     },
29518     
29519     arrange : function()
29520     {
29521         if(!this.delegates.length){
29522             this.progressDialog.hide();
29523             this.refresh();
29524             return;
29525         }
29526         
29527         var delegate = this.delegates.shift();
29528         
29529         this.progressDialog.show();
29530         
29531         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29532         
29533         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29534         
29535         delegate();
29536     },
29537     
29538     refresh : function()
29539     {
29540         this.uploader.show();
29541         
29542         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29543             this.uploader.hide();
29544         }
29545         
29546         Roo.isTouch ? this.closable(false) : this.closable(true);
29547         
29548         this.fireEvent('refresh', this);
29549     },
29550     
29551     onRemove : function(e, el, o)
29552     {
29553         e.preventDefault();
29554         
29555         this.fireEvent('remove', this, o);
29556         
29557     },
29558     
29559     remove : function(o)
29560     {
29561         var files = [];
29562         
29563         Roo.each(this.files, function(file){
29564             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29565                 files.push(file);
29566                 return;
29567             }
29568
29569             o.target.remove();
29570
29571         }, this);
29572         
29573         this.files = files;
29574         
29575         this.refresh();
29576     },
29577     
29578     clear : function()
29579     {
29580         Roo.each(this.files, function(file){
29581             if(!file.target){
29582                 return;
29583             }
29584             
29585             file.target.remove();
29586
29587         }, this);
29588         
29589         this.files = [];
29590         
29591         this.refresh();
29592     },
29593     
29594     onClick : function(e, el, o)
29595     {
29596         e.preventDefault();
29597         
29598         this.fireEvent('click', this, o);
29599         
29600     },
29601     
29602     closable : function(closable)
29603     {
29604         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29605             
29606             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29607             
29608             if(closable){
29609                 el.show();
29610                 return;
29611             }
29612             
29613             el.hide();
29614             
29615         }, this);
29616     },
29617     
29618     xhrOnLoad : function(xhr)
29619     {
29620         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29621             el.remove();
29622         }, this);
29623         
29624         if (xhr.readyState !== 4) {
29625             this.arrange();
29626             this.fireEvent('exception', this, xhr);
29627             return;
29628         }
29629
29630         var response = Roo.decode(xhr.responseText);
29631         
29632         if(!response.success){
29633             this.arrange();
29634             this.fireEvent('exception', this, xhr);
29635             return;
29636         }
29637         
29638         var file = this.renderPreview(response.data);
29639         
29640         this.files.push(file);
29641         
29642         this.arrange();
29643         
29644         this.fireEvent('afterupload', this, xhr);
29645         
29646     },
29647     
29648     xhrOnError : function(xhr)
29649     {
29650         Roo.log('xhr on error');
29651         
29652         var response = Roo.decode(xhr.responseText);
29653           
29654         Roo.log(response);
29655         
29656         this.arrange();
29657     },
29658     
29659     process : function(file)
29660     {
29661         if(this.fireEvent('process', this, file) !== false){
29662             if(this.editable && file.type.indexOf('image') != -1){
29663                 this.fireEvent('edit', this, file);
29664                 return;
29665             }
29666
29667             this.uploadStart(file, false);
29668
29669             return;
29670         }
29671         
29672     },
29673     
29674     uploadStart : function(file, crop)
29675     {
29676         this.xhr = new XMLHttpRequest();
29677         
29678         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29679             this.arrange();
29680             return;
29681         }
29682         
29683         file.xhr = this.xhr;
29684             
29685         this.managerEl.createChild({
29686             tag : 'div',
29687             cls : 'roo-document-manager-loading',
29688             cn : [
29689                 {
29690                     tag : 'div',
29691                     tooltip : file.name,
29692                     cls : 'roo-document-manager-thumb',
29693                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29694                 }
29695             ]
29696
29697         });
29698
29699         this.xhr.open(this.method, this.url, true);
29700         
29701         var headers = {
29702             "Accept": "application/json",
29703             "Cache-Control": "no-cache",
29704             "X-Requested-With": "XMLHttpRequest"
29705         };
29706         
29707         for (var headerName in headers) {
29708             var headerValue = headers[headerName];
29709             if (headerValue) {
29710                 this.xhr.setRequestHeader(headerName, headerValue);
29711             }
29712         }
29713         
29714         var _this = this;
29715         
29716         this.xhr.onload = function()
29717         {
29718             _this.xhrOnLoad(_this.xhr);
29719         }
29720         
29721         this.xhr.onerror = function()
29722         {
29723             _this.xhrOnError(_this.xhr);
29724         }
29725         
29726         var formData = new FormData();
29727
29728         formData.append('returnHTML', 'NO');
29729         
29730         if(crop){
29731             formData.append('crop', crop);
29732         }
29733         
29734         formData.append(this.paramName, file, file.name);
29735         
29736         var options = {
29737             file : file, 
29738             manually : false
29739         };
29740         
29741         if(this.fireEvent('prepare', this, formData, options) != false){
29742             
29743             if(options.manually){
29744                 return;
29745             }
29746             
29747             this.xhr.send(formData);
29748             return;
29749         };
29750         
29751         this.uploadCancel();
29752     },
29753     
29754     uploadCancel : function()
29755     {
29756         if (this.xhr) {
29757             this.xhr.abort();
29758         }
29759         
29760         this.delegates = [];
29761         
29762         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29763             el.remove();
29764         }, this);
29765         
29766         this.arrange();
29767     },
29768     
29769     renderPreview : function(file)
29770     {
29771         if(typeof(file.target) != 'undefined' && file.target){
29772             return file;
29773         }
29774         
29775         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29776         
29777         var previewEl = this.managerEl.createChild({
29778             tag : 'div',
29779             cls : 'roo-document-manager-preview',
29780             cn : [
29781                 {
29782                     tag : 'div',
29783                     tooltip : file[this.toolTipName],
29784                     cls : 'roo-document-manager-thumb',
29785                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29786                 },
29787                 {
29788                     tag : 'button',
29789                     cls : 'close',
29790                     html : '<i class="fa fa-times-circle"></i>'
29791                 }
29792             ]
29793         });
29794
29795         var close = previewEl.select('button.close', true).first();
29796
29797         close.on('click', this.onRemove, this, file);
29798
29799         file.target = previewEl;
29800
29801         var image = previewEl.select('img', true).first();
29802         
29803         var _this = this;
29804         
29805         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29806         
29807         image.on('click', this.onClick, this, file);
29808         
29809         this.fireEvent('previewrendered', this, file);
29810         
29811         return file;
29812         
29813     },
29814     
29815     onPreviewLoad : function(file, image)
29816     {
29817         if(typeof(file.target) == 'undefined' || !file.target){
29818             return;
29819         }
29820         
29821         var width = image.dom.naturalWidth || image.dom.width;
29822         var height = image.dom.naturalHeight || image.dom.height;
29823         
29824         if(!this.previewResize) {
29825             return;
29826         }
29827         
29828         if(width > height){
29829             file.target.addClass('wide');
29830             return;
29831         }
29832         
29833         file.target.addClass('tall');
29834         return;
29835         
29836     },
29837     
29838     uploadFromSource : function(file, crop)
29839     {
29840         this.xhr = new XMLHttpRequest();
29841         
29842         this.managerEl.createChild({
29843             tag : 'div',
29844             cls : 'roo-document-manager-loading',
29845             cn : [
29846                 {
29847                     tag : 'div',
29848                     tooltip : file.name,
29849                     cls : 'roo-document-manager-thumb',
29850                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29851                 }
29852             ]
29853
29854         });
29855
29856         this.xhr.open(this.method, this.url, true);
29857         
29858         var headers = {
29859             "Accept": "application/json",
29860             "Cache-Control": "no-cache",
29861             "X-Requested-With": "XMLHttpRequest"
29862         };
29863         
29864         for (var headerName in headers) {
29865             var headerValue = headers[headerName];
29866             if (headerValue) {
29867                 this.xhr.setRequestHeader(headerName, headerValue);
29868             }
29869         }
29870         
29871         var _this = this;
29872         
29873         this.xhr.onload = function()
29874         {
29875             _this.xhrOnLoad(_this.xhr);
29876         }
29877         
29878         this.xhr.onerror = function()
29879         {
29880             _this.xhrOnError(_this.xhr);
29881         }
29882         
29883         var formData = new FormData();
29884
29885         formData.append('returnHTML', 'NO');
29886         
29887         formData.append('crop', crop);
29888         
29889         if(typeof(file.filename) != 'undefined'){
29890             formData.append('filename', file.filename);
29891         }
29892         
29893         if(typeof(file.mimetype) != 'undefined'){
29894             formData.append('mimetype', file.mimetype);
29895         }
29896         
29897         Roo.log(formData);
29898         
29899         if(this.fireEvent('prepare', this, formData) != false){
29900             this.xhr.send(formData);
29901         };
29902     }
29903 });
29904
29905 /*
29906 * Licence: LGPL
29907 */
29908
29909 /**
29910  * @class Roo.bootstrap.DocumentViewer
29911  * @extends Roo.bootstrap.Component
29912  * Bootstrap DocumentViewer class
29913  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29914  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29915  * 
29916  * @constructor
29917  * Create a new DocumentViewer
29918  * @param {Object} config The config object
29919  */
29920
29921 Roo.bootstrap.DocumentViewer = function(config){
29922     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29923     
29924     this.addEvents({
29925         /**
29926          * @event initial
29927          * Fire after initEvent
29928          * @param {Roo.bootstrap.DocumentViewer} this
29929          */
29930         "initial" : true,
29931         /**
29932          * @event click
29933          * Fire after click
29934          * @param {Roo.bootstrap.DocumentViewer} this
29935          */
29936         "click" : true,
29937         /**
29938          * @event download
29939          * Fire after download button
29940          * @param {Roo.bootstrap.DocumentViewer} this
29941          */
29942         "download" : true,
29943         /**
29944          * @event trash
29945          * Fire after trash button
29946          * @param {Roo.bootstrap.DocumentViewer} this
29947          */
29948         "trash" : true
29949         
29950     });
29951 };
29952
29953 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29954     
29955     showDownload : true,
29956     
29957     showTrash : true,
29958     
29959     getAutoCreate : function()
29960     {
29961         var cfg = {
29962             tag : 'div',
29963             cls : 'roo-document-viewer',
29964             cn : [
29965                 {
29966                     tag : 'div',
29967                     cls : 'roo-document-viewer-body',
29968                     cn : [
29969                         {
29970                             tag : 'div',
29971                             cls : 'roo-document-viewer-thumb',
29972                             cn : [
29973                                 {
29974                                     tag : 'img',
29975                                     cls : 'roo-document-viewer-image'
29976                                 }
29977                             ]
29978                         }
29979                     ]
29980                 },
29981                 {
29982                     tag : 'div',
29983                     cls : 'roo-document-viewer-footer',
29984                     cn : {
29985                         tag : 'div',
29986                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29987                         cn : [
29988                             {
29989                                 tag : 'div',
29990                                 cls : 'btn-group roo-document-viewer-download',
29991                                 cn : [
29992                                     {
29993                                         tag : 'button',
29994                                         cls : 'btn btn-default',
29995                                         html : '<i class="fa fa-download"></i>'
29996                                     }
29997                                 ]
29998                             },
29999                             {
30000                                 tag : 'div',
30001                                 cls : 'btn-group roo-document-viewer-trash',
30002                                 cn : [
30003                                     {
30004                                         tag : 'button',
30005                                         cls : 'btn btn-default',
30006                                         html : '<i class="fa fa-trash"></i>'
30007                                     }
30008                                 ]
30009                             }
30010                         ]
30011                     }
30012                 }
30013             ]
30014         };
30015         
30016         return cfg;
30017     },
30018     
30019     initEvents : function()
30020     {
30021         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30022         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30023         
30024         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30025         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30026         
30027         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30028         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30029         
30030         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30031         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30032         
30033         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30034         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30035         
30036         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30037         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30038         
30039         this.bodyEl.on('click', this.onClick, this);
30040         this.downloadBtn.on('click', this.onDownload, this);
30041         this.trashBtn.on('click', this.onTrash, this);
30042         
30043         this.downloadBtn.hide();
30044         this.trashBtn.hide();
30045         
30046         if(this.showDownload){
30047             this.downloadBtn.show();
30048         }
30049         
30050         if(this.showTrash){
30051             this.trashBtn.show();
30052         }
30053         
30054         if(!this.showDownload && !this.showTrash) {
30055             this.footerEl.hide();
30056         }
30057         
30058     },
30059     
30060     initial : function()
30061     {
30062         this.fireEvent('initial', this);
30063         
30064     },
30065     
30066     onClick : function(e)
30067     {
30068         e.preventDefault();
30069         
30070         this.fireEvent('click', this);
30071     },
30072     
30073     onDownload : function(e)
30074     {
30075         e.preventDefault();
30076         
30077         this.fireEvent('download', this);
30078     },
30079     
30080     onTrash : function(e)
30081     {
30082         e.preventDefault();
30083         
30084         this.fireEvent('trash', this);
30085     }
30086     
30087 });
30088 /*
30089  * - LGPL
30090  *
30091  * nav progress bar
30092  * 
30093  */
30094
30095 /**
30096  * @class Roo.bootstrap.NavProgressBar
30097  * @extends Roo.bootstrap.Component
30098  * Bootstrap NavProgressBar class
30099  * 
30100  * @constructor
30101  * Create a new nav progress bar
30102  * @param {Object} config The config object
30103  */
30104
30105 Roo.bootstrap.NavProgressBar = function(config){
30106     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30107
30108     this.bullets = this.bullets || [];
30109    
30110 //    Roo.bootstrap.NavProgressBar.register(this);
30111      this.addEvents({
30112         /**
30113              * @event changed
30114              * Fires when the active item changes
30115              * @param {Roo.bootstrap.NavProgressBar} this
30116              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30117              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30118          */
30119         'changed': true
30120      });
30121     
30122 };
30123
30124 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30125     
30126     bullets : [],
30127     barItems : [],
30128     
30129     getAutoCreate : function()
30130     {
30131         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30132         
30133         cfg = {
30134             tag : 'div',
30135             cls : 'roo-navigation-bar-group',
30136             cn : [
30137                 {
30138                     tag : 'div',
30139                     cls : 'roo-navigation-top-bar'
30140                 },
30141                 {
30142                     tag : 'div',
30143                     cls : 'roo-navigation-bullets-bar',
30144                     cn : [
30145                         {
30146                             tag : 'ul',
30147                             cls : 'roo-navigation-bar'
30148                         }
30149                     ]
30150                 },
30151                 
30152                 {
30153                     tag : 'div',
30154                     cls : 'roo-navigation-bottom-bar'
30155                 }
30156             ]
30157             
30158         };
30159         
30160         return cfg;
30161         
30162     },
30163     
30164     initEvents: function() 
30165     {
30166         
30167     },
30168     
30169     onRender : function(ct, position) 
30170     {
30171         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30172         
30173         if(this.bullets.length){
30174             Roo.each(this.bullets, function(b){
30175                this.addItem(b);
30176             }, this);
30177         }
30178         
30179         this.format();
30180         
30181     },
30182     
30183     addItem : function(cfg)
30184     {
30185         var item = new Roo.bootstrap.NavProgressItem(cfg);
30186         
30187         item.parentId = this.id;
30188         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30189         
30190         if(cfg.html){
30191             var top = new Roo.bootstrap.Element({
30192                 tag : 'div',
30193                 cls : 'roo-navigation-bar-text'
30194             });
30195             
30196             var bottom = new Roo.bootstrap.Element({
30197                 tag : 'div',
30198                 cls : 'roo-navigation-bar-text'
30199             });
30200             
30201             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30202             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30203             
30204             var topText = new Roo.bootstrap.Element({
30205                 tag : 'span',
30206                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30207             });
30208             
30209             var bottomText = new Roo.bootstrap.Element({
30210                 tag : 'span',
30211                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30212             });
30213             
30214             topText.onRender(top.el, null);
30215             bottomText.onRender(bottom.el, null);
30216             
30217             item.topEl = top;
30218             item.bottomEl = bottom;
30219         }
30220         
30221         this.barItems.push(item);
30222         
30223         return item;
30224     },
30225     
30226     getActive : function()
30227     {
30228         var active = false;
30229         
30230         Roo.each(this.barItems, function(v){
30231             
30232             if (!v.isActive()) {
30233                 return;
30234             }
30235             
30236             active = v;
30237             return false;
30238             
30239         });
30240         
30241         return active;
30242     },
30243     
30244     setActiveItem : function(item)
30245     {
30246         var prev = false;
30247         
30248         Roo.each(this.barItems, function(v){
30249             if (v.rid == item.rid) {
30250                 return ;
30251             }
30252             
30253             if (v.isActive()) {
30254                 v.setActive(false);
30255                 prev = v;
30256             }
30257         });
30258
30259         item.setActive(true);
30260         
30261         this.fireEvent('changed', this, item, prev);
30262     },
30263     
30264     getBarItem: function(rid)
30265     {
30266         var ret = false;
30267         
30268         Roo.each(this.barItems, function(e) {
30269             if (e.rid != rid) {
30270                 return;
30271             }
30272             
30273             ret =  e;
30274             return false;
30275         });
30276         
30277         return ret;
30278     },
30279     
30280     indexOfItem : function(item)
30281     {
30282         var index = false;
30283         
30284         Roo.each(this.barItems, function(v, i){
30285             
30286             if (v.rid != item.rid) {
30287                 return;
30288             }
30289             
30290             index = i;
30291             return false
30292         });
30293         
30294         return index;
30295     },
30296     
30297     setActiveNext : function()
30298     {
30299         var i = this.indexOfItem(this.getActive());
30300         
30301         if (i > this.barItems.length) {
30302             return;
30303         }
30304         
30305         this.setActiveItem(this.barItems[i+1]);
30306     },
30307     
30308     setActivePrev : function()
30309     {
30310         var i = this.indexOfItem(this.getActive());
30311         
30312         if (i  < 1) {
30313             return;
30314         }
30315         
30316         this.setActiveItem(this.barItems[i-1]);
30317     },
30318     
30319     format : function()
30320     {
30321         if(!this.barItems.length){
30322             return;
30323         }
30324      
30325         var width = 100 / this.barItems.length;
30326         
30327         Roo.each(this.barItems, function(i){
30328             i.el.setStyle('width', width + '%');
30329             i.topEl.el.setStyle('width', width + '%');
30330             i.bottomEl.el.setStyle('width', width + '%');
30331         }, this);
30332         
30333     }
30334     
30335 });
30336 /*
30337  * - LGPL
30338  *
30339  * Nav Progress Item
30340  * 
30341  */
30342
30343 /**
30344  * @class Roo.bootstrap.NavProgressItem
30345  * @extends Roo.bootstrap.Component
30346  * Bootstrap NavProgressItem class
30347  * @cfg {String} rid the reference id
30348  * @cfg {Boolean} active (true|false) Is item active default false
30349  * @cfg {Boolean} disabled (true|false) Is item active default false
30350  * @cfg {String} html
30351  * @cfg {String} position (top|bottom) text position default bottom
30352  * @cfg {String} icon show icon instead of number
30353  * 
30354  * @constructor
30355  * Create a new NavProgressItem
30356  * @param {Object} config The config object
30357  */
30358 Roo.bootstrap.NavProgressItem = function(config){
30359     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30360     this.addEvents({
30361         // raw events
30362         /**
30363          * @event click
30364          * The raw click event for the entire grid.
30365          * @param {Roo.bootstrap.NavProgressItem} this
30366          * @param {Roo.EventObject} e
30367          */
30368         "click" : true
30369     });
30370    
30371 };
30372
30373 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30374     
30375     rid : '',
30376     active : false,
30377     disabled : false,
30378     html : '',
30379     position : 'bottom',
30380     icon : false,
30381     
30382     getAutoCreate : function()
30383     {
30384         var iconCls = 'roo-navigation-bar-item-icon';
30385         
30386         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30387         
30388         var cfg = {
30389             tag: 'li',
30390             cls: 'roo-navigation-bar-item',
30391             cn : [
30392                 {
30393                     tag : 'i',
30394                     cls : iconCls
30395                 }
30396             ]
30397         };
30398         
30399         if(this.active){
30400             cfg.cls += ' active';
30401         }
30402         if(this.disabled){
30403             cfg.cls += ' disabled';
30404         }
30405         
30406         return cfg;
30407     },
30408     
30409     disable : function()
30410     {
30411         this.setDisabled(true);
30412     },
30413     
30414     enable : function()
30415     {
30416         this.setDisabled(false);
30417     },
30418     
30419     initEvents: function() 
30420     {
30421         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30422         
30423         this.iconEl.on('click', this.onClick, this);
30424     },
30425     
30426     onClick : function(e)
30427     {
30428         e.preventDefault();
30429         
30430         if(this.disabled){
30431             return;
30432         }
30433         
30434         if(this.fireEvent('click', this, e) === false){
30435             return;
30436         };
30437         
30438         this.parent().setActiveItem(this);
30439     },
30440     
30441     isActive: function () 
30442     {
30443         return this.active;
30444     },
30445     
30446     setActive : function(state)
30447     {
30448         if(this.active == state){
30449             return;
30450         }
30451         
30452         this.active = state;
30453         
30454         if (state) {
30455             this.el.addClass('active');
30456             return;
30457         }
30458         
30459         this.el.removeClass('active');
30460         
30461         return;
30462     },
30463     
30464     setDisabled : function(state)
30465     {
30466         if(this.disabled == state){
30467             return;
30468         }
30469         
30470         this.disabled = state;
30471         
30472         if (state) {
30473             this.el.addClass('disabled');
30474             return;
30475         }
30476         
30477         this.el.removeClass('disabled');
30478     },
30479     
30480     tooltipEl : function()
30481     {
30482         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30483     }
30484 });
30485  
30486
30487  /*
30488  * - LGPL
30489  *
30490  * FieldLabel
30491  * 
30492  */
30493
30494 /**
30495  * @class Roo.bootstrap.FieldLabel
30496  * @extends Roo.bootstrap.Component
30497  * Bootstrap FieldLabel class
30498  * @cfg {String} html contents of the element
30499  * @cfg {String} tag tag of the element default label
30500  * @cfg {String} cls class of the element
30501  * @cfg {String} target label target 
30502  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30503  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30504  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30505  * @cfg {String} iconTooltip default "This field is required"
30506  * @cfg {String} indicatorpos (left|right) default left
30507  * 
30508  * @constructor
30509  * Create a new FieldLabel
30510  * @param {Object} config The config object
30511  */
30512
30513 Roo.bootstrap.FieldLabel = function(config){
30514     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30515     
30516     this.addEvents({
30517             /**
30518              * @event invalid
30519              * Fires after the field has been marked as invalid.
30520              * @param {Roo.form.FieldLabel} this
30521              * @param {String} msg The validation message
30522              */
30523             invalid : true,
30524             /**
30525              * @event valid
30526              * Fires after the field has been validated with no errors.
30527              * @param {Roo.form.FieldLabel} this
30528              */
30529             valid : true
30530         });
30531 };
30532
30533 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30534     
30535     tag: 'label',
30536     cls: '',
30537     html: '',
30538     target: '',
30539     allowBlank : true,
30540     invalidClass : 'has-warning',
30541     validClass : 'has-success',
30542     iconTooltip : 'This field is required',
30543     indicatorpos : 'left',
30544     
30545     getAutoCreate : function(){
30546         
30547         var cls = "";
30548         if (!this.allowBlank) {
30549             cls  = "visible";
30550         }
30551         
30552         var cfg = {
30553             tag : this.tag,
30554             cls : 'roo-bootstrap-field-label ' + this.cls,
30555             for : this.target,
30556             cn : [
30557                 {
30558                     tag : 'i',
30559                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30560                     tooltip : this.iconTooltip
30561                 },
30562                 {
30563                     tag : 'span',
30564                     html : this.html
30565                 }
30566             ] 
30567         };
30568         
30569         if(this.indicatorpos == 'right'){
30570             var cfg = {
30571                 tag : this.tag,
30572                 cls : 'roo-bootstrap-field-label ' + this.cls,
30573                 for : this.target,
30574                 cn : [
30575                     {
30576                         tag : 'span',
30577                         html : this.html
30578                     },
30579                     {
30580                         tag : 'i',
30581                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30582                         tooltip : this.iconTooltip
30583                     }
30584                 ] 
30585             };
30586         }
30587         
30588         return cfg;
30589     },
30590     
30591     initEvents: function() 
30592     {
30593         Roo.bootstrap.Element.superclass.initEvents.call(this);
30594         
30595         this.indicator = this.indicatorEl();
30596         
30597         if(this.indicator){
30598             this.indicator.removeClass('visible');
30599             this.indicator.addClass('invisible');
30600         }
30601         
30602         Roo.bootstrap.FieldLabel.register(this);
30603     },
30604     
30605     indicatorEl : function()
30606     {
30607         var indicator = this.el.select('i.roo-required-indicator',true).first();
30608         
30609         if(!indicator){
30610             return false;
30611         }
30612         
30613         return indicator;
30614         
30615     },
30616     
30617     /**
30618      * Mark this field as valid
30619      */
30620     markValid : function()
30621     {
30622         if(this.indicator){
30623             this.indicator.removeClass('visible');
30624             this.indicator.addClass('invisible');
30625         }
30626         if (Roo.bootstrap.version == 3) {
30627             this.el.removeClass(this.invalidClass);
30628             this.el.addClass(this.validClass);
30629         } else {
30630             this.el.removeClass('is-invalid');
30631             this.el.addClass('is-valid');
30632         }
30633         
30634         
30635         this.fireEvent('valid', this);
30636     },
30637     
30638     /**
30639      * Mark this field as invalid
30640      * @param {String} msg The validation message
30641      */
30642     markInvalid : function(msg)
30643     {
30644         if(this.indicator){
30645             this.indicator.removeClass('invisible');
30646             this.indicator.addClass('visible');
30647         }
30648           if (Roo.bootstrap.version == 3) {
30649             this.el.removeClass(this.validClass);
30650             this.el.addClass(this.invalidClass);
30651         } else {
30652             this.el.removeClass('is-valid');
30653             this.el.addClass('is-invalid');
30654         }
30655         
30656         
30657         this.fireEvent('invalid', this, msg);
30658     }
30659     
30660    
30661 });
30662
30663 Roo.apply(Roo.bootstrap.FieldLabel, {
30664     
30665     groups: {},
30666     
30667      /**
30668     * register a FieldLabel Group
30669     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30670     */
30671     register : function(label)
30672     {
30673         if(this.groups.hasOwnProperty(label.target)){
30674             return;
30675         }
30676      
30677         this.groups[label.target] = label;
30678         
30679     },
30680     /**
30681     * fetch a FieldLabel Group based on the target
30682     * @param {string} target
30683     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30684     */
30685     get: function(target) {
30686         if (typeof(this.groups[target]) == 'undefined') {
30687             return false;
30688         }
30689         
30690         return this.groups[target] ;
30691     }
30692 });
30693
30694  
30695
30696  /*
30697  * - LGPL
30698  *
30699  * page DateSplitField.
30700  * 
30701  */
30702
30703
30704 /**
30705  * @class Roo.bootstrap.DateSplitField
30706  * @extends Roo.bootstrap.Component
30707  * Bootstrap DateSplitField class
30708  * @cfg {string} fieldLabel - the label associated
30709  * @cfg {Number} labelWidth set the width of label (0-12)
30710  * @cfg {String} labelAlign (top|left)
30711  * @cfg {Boolean} dayAllowBlank (true|false) default false
30712  * @cfg {Boolean} monthAllowBlank (true|false) default false
30713  * @cfg {Boolean} yearAllowBlank (true|false) default false
30714  * @cfg {string} dayPlaceholder 
30715  * @cfg {string} monthPlaceholder
30716  * @cfg {string} yearPlaceholder
30717  * @cfg {string} dayFormat default 'd'
30718  * @cfg {string} monthFormat default 'm'
30719  * @cfg {string} yearFormat default 'Y'
30720  * @cfg {Number} labellg set the width of label (1-12)
30721  * @cfg {Number} labelmd set the width of label (1-12)
30722  * @cfg {Number} labelsm set the width of label (1-12)
30723  * @cfg {Number} labelxs set the width of label (1-12)
30724
30725  *     
30726  * @constructor
30727  * Create a new DateSplitField
30728  * @param {Object} config The config object
30729  */
30730
30731 Roo.bootstrap.DateSplitField = function(config){
30732     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30733     
30734     this.addEvents({
30735         // raw events
30736          /**
30737          * @event years
30738          * getting the data of years
30739          * @param {Roo.bootstrap.DateSplitField} this
30740          * @param {Object} years
30741          */
30742         "years" : true,
30743         /**
30744          * @event days
30745          * getting the data of days
30746          * @param {Roo.bootstrap.DateSplitField} this
30747          * @param {Object} days
30748          */
30749         "days" : true,
30750         /**
30751          * @event invalid
30752          * Fires after the field has been marked as invalid.
30753          * @param {Roo.form.Field} this
30754          * @param {String} msg The validation message
30755          */
30756         invalid : true,
30757        /**
30758          * @event valid
30759          * Fires after the field has been validated with no errors.
30760          * @param {Roo.form.Field} this
30761          */
30762         valid : true
30763     });
30764 };
30765
30766 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30767     
30768     fieldLabel : '',
30769     labelAlign : 'top',
30770     labelWidth : 3,
30771     dayAllowBlank : false,
30772     monthAllowBlank : false,
30773     yearAllowBlank : false,
30774     dayPlaceholder : '',
30775     monthPlaceholder : '',
30776     yearPlaceholder : '',
30777     dayFormat : 'd',
30778     monthFormat : 'm',
30779     yearFormat : 'Y',
30780     isFormField : true,
30781     labellg : 0,
30782     labelmd : 0,
30783     labelsm : 0,
30784     labelxs : 0,
30785     
30786     getAutoCreate : function()
30787     {
30788         var cfg = {
30789             tag : 'div',
30790             cls : 'row roo-date-split-field-group',
30791             cn : [
30792                 {
30793                     tag : 'input',
30794                     type : 'hidden',
30795                     cls : 'form-hidden-field roo-date-split-field-group-value',
30796                     name : this.name
30797                 }
30798             ]
30799         };
30800         
30801         var labelCls = 'col-md-12';
30802         var contentCls = 'col-md-4';
30803         
30804         if(this.fieldLabel){
30805             
30806             var label = {
30807                 tag : 'div',
30808                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30809                 cn : [
30810                     {
30811                         tag : 'label',
30812                         html : this.fieldLabel
30813                     }
30814                 ]
30815             };
30816             
30817             if(this.labelAlign == 'left'){
30818             
30819                 if(this.labelWidth > 12){
30820                     label.style = "width: " + this.labelWidth + 'px';
30821                 }
30822
30823                 if(this.labelWidth < 13 && this.labelmd == 0){
30824                     this.labelmd = this.labelWidth;
30825                 }
30826
30827                 if(this.labellg > 0){
30828                     labelCls = ' col-lg-' + this.labellg;
30829                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30830                 }
30831
30832                 if(this.labelmd > 0){
30833                     labelCls = ' col-md-' + this.labelmd;
30834                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30835                 }
30836
30837                 if(this.labelsm > 0){
30838                     labelCls = ' col-sm-' + this.labelsm;
30839                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30840                 }
30841
30842                 if(this.labelxs > 0){
30843                     labelCls = ' col-xs-' + this.labelxs;
30844                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30845                 }
30846             }
30847             
30848             label.cls += ' ' + labelCls;
30849             
30850             cfg.cn.push(label);
30851         }
30852         
30853         Roo.each(['day', 'month', 'year'], function(t){
30854             cfg.cn.push({
30855                 tag : 'div',
30856                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30857             });
30858         }, this);
30859         
30860         return cfg;
30861     },
30862     
30863     inputEl: function ()
30864     {
30865         return this.el.select('.roo-date-split-field-group-value', true).first();
30866     },
30867     
30868     onRender : function(ct, position) 
30869     {
30870         var _this = this;
30871         
30872         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30873         
30874         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30875         
30876         this.dayField = new Roo.bootstrap.ComboBox({
30877             allowBlank : this.dayAllowBlank,
30878             alwaysQuery : true,
30879             displayField : 'value',
30880             editable : false,
30881             fieldLabel : '',
30882             forceSelection : true,
30883             mode : 'local',
30884             placeholder : this.dayPlaceholder,
30885             selectOnFocus : true,
30886             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30887             triggerAction : 'all',
30888             typeAhead : true,
30889             valueField : 'value',
30890             store : new Roo.data.SimpleStore({
30891                 data : (function() {    
30892                     var days = [];
30893                     _this.fireEvent('days', _this, days);
30894                     return days;
30895                 })(),
30896                 fields : [ 'value' ]
30897             }),
30898             listeners : {
30899                 select : function (_self, record, index)
30900                 {
30901                     _this.setValue(_this.getValue());
30902                 }
30903             }
30904         });
30905
30906         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30907         
30908         this.monthField = new Roo.bootstrap.MonthField({
30909             after : '<i class=\"fa fa-calendar\"></i>',
30910             allowBlank : this.monthAllowBlank,
30911             placeholder : this.monthPlaceholder,
30912             readOnly : true,
30913             listeners : {
30914                 render : function (_self)
30915                 {
30916                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30917                         e.preventDefault();
30918                         _self.focus();
30919                     });
30920                 },
30921                 select : function (_self, oldvalue, newvalue)
30922                 {
30923                     _this.setValue(_this.getValue());
30924                 }
30925             }
30926         });
30927         
30928         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30929         
30930         this.yearField = new Roo.bootstrap.ComboBox({
30931             allowBlank : this.yearAllowBlank,
30932             alwaysQuery : true,
30933             displayField : 'value',
30934             editable : false,
30935             fieldLabel : '',
30936             forceSelection : true,
30937             mode : 'local',
30938             placeholder : this.yearPlaceholder,
30939             selectOnFocus : true,
30940             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30941             triggerAction : 'all',
30942             typeAhead : true,
30943             valueField : 'value',
30944             store : new Roo.data.SimpleStore({
30945                 data : (function() {
30946                     var years = [];
30947                     _this.fireEvent('years', _this, years);
30948                     return years;
30949                 })(),
30950                 fields : [ 'value' ]
30951             }),
30952             listeners : {
30953                 select : function (_self, record, index)
30954                 {
30955                     _this.setValue(_this.getValue());
30956                 }
30957             }
30958         });
30959
30960         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30961     },
30962     
30963     setValue : function(v, format)
30964     {
30965         this.inputEl.dom.value = v;
30966         
30967         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30968         
30969         var d = Date.parseDate(v, f);
30970         
30971         if(!d){
30972             this.validate();
30973             return;
30974         }
30975         
30976         this.setDay(d.format(this.dayFormat));
30977         this.setMonth(d.format(this.monthFormat));
30978         this.setYear(d.format(this.yearFormat));
30979         
30980         this.validate();
30981         
30982         return;
30983     },
30984     
30985     setDay : function(v)
30986     {
30987         this.dayField.setValue(v);
30988         this.inputEl.dom.value = this.getValue();
30989         this.validate();
30990         return;
30991     },
30992     
30993     setMonth : function(v)
30994     {
30995         this.monthField.setValue(v, true);
30996         this.inputEl.dom.value = this.getValue();
30997         this.validate();
30998         return;
30999     },
31000     
31001     setYear : function(v)
31002     {
31003         this.yearField.setValue(v);
31004         this.inputEl.dom.value = this.getValue();
31005         this.validate();
31006         return;
31007     },
31008     
31009     getDay : function()
31010     {
31011         return this.dayField.getValue();
31012     },
31013     
31014     getMonth : function()
31015     {
31016         return this.monthField.getValue();
31017     },
31018     
31019     getYear : function()
31020     {
31021         return this.yearField.getValue();
31022     },
31023     
31024     getValue : function()
31025     {
31026         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31027         
31028         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31029         
31030         return date;
31031     },
31032     
31033     reset : function()
31034     {
31035         this.setDay('');
31036         this.setMonth('');
31037         this.setYear('');
31038         this.inputEl.dom.value = '';
31039         this.validate();
31040         return;
31041     },
31042     
31043     validate : function()
31044     {
31045         var d = this.dayField.validate();
31046         var m = this.monthField.validate();
31047         var y = this.yearField.validate();
31048         
31049         var valid = true;
31050         
31051         if(
31052                 (!this.dayAllowBlank && !d) ||
31053                 (!this.monthAllowBlank && !m) ||
31054                 (!this.yearAllowBlank && !y)
31055         ){
31056             valid = false;
31057         }
31058         
31059         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31060             return valid;
31061         }
31062         
31063         if(valid){
31064             this.markValid();
31065             return valid;
31066         }
31067         
31068         this.markInvalid();
31069         
31070         return valid;
31071     },
31072     
31073     markValid : function()
31074     {
31075         
31076         var label = this.el.select('label', true).first();
31077         var icon = this.el.select('i.fa-star', true).first();
31078
31079         if(label && icon){
31080             icon.remove();
31081         }
31082         
31083         this.fireEvent('valid', this);
31084     },
31085     
31086      /**
31087      * Mark this field as invalid
31088      * @param {String} msg The validation message
31089      */
31090     markInvalid : function(msg)
31091     {
31092         
31093         var label = this.el.select('label', true).first();
31094         var icon = this.el.select('i.fa-star', true).first();
31095
31096         if(label && !icon){
31097             this.el.select('.roo-date-split-field-label', true).createChild({
31098                 tag : 'i',
31099                 cls : 'text-danger fa fa-lg fa-star',
31100                 tooltip : 'This field is required',
31101                 style : 'margin-right:5px;'
31102             }, label, true);
31103         }
31104         
31105         this.fireEvent('invalid', this, msg);
31106     },
31107     
31108     clearInvalid : function()
31109     {
31110         var label = this.el.select('label', true).first();
31111         var icon = this.el.select('i.fa-star', true).first();
31112
31113         if(label && icon){
31114             icon.remove();
31115         }
31116         
31117         this.fireEvent('valid', this);
31118     },
31119     
31120     getName: function()
31121     {
31122         return this.name;
31123     }
31124     
31125 });
31126
31127  /**
31128  *
31129  * This is based on 
31130  * http://masonry.desandro.com
31131  *
31132  * The idea is to render all the bricks based on vertical width...
31133  *
31134  * The original code extends 'outlayer' - we might need to use that....
31135  * 
31136  */
31137
31138
31139 /**
31140  * @class Roo.bootstrap.LayoutMasonry
31141  * @extends Roo.bootstrap.Component
31142  * Bootstrap Layout Masonry class
31143  * 
31144  * @constructor
31145  * Create a new Element
31146  * @param {Object} config The config object
31147  */
31148
31149 Roo.bootstrap.LayoutMasonry = function(config){
31150     
31151     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31152     
31153     this.bricks = [];
31154     
31155     Roo.bootstrap.LayoutMasonry.register(this);
31156     
31157     this.addEvents({
31158         // raw events
31159         /**
31160          * @event layout
31161          * Fire after layout the items
31162          * @param {Roo.bootstrap.LayoutMasonry} this
31163          * @param {Roo.EventObject} e
31164          */
31165         "layout" : true
31166     });
31167     
31168 };
31169
31170 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31171     
31172     /**
31173      * @cfg {Boolean} isLayoutInstant = no animation?
31174      */   
31175     isLayoutInstant : false, // needed?
31176    
31177     /**
31178      * @cfg {Number} boxWidth  width of the columns
31179      */   
31180     boxWidth : 450,
31181     
31182       /**
31183      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31184      */   
31185     boxHeight : 0,
31186     
31187     /**
31188      * @cfg {Number} padWidth padding below box..
31189      */   
31190     padWidth : 10, 
31191     
31192     /**
31193      * @cfg {Number} gutter gutter width..
31194      */   
31195     gutter : 10,
31196     
31197      /**
31198      * @cfg {Number} maxCols maximum number of columns
31199      */   
31200     
31201     maxCols: 0,
31202     
31203     /**
31204      * @cfg {Boolean} isAutoInitial defalut true
31205      */   
31206     isAutoInitial : true, 
31207     
31208     containerWidth: 0,
31209     
31210     /**
31211      * @cfg {Boolean} isHorizontal defalut false
31212      */   
31213     isHorizontal : false, 
31214
31215     currentSize : null,
31216     
31217     tag: 'div',
31218     
31219     cls: '',
31220     
31221     bricks: null, //CompositeElement
31222     
31223     cols : 1,
31224     
31225     _isLayoutInited : false,
31226     
31227 //    isAlternative : false, // only use for vertical layout...
31228     
31229     /**
31230      * @cfg {Number} alternativePadWidth padding below box..
31231      */   
31232     alternativePadWidth : 50,
31233     
31234     selectedBrick : [],
31235     
31236     getAutoCreate : function(){
31237         
31238         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31239         
31240         var cfg = {
31241             tag: this.tag,
31242             cls: 'blog-masonary-wrapper ' + this.cls,
31243             cn : {
31244                 cls : 'mas-boxes masonary'
31245             }
31246         };
31247         
31248         return cfg;
31249     },
31250     
31251     getChildContainer: function( )
31252     {
31253         if (this.boxesEl) {
31254             return this.boxesEl;
31255         }
31256         
31257         this.boxesEl = this.el.select('.mas-boxes').first();
31258         
31259         return this.boxesEl;
31260     },
31261     
31262     
31263     initEvents : function()
31264     {
31265         var _this = this;
31266         
31267         if(this.isAutoInitial){
31268             Roo.log('hook children rendered');
31269             this.on('childrenrendered', function() {
31270                 Roo.log('children rendered');
31271                 _this.initial();
31272             } ,this);
31273         }
31274     },
31275     
31276     initial : function()
31277     {
31278         this.selectedBrick = [];
31279         
31280         this.currentSize = this.el.getBox(true);
31281         
31282         Roo.EventManager.onWindowResize(this.resize, this); 
31283
31284         if(!this.isAutoInitial){
31285             this.layout();
31286             return;
31287         }
31288         
31289         this.layout();
31290         
31291         return;
31292         //this.layout.defer(500,this);
31293         
31294     },
31295     
31296     resize : function()
31297     {
31298         var cs = this.el.getBox(true);
31299         
31300         if (
31301                 this.currentSize.width == cs.width && 
31302                 this.currentSize.x == cs.x && 
31303                 this.currentSize.height == cs.height && 
31304                 this.currentSize.y == cs.y 
31305         ) {
31306             Roo.log("no change in with or X or Y");
31307             return;
31308         }
31309         
31310         this.currentSize = cs;
31311         
31312         this.layout();
31313         
31314     },
31315     
31316     layout : function()
31317     {   
31318         this._resetLayout();
31319         
31320         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31321         
31322         this.layoutItems( isInstant );
31323       
31324         this._isLayoutInited = true;
31325         
31326         this.fireEvent('layout', this);
31327         
31328     },
31329     
31330     _resetLayout : function()
31331     {
31332         if(this.isHorizontal){
31333             this.horizontalMeasureColumns();
31334             return;
31335         }
31336         
31337         this.verticalMeasureColumns();
31338         
31339     },
31340     
31341     verticalMeasureColumns : function()
31342     {
31343         this.getContainerWidth();
31344         
31345 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31346 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31347 //            return;
31348 //        }
31349         
31350         var boxWidth = this.boxWidth + this.padWidth;
31351         
31352         if(this.containerWidth < this.boxWidth){
31353             boxWidth = this.containerWidth
31354         }
31355         
31356         var containerWidth = this.containerWidth;
31357         
31358         var cols = Math.floor(containerWidth / boxWidth);
31359         
31360         this.cols = Math.max( cols, 1 );
31361         
31362         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31363         
31364         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31365         
31366         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31367         
31368         this.colWidth = boxWidth + avail - this.padWidth;
31369         
31370         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31371         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31372     },
31373     
31374     horizontalMeasureColumns : function()
31375     {
31376         this.getContainerWidth();
31377         
31378         var boxWidth = this.boxWidth;
31379         
31380         if(this.containerWidth < boxWidth){
31381             boxWidth = this.containerWidth;
31382         }
31383         
31384         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31385         
31386         this.el.setHeight(boxWidth);
31387         
31388     },
31389     
31390     getContainerWidth : function()
31391     {
31392         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31393     },
31394     
31395     layoutItems : function( isInstant )
31396     {
31397         Roo.log(this.bricks);
31398         
31399         var items = Roo.apply([], this.bricks);
31400         
31401         if(this.isHorizontal){
31402             this._horizontalLayoutItems( items , isInstant );
31403             return;
31404         }
31405         
31406 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31407 //            this._verticalAlternativeLayoutItems( items , isInstant );
31408 //            return;
31409 //        }
31410         
31411         this._verticalLayoutItems( items , isInstant );
31412         
31413     },
31414     
31415     _verticalLayoutItems : function ( items , isInstant)
31416     {
31417         if ( !items || !items.length ) {
31418             return;
31419         }
31420         
31421         var standard = [
31422             ['xs', 'xs', 'xs', 'tall'],
31423             ['xs', 'xs', 'tall'],
31424             ['xs', 'xs', 'sm'],
31425             ['xs', 'xs', 'xs'],
31426             ['xs', 'tall'],
31427             ['xs', 'sm'],
31428             ['xs', 'xs'],
31429             ['xs'],
31430             
31431             ['sm', 'xs', 'xs'],
31432             ['sm', 'xs'],
31433             ['sm'],
31434             
31435             ['tall', 'xs', 'xs', 'xs'],
31436             ['tall', 'xs', 'xs'],
31437             ['tall', 'xs'],
31438             ['tall']
31439             
31440         ];
31441         
31442         var queue = [];
31443         
31444         var boxes = [];
31445         
31446         var box = [];
31447         
31448         Roo.each(items, function(item, k){
31449             
31450             switch (item.size) {
31451                 // these layouts take up a full box,
31452                 case 'md' :
31453                 case 'md-left' :
31454                 case 'md-right' :
31455                 case 'wide' :
31456                     
31457                     if(box.length){
31458                         boxes.push(box);
31459                         box = [];
31460                     }
31461                     
31462                     boxes.push([item]);
31463                     
31464                     break;
31465                     
31466                 case 'xs' :
31467                 case 'sm' :
31468                 case 'tall' :
31469                     
31470                     box.push(item);
31471                     
31472                     break;
31473                 default :
31474                     break;
31475                     
31476             }
31477             
31478         }, this);
31479         
31480         if(box.length){
31481             boxes.push(box);
31482             box = [];
31483         }
31484         
31485         var filterPattern = function(box, length)
31486         {
31487             if(!box.length){
31488                 return;
31489             }
31490             
31491             var match = false;
31492             
31493             var pattern = box.slice(0, length);
31494             
31495             var format = [];
31496             
31497             Roo.each(pattern, function(i){
31498                 format.push(i.size);
31499             }, this);
31500             
31501             Roo.each(standard, function(s){
31502                 
31503                 if(String(s) != String(format)){
31504                     return;
31505                 }
31506                 
31507                 match = true;
31508                 return false;
31509                 
31510             }, this);
31511             
31512             if(!match && length == 1){
31513                 return;
31514             }
31515             
31516             if(!match){
31517                 filterPattern(box, length - 1);
31518                 return;
31519             }
31520                 
31521             queue.push(pattern);
31522
31523             box = box.slice(length, box.length);
31524
31525             filterPattern(box, 4);
31526
31527             return;
31528             
31529         }
31530         
31531         Roo.each(boxes, function(box, k){
31532             
31533             if(!box.length){
31534                 return;
31535             }
31536             
31537             if(box.length == 1){
31538                 queue.push(box);
31539                 return;
31540             }
31541             
31542             filterPattern(box, 4);
31543             
31544         }, this);
31545         
31546         this._processVerticalLayoutQueue( queue, isInstant );
31547         
31548     },
31549     
31550 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31551 //    {
31552 //        if ( !items || !items.length ) {
31553 //            return;
31554 //        }
31555 //
31556 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31557 //        
31558 //    },
31559     
31560     _horizontalLayoutItems : function ( items , isInstant)
31561     {
31562         if ( !items || !items.length || items.length < 3) {
31563             return;
31564         }
31565         
31566         items.reverse();
31567         
31568         var eItems = items.slice(0, 3);
31569         
31570         items = items.slice(3, items.length);
31571         
31572         var standard = [
31573             ['xs', 'xs', 'xs', 'wide'],
31574             ['xs', 'xs', 'wide'],
31575             ['xs', 'xs', 'sm'],
31576             ['xs', 'xs', 'xs'],
31577             ['xs', 'wide'],
31578             ['xs', 'sm'],
31579             ['xs', 'xs'],
31580             ['xs'],
31581             
31582             ['sm', 'xs', 'xs'],
31583             ['sm', 'xs'],
31584             ['sm'],
31585             
31586             ['wide', 'xs', 'xs', 'xs'],
31587             ['wide', 'xs', 'xs'],
31588             ['wide', 'xs'],
31589             ['wide'],
31590             
31591             ['wide-thin']
31592         ];
31593         
31594         var queue = [];
31595         
31596         var boxes = [];
31597         
31598         var box = [];
31599         
31600         Roo.each(items, function(item, k){
31601             
31602             switch (item.size) {
31603                 case 'md' :
31604                 case 'md-left' :
31605                 case 'md-right' :
31606                 case 'tall' :
31607                     
31608                     if(box.length){
31609                         boxes.push(box);
31610                         box = [];
31611                     }
31612                     
31613                     boxes.push([item]);
31614                     
31615                     break;
31616                     
31617                 case 'xs' :
31618                 case 'sm' :
31619                 case 'wide' :
31620                 case 'wide-thin' :
31621                     
31622                     box.push(item);
31623                     
31624                     break;
31625                 default :
31626                     break;
31627                     
31628             }
31629             
31630         }, this);
31631         
31632         if(box.length){
31633             boxes.push(box);
31634             box = [];
31635         }
31636         
31637         var filterPattern = function(box, length)
31638         {
31639             if(!box.length){
31640                 return;
31641             }
31642             
31643             var match = false;
31644             
31645             var pattern = box.slice(0, length);
31646             
31647             var format = [];
31648             
31649             Roo.each(pattern, function(i){
31650                 format.push(i.size);
31651             }, this);
31652             
31653             Roo.each(standard, function(s){
31654                 
31655                 if(String(s) != String(format)){
31656                     return;
31657                 }
31658                 
31659                 match = true;
31660                 return false;
31661                 
31662             }, this);
31663             
31664             if(!match && length == 1){
31665                 return;
31666             }
31667             
31668             if(!match){
31669                 filterPattern(box, length - 1);
31670                 return;
31671             }
31672                 
31673             queue.push(pattern);
31674
31675             box = box.slice(length, box.length);
31676
31677             filterPattern(box, 4);
31678
31679             return;
31680             
31681         }
31682         
31683         Roo.each(boxes, function(box, k){
31684             
31685             if(!box.length){
31686                 return;
31687             }
31688             
31689             if(box.length == 1){
31690                 queue.push(box);
31691                 return;
31692             }
31693             
31694             filterPattern(box, 4);
31695             
31696         }, this);
31697         
31698         
31699         var prune = [];
31700         
31701         var pos = this.el.getBox(true);
31702         
31703         var minX = pos.x;
31704         
31705         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31706         
31707         var hit_end = false;
31708         
31709         Roo.each(queue, function(box){
31710             
31711             if(hit_end){
31712                 
31713                 Roo.each(box, function(b){
31714                 
31715                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31716                     b.el.hide();
31717
31718                 }, this);
31719
31720                 return;
31721             }
31722             
31723             var mx = 0;
31724             
31725             Roo.each(box, function(b){
31726                 
31727                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31728                 b.el.show();
31729
31730                 mx = Math.max(mx, b.x);
31731                 
31732             }, this);
31733             
31734             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31735             
31736             if(maxX < minX){
31737                 
31738                 Roo.each(box, function(b){
31739                 
31740                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31741                     b.el.hide();
31742                     
31743                 }, this);
31744                 
31745                 hit_end = true;
31746                 
31747                 return;
31748             }
31749             
31750             prune.push(box);
31751             
31752         }, this);
31753         
31754         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31755     },
31756     
31757     /** Sets position of item in DOM
31758     * @param {Element} item
31759     * @param {Number} x - horizontal position
31760     * @param {Number} y - vertical position
31761     * @param {Boolean} isInstant - disables transitions
31762     */
31763     _processVerticalLayoutQueue : function( queue, isInstant )
31764     {
31765         var pos = this.el.getBox(true);
31766         var x = pos.x;
31767         var y = pos.y;
31768         var maxY = [];
31769         
31770         for (var i = 0; i < this.cols; i++){
31771             maxY[i] = pos.y;
31772         }
31773         
31774         Roo.each(queue, function(box, k){
31775             
31776             var col = k % this.cols;
31777             
31778             Roo.each(box, function(b,kk){
31779                 
31780                 b.el.position('absolute');
31781                 
31782                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31783                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31784                 
31785                 if(b.size == 'md-left' || b.size == 'md-right'){
31786                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31787                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31788                 }
31789                 
31790                 b.el.setWidth(width);
31791                 b.el.setHeight(height);
31792                 // iframe?
31793                 b.el.select('iframe',true).setSize(width,height);
31794                 
31795             }, this);
31796             
31797             for (var i = 0; i < this.cols; i++){
31798                 
31799                 if(maxY[i] < maxY[col]){
31800                     col = i;
31801                     continue;
31802                 }
31803                 
31804                 col = Math.min(col, i);
31805                 
31806             }
31807             
31808             x = pos.x + col * (this.colWidth + this.padWidth);
31809             
31810             y = maxY[col];
31811             
31812             var positions = [];
31813             
31814             switch (box.length){
31815                 case 1 :
31816                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31817                     break;
31818                 case 2 :
31819                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31820                     break;
31821                 case 3 :
31822                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31823                     break;
31824                 case 4 :
31825                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31826                     break;
31827                 default :
31828                     break;
31829             }
31830             
31831             Roo.each(box, function(b,kk){
31832                 
31833                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31834                 
31835                 var sz = b.el.getSize();
31836                 
31837                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31838                 
31839             }, this);
31840             
31841         }, this);
31842         
31843         var mY = 0;
31844         
31845         for (var i = 0; i < this.cols; i++){
31846             mY = Math.max(mY, maxY[i]);
31847         }
31848         
31849         this.el.setHeight(mY - pos.y);
31850         
31851     },
31852     
31853 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31854 //    {
31855 //        var pos = this.el.getBox(true);
31856 //        var x = pos.x;
31857 //        var y = pos.y;
31858 //        var maxX = pos.right;
31859 //        
31860 //        var maxHeight = 0;
31861 //        
31862 //        Roo.each(items, function(item, k){
31863 //            
31864 //            var c = k % 2;
31865 //            
31866 //            item.el.position('absolute');
31867 //                
31868 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31869 //
31870 //            item.el.setWidth(width);
31871 //
31872 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31873 //
31874 //            item.el.setHeight(height);
31875 //            
31876 //            if(c == 0){
31877 //                item.el.setXY([x, y], isInstant ? false : true);
31878 //            } else {
31879 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31880 //            }
31881 //            
31882 //            y = y + height + this.alternativePadWidth;
31883 //            
31884 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31885 //            
31886 //        }, this);
31887 //        
31888 //        this.el.setHeight(maxHeight);
31889 //        
31890 //    },
31891     
31892     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31893     {
31894         var pos = this.el.getBox(true);
31895         
31896         var minX = pos.x;
31897         var minY = pos.y;
31898         
31899         var maxX = pos.right;
31900         
31901         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31902         
31903         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31904         
31905         Roo.each(queue, function(box, k){
31906             
31907             Roo.each(box, function(b, kk){
31908                 
31909                 b.el.position('absolute');
31910                 
31911                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31912                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31913                 
31914                 if(b.size == 'md-left' || b.size == 'md-right'){
31915                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31916                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31917                 }
31918                 
31919                 b.el.setWidth(width);
31920                 b.el.setHeight(height);
31921                 
31922             }, this);
31923             
31924             if(!box.length){
31925                 return;
31926             }
31927             
31928             var positions = [];
31929             
31930             switch (box.length){
31931                 case 1 :
31932                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31933                     break;
31934                 case 2 :
31935                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31936                     break;
31937                 case 3 :
31938                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31939                     break;
31940                 case 4 :
31941                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31942                     break;
31943                 default :
31944                     break;
31945             }
31946             
31947             Roo.each(box, function(b,kk){
31948                 
31949                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31950                 
31951                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31952                 
31953             }, this);
31954             
31955         }, this);
31956         
31957     },
31958     
31959     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31960     {
31961         Roo.each(eItems, function(b,k){
31962             
31963             b.size = (k == 0) ? 'sm' : 'xs';
31964             b.x = (k == 0) ? 2 : 1;
31965             b.y = (k == 0) ? 2 : 1;
31966             
31967             b.el.position('absolute');
31968             
31969             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31970                 
31971             b.el.setWidth(width);
31972             
31973             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31974             
31975             b.el.setHeight(height);
31976             
31977         }, this);
31978
31979         var positions = [];
31980         
31981         positions.push({
31982             x : maxX - this.unitWidth * 2 - this.gutter,
31983             y : minY
31984         });
31985         
31986         positions.push({
31987             x : maxX - this.unitWidth,
31988             y : minY + (this.unitWidth + this.gutter) * 2
31989         });
31990         
31991         positions.push({
31992             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31993             y : minY
31994         });
31995         
31996         Roo.each(eItems, function(b,k){
31997             
31998             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31999
32000         }, this);
32001         
32002     },
32003     
32004     getVerticalOneBoxColPositions : function(x, y, box)
32005     {
32006         var pos = [];
32007         
32008         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32009         
32010         if(box[0].size == 'md-left'){
32011             rand = 0;
32012         }
32013         
32014         if(box[0].size == 'md-right'){
32015             rand = 1;
32016         }
32017         
32018         pos.push({
32019             x : x + (this.unitWidth + this.gutter) * rand,
32020             y : y
32021         });
32022         
32023         return pos;
32024     },
32025     
32026     getVerticalTwoBoxColPositions : function(x, y, box)
32027     {
32028         var pos = [];
32029         
32030         if(box[0].size == 'xs'){
32031             
32032             pos.push({
32033                 x : x,
32034                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32035             });
32036
32037             pos.push({
32038                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32039                 y : y
32040             });
32041             
32042             return pos;
32043             
32044         }
32045         
32046         pos.push({
32047             x : x,
32048             y : y
32049         });
32050
32051         pos.push({
32052             x : x + (this.unitWidth + this.gutter) * 2,
32053             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32054         });
32055         
32056         return pos;
32057         
32058     },
32059     
32060     getVerticalThreeBoxColPositions : function(x, y, box)
32061     {
32062         var pos = [];
32063         
32064         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32065             
32066             pos.push({
32067                 x : x,
32068                 y : y
32069             });
32070
32071             pos.push({
32072                 x : x + (this.unitWidth + this.gutter) * 1,
32073                 y : y
32074             });
32075             
32076             pos.push({
32077                 x : x + (this.unitWidth + this.gutter) * 2,
32078                 y : y
32079             });
32080             
32081             return pos;
32082             
32083         }
32084         
32085         if(box[0].size == 'xs' && box[1].size == 'xs'){
32086             
32087             pos.push({
32088                 x : x,
32089                 y : y
32090             });
32091
32092             pos.push({
32093                 x : x,
32094                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32095             });
32096             
32097             pos.push({
32098                 x : x + (this.unitWidth + this.gutter) * 1,
32099                 y : y
32100             });
32101             
32102             return pos;
32103             
32104         }
32105         
32106         pos.push({
32107             x : x,
32108             y : y
32109         });
32110
32111         pos.push({
32112             x : x + (this.unitWidth + this.gutter) * 2,
32113             y : y
32114         });
32115
32116         pos.push({
32117             x : x + (this.unitWidth + this.gutter) * 2,
32118             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32119         });
32120             
32121         return pos;
32122         
32123     },
32124     
32125     getVerticalFourBoxColPositions : function(x, y, box)
32126     {
32127         var pos = [];
32128         
32129         if(box[0].size == 'xs'){
32130             
32131             pos.push({
32132                 x : x,
32133                 y : y
32134             });
32135
32136             pos.push({
32137                 x : x,
32138                 y : y + (this.unitHeight + this.gutter) * 1
32139             });
32140             
32141             pos.push({
32142                 x : x,
32143                 y : y + (this.unitHeight + this.gutter) * 2
32144             });
32145             
32146             pos.push({
32147                 x : x + (this.unitWidth + this.gutter) * 1,
32148                 y : y
32149             });
32150             
32151             return pos;
32152             
32153         }
32154         
32155         pos.push({
32156             x : x,
32157             y : y
32158         });
32159
32160         pos.push({
32161             x : x + (this.unitWidth + this.gutter) * 2,
32162             y : y
32163         });
32164
32165         pos.push({
32166             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32167             y : y + (this.unitHeight + this.gutter) * 1
32168         });
32169
32170         pos.push({
32171             x : x + (this.unitWidth + this.gutter) * 2,
32172             y : y + (this.unitWidth + this.gutter) * 2
32173         });
32174
32175         return pos;
32176         
32177     },
32178     
32179     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32180     {
32181         var pos = [];
32182         
32183         if(box[0].size == 'md-left'){
32184             pos.push({
32185                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32186                 y : minY
32187             });
32188             
32189             return pos;
32190         }
32191         
32192         if(box[0].size == 'md-right'){
32193             pos.push({
32194                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32195                 y : minY + (this.unitWidth + this.gutter) * 1
32196             });
32197             
32198             return pos;
32199         }
32200         
32201         var rand = Math.floor(Math.random() * (4 - box[0].y));
32202         
32203         pos.push({
32204             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32205             y : minY + (this.unitWidth + this.gutter) * rand
32206         });
32207         
32208         return pos;
32209         
32210     },
32211     
32212     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32213     {
32214         var pos = [];
32215         
32216         if(box[0].size == 'xs'){
32217             
32218             pos.push({
32219                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32220                 y : minY
32221             });
32222
32223             pos.push({
32224                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32225                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32226             });
32227             
32228             return pos;
32229             
32230         }
32231         
32232         pos.push({
32233             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32234             y : minY
32235         });
32236
32237         pos.push({
32238             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32239             y : minY + (this.unitWidth + this.gutter) * 2
32240         });
32241         
32242         return pos;
32243         
32244     },
32245     
32246     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32247     {
32248         var pos = [];
32249         
32250         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32251             
32252             pos.push({
32253                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32254                 y : minY
32255             });
32256
32257             pos.push({
32258                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32259                 y : minY + (this.unitWidth + this.gutter) * 1
32260             });
32261             
32262             pos.push({
32263                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32264                 y : minY + (this.unitWidth + this.gutter) * 2
32265             });
32266             
32267             return pos;
32268             
32269         }
32270         
32271         if(box[0].size == 'xs' && box[1].size == 'xs'){
32272             
32273             pos.push({
32274                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32275                 y : minY
32276             });
32277
32278             pos.push({
32279                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32280                 y : minY
32281             });
32282             
32283             pos.push({
32284                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32285                 y : minY + (this.unitWidth + this.gutter) * 1
32286             });
32287             
32288             return pos;
32289             
32290         }
32291         
32292         pos.push({
32293             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32294             y : minY
32295         });
32296
32297         pos.push({
32298             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32299             y : minY + (this.unitWidth + this.gutter) * 2
32300         });
32301
32302         pos.push({
32303             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32304             y : minY + (this.unitWidth + this.gutter) * 2
32305         });
32306             
32307         return pos;
32308         
32309     },
32310     
32311     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32312     {
32313         var pos = [];
32314         
32315         if(box[0].size == 'xs'){
32316             
32317             pos.push({
32318                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32319                 y : minY
32320             });
32321
32322             pos.push({
32323                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32324                 y : minY
32325             });
32326             
32327             pos.push({
32328                 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),
32329                 y : minY
32330             });
32331             
32332             pos.push({
32333                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32334                 y : minY + (this.unitWidth + this.gutter) * 1
32335             });
32336             
32337             return pos;
32338             
32339         }
32340         
32341         pos.push({
32342             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32343             y : minY
32344         });
32345         
32346         pos.push({
32347             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32348             y : minY + (this.unitWidth + this.gutter) * 2
32349         });
32350         
32351         pos.push({
32352             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32353             y : minY + (this.unitWidth + this.gutter) * 2
32354         });
32355         
32356         pos.push({
32357             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),
32358             y : minY + (this.unitWidth + this.gutter) * 2
32359         });
32360
32361         return pos;
32362         
32363     },
32364     
32365     /**
32366     * remove a Masonry Brick
32367     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32368     */
32369     removeBrick : function(brick_id)
32370     {
32371         if (!brick_id) {
32372             return;
32373         }
32374         
32375         for (var i = 0; i<this.bricks.length; i++) {
32376             if (this.bricks[i].id == brick_id) {
32377                 this.bricks.splice(i,1);
32378                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32379                 this.initial();
32380             }
32381         }
32382     },
32383     
32384     /**
32385     * adds a Masonry Brick
32386     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32387     */
32388     addBrick : function(cfg)
32389     {
32390         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32391         //this.register(cn);
32392         cn.parentId = this.id;
32393         cn.render(this.el);
32394         return cn;
32395     },
32396     
32397     /**
32398     * register a Masonry Brick
32399     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32400     */
32401     
32402     register : function(brick)
32403     {
32404         this.bricks.push(brick);
32405         brick.masonryId = this.id;
32406     },
32407     
32408     /**
32409     * clear all the Masonry Brick
32410     */
32411     clearAll : function()
32412     {
32413         this.bricks = [];
32414         //this.getChildContainer().dom.innerHTML = "";
32415         this.el.dom.innerHTML = '';
32416     },
32417     
32418     getSelected : function()
32419     {
32420         if (!this.selectedBrick) {
32421             return false;
32422         }
32423         
32424         return this.selectedBrick;
32425     }
32426 });
32427
32428 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32429     
32430     groups: {},
32431      /**
32432     * register a Masonry Layout
32433     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32434     */
32435     
32436     register : function(layout)
32437     {
32438         this.groups[layout.id] = layout;
32439     },
32440     /**
32441     * fetch a  Masonry Layout based on the masonry layout ID
32442     * @param {string} the masonry layout to add
32443     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32444     */
32445     
32446     get: function(layout_id) {
32447         if (typeof(this.groups[layout_id]) == 'undefined') {
32448             return false;
32449         }
32450         return this.groups[layout_id] ;
32451     }
32452     
32453     
32454     
32455 });
32456
32457  
32458
32459  /**
32460  *
32461  * This is based on 
32462  * http://masonry.desandro.com
32463  *
32464  * The idea is to render all the bricks based on vertical width...
32465  *
32466  * The original code extends 'outlayer' - we might need to use that....
32467  * 
32468  */
32469
32470
32471 /**
32472  * @class Roo.bootstrap.LayoutMasonryAuto
32473  * @extends Roo.bootstrap.Component
32474  * Bootstrap Layout Masonry class
32475  * 
32476  * @constructor
32477  * Create a new Element
32478  * @param {Object} config The config object
32479  */
32480
32481 Roo.bootstrap.LayoutMasonryAuto = function(config){
32482     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32483 };
32484
32485 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32486     
32487       /**
32488      * @cfg {Boolean} isFitWidth  - resize the width..
32489      */   
32490     isFitWidth : false,  // options..
32491     /**
32492      * @cfg {Boolean} isOriginLeft = left align?
32493      */   
32494     isOriginLeft : true,
32495     /**
32496      * @cfg {Boolean} isOriginTop = top align?
32497      */   
32498     isOriginTop : false,
32499     /**
32500      * @cfg {Boolean} isLayoutInstant = no animation?
32501      */   
32502     isLayoutInstant : false, // needed?
32503     /**
32504      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32505      */   
32506     isResizingContainer : true,
32507     /**
32508      * @cfg {Number} columnWidth  width of the columns 
32509      */   
32510     
32511     columnWidth : 0,
32512     
32513     /**
32514      * @cfg {Number} maxCols maximum number of columns
32515      */   
32516     
32517     maxCols: 0,
32518     /**
32519      * @cfg {Number} padHeight padding below box..
32520      */   
32521     
32522     padHeight : 10, 
32523     
32524     /**
32525      * @cfg {Boolean} isAutoInitial defalut true
32526      */   
32527     
32528     isAutoInitial : true, 
32529     
32530     // private?
32531     gutter : 0,
32532     
32533     containerWidth: 0,
32534     initialColumnWidth : 0,
32535     currentSize : null,
32536     
32537     colYs : null, // array.
32538     maxY : 0,
32539     padWidth: 10,
32540     
32541     
32542     tag: 'div',
32543     cls: '',
32544     bricks: null, //CompositeElement
32545     cols : 0, // array?
32546     // element : null, // wrapped now this.el
32547     _isLayoutInited : null, 
32548     
32549     
32550     getAutoCreate : function(){
32551         
32552         var cfg = {
32553             tag: this.tag,
32554             cls: 'blog-masonary-wrapper ' + this.cls,
32555             cn : {
32556                 cls : 'mas-boxes masonary'
32557             }
32558         };
32559         
32560         return cfg;
32561     },
32562     
32563     getChildContainer: function( )
32564     {
32565         if (this.boxesEl) {
32566             return this.boxesEl;
32567         }
32568         
32569         this.boxesEl = this.el.select('.mas-boxes').first();
32570         
32571         return this.boxesEl;
32572     },
32573     
32574     
32575     initEvents : function()
32576     {
32577         var _this = this;
32578         
32579         if(this.isAutoInitial){
32580             Roo.log('hook children rendered');
32581             this.on('childrenrendered', function() {
32582                 Roo.log('children rendered');
32583                 _this.initial();
32584             } ,this);
32585         }
32586         
32587     },
32588     
32589     initial : function()
32590     {
32591         this.reloadItems();
32592
32593         this.currentSize = this.el.getBox(true);
32594
32595         /// was window resize... - let's see if this works..
32596         Roo.EventManager.onWindowResize(this.resize, this); 
32597
32598         if(!this.isAutoInitial){
32599             this.layout();
32600             return;
32601         }
32602         
32603         this.layout.defer(500,this);
32604     },
32605     
32606     reloadItems: function()
32607     {
32608         this.bricks = this.el.select('.masonry-brick', true);
32609         
32610         this.bricks.each(function(b) {
32611             //Roo.log(b.getSize());
32612             if (!b.attr('originalwidth')) {
32613                 b.attr('originalwidth',  b.getSize().width);
32614             }
32615             
32616         });
32617         
32618         Roo.log(this.bricks.elements.length);
32619     },
32620     
32621     resize : function()
32622     {
32623         Roo.log('resize');
32624         var cs = this.el.getBox(true);
32625         
32626         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32627             Roo.log("no change in with or X");
32628             return;
32629         }
32630         this.currentSize = cs;
32631         this.layout();
32632     },
32633     
32634     layout : function()
32635     {
32636          Roo.log('layout');
32637         this._resetLayout();
32638         //this._manageStamps();
32639       
32640         // don't animate first layout
32641         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32642         this.layoutItems( isInstant );
32643       
32644         // flag for initalized
32645         this._isLayoutInited = true;
32646     },
32647     
32648     layoutItems : function( isInstant )
32649     {
32650         //var items = this._getItemsForLayout( this.items );
32651         // original code supports filtering layout items.. we just ignore it..
32652         
32653         this._layoutItems( this.bricks , isInstant );
32654       
32655         this._postLayout();
32656     },
32657     _layoutItems : function ( items , isInstant)
32658     {
32659        //this.fireEvent( 'layout', this, items );
32660     
32661
32662         if ( !items || !items.elements.length ) {
32663           // no items, emit event with empty array
32664             return;
32665         }
32666
32667         var queue = [];
32668         items.each(function(item) {
32669             Roo.log("layout item");
32670             Roo.log(item);
32671             // get x/y object from method
32672             var position = this._getItemLayoutPosition( item );
32673             // enqueue
32674             position.item = item;
32675             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32676             queue.push( position );
32677         }, this);
32678       
32679         this._processLayoutQueue( queue );
32680     },
32681     /** Sets position of item in DOM
32682     * @param {Element} item
32683     * @param {Number} x - horizontal position
32684     * @param {Number} y - vertical position
32685     * @param {Boolean} isInstant - disables transitions
32686     */
32687     _processLayoutQueue : function( queue )
32688     {
32689         for ( var i=0, len = queue.length; i < len; i++ ) {
32690             var obj = queue[i];
32691             obj.item.position('absolute');
32692             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32693         }
32694     },
32695       
32696     
32697     /**
32698     * Any logic you want to do after each layout,
32699     * i.e. size the container
32700     */
32701     _postLayout : function()
32702     {
32703         this.resizeContainer();
32704     },
32705     
32706     resizeContainer : function()
32707     {
32708         if ( !this.isResizingContainer ) {
32709             return;
32710         }
32711         var size = this._getContainerSize();
32712         if ( size ) {
32713             this.el.setSize(size.width,size.height);
32714             this.boxesEl.setSize(size.width,size.height);
32715         }
32716     },
32717     
32718     
32719     
32720     _resetLayout : function()
32721     {
32722         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32723         this.colWidth = this.el.getWidth();
32724         //this.gutter = this.el.getWidth(); 
32725         
32726         this.measureColumns();
32727
32728         // reset column Y
32729         var i = this.cols;
32730         this.colYs = [];
32731         while (i--) {
32732             this.colYs.push( 0 );
32733         }
32734     
32735         this.maxY = 0;
32736     },
32737
32738     measureColumns : function()
32739     {
32740         this.getContainerWidth();
32741       // if columnWidth is 0, default to outerWidth of first item
32742         if ( !this.columnWidth ) {
32743             var firstItem = this.bricks.first();
32744             Roo.log(firstItem);
32745             this.columnWidth  = this.containerWidth;
32746             if (firstItem && firstItem.attr('originalwidth') ) {
32747                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32748             }
32749             // columnWidth fall back to item of first element
32750             Roo.log("set column width?");
32751                         this.initialColumnWidth = this.columnWidth  ;
32752
32753             // if first elem has no width, default to size of container
32754             
32755         }
32756         
32757         
32758         if (this.initialColumnWidth) {
32759             this.columnWidth = this.initialColumnWidth;
32760         }
32761         
32762         
32763             
32764         // column width is fixed at the top - however if container width get's smaller we should
32765         // reduce it...
32766         
32767         // this bit calcs how man columns..
32768             
32769         var columnWidth = this.columnWidth += this.gutter;
32770       
32771         // calculate columns
32772         var containerWidth = this.containerWidth + this.gutter;
32773         
32774         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32775         // fix rounding errors, typically with gutters
32776         var excess = columnWidth - containerWidth % columnWidth;
32777         
32778         
32779         // if overshoot is less than a pixel, round up, otherwise floor it
32780         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32781         cols = Math[ mathMethod ]( cols );
32782         this.cols = Math.max( cols, 1 );
32783         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32784         
32785          // padding positioning..
32786         var totalColWidth = this.cols * this.columnWidth;
32787         var padavail = this.containerWidth - totalColWidth;
32788         // so for 2 columns - we need 3 'pads'
32789         
32790         var padNeeded = (1+this.cols) * this.padWidth;
32791         
32792         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32793         
32794         this.columnWidth += padExtra
32795         //this.padWidth = Math.floor(padavail /  ( this.cols));
32796         
32797         // adjust colum width so that padding is fixed??
32798         
32799         // we have 3 columns ... total = width * 3
32800         // we have X left over... that should be used by 
32801         
32802         //if (this.expandC) {
32803             
32804         //}
32805         
32806         
32807         
32808     },
32809     
32810     getContainerWidth : function()
32811     {
32812        /* // container is parent if fit width
32813         var container = this.isFitWidth ? this.element.parentNode : this.element;
32814         // check that this.size and size are there
32815         // IE8 triggers resize on body size change, so they might not be
32816         
32817         var size = getSize( container );  //FIXME
32818         this.containerWidth = size && size.innerWidth; //FIXME
32819         */
32820          
32821         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32822         
32823     },
32824     
32825     _getItemLayoutPosition : function( item )  // what is item?
32826     {
32827         // we resize the item to our columnWidth..
32828       
32829         item.setWidth(this.columnWidth);
32830         item.autoBoxAdjust  = false;
32831         
32832         var sz = item.getSize();
32833  
32834         // how many columns does this brick span
32835         var remainder = this.containerWidth % this.columnWidth;
32836         
32837         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32838         // round if off by 1 pixel, otherwise use ceil
32839         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32840         colSpan = Math.min( colSpan, this.cols );
32841         
32842         // normally this should be '1' as we dont' currently allow multi width columns..
32843         
32844         var colGroup = this._getColGroup( colSpan );
32845         // get the minimum Y value from the columns
32846         var minimumY = Math.min.apply( Math, colGroup );
32847         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32848         
32849         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32850          
32851         // position the brick
32852         var position = {
32853             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32854             y: this.currentSize.y + minimumY + this.padHeight
32855         };
32856         
32857         Roo.log(position);
32858         // apply setHeight to necessary columns
32859         var setHeight = minimumY + sz.height + this.padHeight;
32860         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32861         
32862         var setSpan = this.cols + 1 - colGroup.length;
32863         for ( var i = 0; i < setSpan; i++ ) {
32864           this.colYs[ shortColIndex + i ] = setHeight ;
32865         }
32866       
32867         return position;
32868     },
32869     
32870     /**
32871      * @param {Number} colSpan - number of columns the element spans
32872      * @returns {Array} colGroup
32873      */
32874     _getColGroup : function( colSpan )
32875     {
32876         if ( colSpan < 2 ) {
32877           // if brick spans only one column, use all the column Ys
32878           return this.colYs;
32879         }
32880       
32881         var colGroup = [];
32882         // how many different places could this brick fit horizontally
32883         var groupCount = this.cols + 1 - colSpan;
32884         // for each group potential horizontal position
32885         for ( var i = 0; i < groupCount; i++ ) {
32886           // make an array of colY values for that one group
32887           var groupColYs = this.colYs.slice( i, i + colSpan );
32888           // and get the max value of the array
32889           colGroup[i] = Math.max.apply( Math, groupColYs );
32890         }
32891         return colGroup;
32892     },
32893     /*
32894     _manageStamp : function( stamp )
32895     {
32896         var stampSize =  stamp.getSize();
32897         var offset = stamp.getBox();
32898         // get the columns that this stamp affects
32899         var firstX = this.isOriginLeft ? offset.x : offset.right;
32900         var lastX = firstX + stampSize.width;
32901         var firstCol = Math.floor( firstX / this.columnWidth );
32902         firstCol = Math.max( 0, firstCol );
32903         
32904         var lastCol = Math.floor( lastX / this.columnWidth );
32905         // lastCol should not go over if multiple of columnWidth #425
32906         lastCol -= lastX % this.columnWidth ? 0 : 1;
32907         lastCol = Math.min( this.cols - 1, lastCol );
32908         
32909         // set colYs to bottom of the stamp
32910         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32911             stampSize.height;
32912             
32913         for ( var i = firstCol; i <= lastCol; i++ ) {
32914           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32915         }
32916     },
32917     */
32918     
32919     _getContainerSize : function()
32920     {
32921         this.maxY = Math.max.apply( Math, this.colYs );
32922         var size = {
32923             height: this.maxY
32924         };
32925       
32926         if ( this.isFitWidth ) {
32927             size.width = this._getContainerFitWidth();
32928         }
32929       
32930         return size;
32931     },
32932     
32933     _getContainerFitWidth : function()
32934     {
32935         var unusedCols = 0;
32936         // count unused columns
32937         var i = this.cols;
32938         while ( --i ) {
32939           if ( this.colYs[i] !== 0 ) {
32940             break;
32941           }
32942           unusedCols++;
32943         }
32944         // fit container to columns that have been used
32945         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32946     },
32947     
32948     needsResizeLayout : function()
32949     {
32950         var previousWidth = this.containerWidth;
32951         this.getContainerWidth();
32952         return previousWidth !== this.containerWidth;
32953     }
32954  
32955 });
32956
32957  
32958
32959  /*
32960  * - LGPL
32961  *
32962  * element
32963  * 
32964  */
32965
32966 /**
32967  * @class Roo.bootstrap.MasonryBrick
32968  * @extends Roo.bootstrap.Component
32969  * Bootstrap MasonryBrick class
32970  * 
32971  * @constructor
32972  * Create a new MasonryBrick
32973  * @param {Object} config The config object
32974  */
32975
32976 Roo.bootstrap.MasonryBrick = function(config){
32977     
32978     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32979     
32980     Roo.bootstrap.MasonryBrick.register(this);
32981     
32982     this.addEvents({
32983         // raw events
32984         /**
32985          * @event click
32986          * When a MasonryBrick is clcik
32987          * @param {Roo.bootstrap.MasonryBrick} this
32988          * @param {Roo.EventObject} e
32989          */
32990         "click" : true
32991     });
32992 };
32993
32994 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32995     
32996     /**
32997      * @cfg {String} title
32998      */   
32999     title : '',
33000     /**
33001      * @cfg {String} html
33002      */   
33003     html : '',
33004     /**
33005      * @cfg {String} bgimage
33006      */   
33007     bgimage : '',
33008     /**
33009      * @cfg {String} videourl
33010      */   
33011     videourl : '',
33012     /**
33013      * @cfg {String} cls
33014      */   
33015     cls : '',
33016     /**
33017      * @cfg {String} href
33018      */   
33019     href : '',
33020     /**
33021      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33022      */   
33023     size : 'xs',
33024     
33025     /**
33026      * @cfg {String} placetitle (center|bottom)
33027      */   
33028     placetitle : '',
33029     
33030     /**
33031      * @cfg {Boolean} isFitContainer defalut true
33032      */   
33033     isFitContainer : true, 
33034     
33035     /**
33036      * @cfg {Boolean} preventDefault defalut false
33037      */   
33038     preventDefault : false, 
33039     
33040     /**
33041      * @cfg {Boolean} inverse defalut false
33042      */   
33043     maskInverse : false, 
33044     
33045     getAutoCreate : function()
33046     {
33047         if(!this.isFitContainer){
33048             return this.getSplitAutoCreate();
33049         }
33050         
33051         var cls = 'masonry-brick masonry-brick-full';
33052         
33053         if(this.href.length){
33054             cls += ' masonry-brick-link';
33055         }
33056         
33057         if(this.bgimage.length){
33058             cls += ' masonry-brick-image';
33059         }
33060         
33061         if(this.maskInverse){
33062             cls += ' mask-inverse';
33063         }
33064         
33065         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33066             cls += ' enable-mask';
33067         }
33068         
33069         if(this.size){
33070             cls += ' masonry-' + this.size + '-brick';
33071         }
33072         
33073         if(this.placetitle.length){
33074             
33075             switch (this.placetitle) {
33076                 case 'center' :
33077                     cls += ' masonry-center-title';
33078                     break;
33079                 case 'bottom' :
33080                     cls += ' masonry-bottom-title';
33081                     break;
33082                 default:
33083                     break;
33084             }
33085             
33086         } else {
33087             if(!this.html.length && !this.bgimage.length){
33088                 cls += ' masonry-center-title';
33089             }
33090
33091             if(!this.html.length && this.bgimage.length){
33092                 cls += ' masonry-bottom-title';
33093             }
33094         }
33095         
33096         if(this.cls){
33097             cls += ' ' + this.cls;
33098         }
33099         
33100         var cfg = {
33101             tag: (this.href.length) ? 'a' : 'div',
33102             cls: cls,
33103             cn: [
33104                 {
33105                     tag: 'div',
33106                     cls: 'masonry-brick-mask'
33107                 },
33108                 {
33109                     tag: 'div',
33110                     cls: 'masonry-brick-paragraph',
33111                     cn: []
33112                 }
33113             ]
33114         };
33115         
33116         if(this.href.length){
33117             cfg.href = this.href;
33118         }
33119         
33120         var cn = cfg.cn[1].cn;
33121         
33122         if(this.title.length){
33123             cn.push({
33124                 tag: 'h4',
33125                 cls: 'masonry-brick-title',
33126                 html: this.title
33127             });
33128         }
33129         
33130         if(this.html.length){
33131             cn.push({
33132                 tag: 'p',
33133                 cls: 'masonry-brick-text',
33134                 html: this.html
33135             });
33136         }
33137         
33138         if (!this.title.length && !this.html.length) {
33139             cfg.cn[1].cls += ' hide';
33140         }
33141         
33142         if(this.bgimage.length){
33143             cfg.cn.push({
33144                 tag: 'img',
33145                 cls: 'masonry-brick-image-view',
33146                 src: this.bgimage
33147             });
33148         }
33149         
33150         if(this.videourl.length){
33151             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33152             // youtube support only?
33153             cfg.cn.push({
33154                 tag: 'iframe',
33155                 cls: 'masonry-brick-image-view',
33156                 src: vurl,
33157                 frameborder : 0,
33158                 allowfullscreen : true
33159             });
33160         }
33161         
33162         return cfg;
33163         
33164     },
33165     
33166     getSplitAutoCreate : function()
33167     {
33168         var cls = 'masonry-brick masonry-brick-split';
33169         
33170         if(this.href.length){
33171             cls += ' masonry-brick-link';
33172         }
33173         
33174         if(this.bgimage.length){
33175             cls += ' masonry-brick-image';
33176         }
33177         
33178         if(this.size){
33179             cls += ' masonry-' + this.size + '-brick';
33180         }
33181         
33182         switch (this.placetitle) {
33183             case 'center' :
33184                 cls += ' masonry-center-title';
33185                 break;
33186             case 'bottom' :
33187                 cls += ' masonry-bottom-title';
33188                 break;
33189             default:
33190                 if(!this.bgimage.length){
33191                     cls += ' masonry-center-title';
33192                 }
33193
33194                 if(this.bgimage.length){
33195                     cls += ' masonry-bottom-title';
33196                 }
33197                 break;
33198         }
33199         
33200         if(this.cls){
33201             cls += ' ' + this.cls;
33202         }
33203         
33204         var cfg = {
33205             tag: (this.href.length) ? 'a' : 'div',
33206             cls: cls,
33207             cn: [
33208                 {
33209                     tag: 'div',
33210                     cls: 'masonry-brick-split-head',
33211                     cn: [
33212                         {
33213                             tag: 'div',
33214                             cls: 'masonry-brick-paragraph',
33215                             cn: []
33216                         }
33217                     ]
33218                 },
33219                 {
33220                     tag: 'div',
33221                     cls: 'masonry-brick-split-body',
33222                     cn: []
33223                 }
33224             ]
33225         };
33226         
33227         if(this.href.length){
33228             cfg.href = this.href;
33229         }
33230         
33231         if(this.title.length){
33232             cfg.cn[0].cn[0].cn.push({
33233                 tag: 'h4',
33234                 cls: 'masonry-brick-title',
33235                 html: this.title
33236             });
33237         }
33238         
33239         if(this.html.length){
33240             cfg.cn[1].cn.push({
33241                 tag: 'p',
33242                 cls: 'masonry-brick-text',
33243                 html: this.html
33244             });
33245         }
33246
33247         if(this.bgimage.length){
33248             cfg.cn[0].cn.push({
33249                 tag: 'img',
33250                 cls: 'masonry-brick-image-view',
33251                 src: this.bgimage
33252             });
33253         }
33254         
33255         if(this.videourl.length){
33256             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33257             // youtube support only?
33258             cfg.cn[0].cn.cn.push({
33259                 tag: 'iframe',
33260                 cls: 'masonry-brick-image-view',
33261                 src: vurl,
33262                 frameborder : 0,
33263                 allowfullscreen : true
33264             });
33265         }
33266         
33267         return cfg;
33268     },
33269     
33270     initEvents: function() 
33271     {
33272         switch (this.size) {
33273             case 'xs' :
33274                 this.x = 1;
33275                 this.y = 1;
33276                 break;
33277             case 'sm' :
33278                 this.x = 2;
33279                 this.y = 2;
33280                 break;
33281             case 'md' :
33282             case 'md-left' :
33283             case 'md-right' :
33284                 this.x = 3;
33285                 this.y = 3;
33286                 break;
33287             case 'tall' :
33288                 this.x = 2;
33289                 this.y = 3;
33290                 break;
33291             case 'wide' :
33292                 this.x = 3;
33293                 this.y = 2;
33294                 break;
33295             case 'wide-thin' :
33296                 this.x = 3;
33297                 this.y = 1;
33298                 break;
33299                         
33300             default :
33301                 break;
33302         }
33303         
33304         if(Roo.isTouch){
33305             this.el.on('touchstart', this.onTouchStart, this);
33306             this.el.on('touchmove', this.onTouchMove, this);
33307             this.el.on('touchend', this.onTouchEnd, this);
33308             this.el.on('contextmenu', this.onContextMenu, this);
33309         } else {
33310             this.el.on('mouseenter'  ,this.enter, this);
33311             this.el.on('mouseleave', this.leave, this);
33312             this.el.on('click', this.onClick, this);
33313         }
33314         
33315         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33316             this.parent().bricks.push(this);   
33317         }
33318         
33319     },
33320     
33321     onClick: function(e, el)
33322     {
33323         var time = this.endTimer - this.startTimer;
33324         // Roo.log(e.preventDefault());
33325         if(Roo.isTouch){
33326             if(time > 1000){
33327                 e.preventDefault();
33328                 return;
33329             }
33330         }
33331         
33332         if(!this.preventDefault){
33333             return;
33334         }
33335         
33336         e.preventDefault();
33337         
33338         if (this.activeClass != '') {
33339             this.selectBrick();
33340         }
33341         
33342         this.fireEvent('click', this, e);
33343     },
33344     
33345     enter: function(e, el)
33346     {
33347         e.preventDefault();
33348         
33349         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33350             return;
33351         }
33352         
33353         if(this.bgimage.length && this.html.length){
33354             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33355         }
33356     },
33357     
33358     leave: function(e, el)
33359     {
33360         e.preventDefault();
33361         
33362         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33363             return;
33364         }
33365         
33366         if(this.bgimage.length && this.html.length){
33367             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33368         }
33369     },
33370     
33371     onTouchStart: function(e, el)
33372     {
33373 //        e.preventDefault();
33374         
33375         this.touchmoved = false;
33376         
33377         if(!this.isFitContainer){
33378             return;
33379         }
33380         
33381         if(!this.bgimage.length || !this.html.length){
33382             return;
33383         }
33384         
33385         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33386         
33387         this.timer = new Date().getTime();
33388         
33389     },
33390     
33391     onTouchMove: function(e, el)
33392     {
33393         this.touchmoved = true;
33394     },
33395     
33396     onContextMenu : function(e,el)
33397     {
33398         e.preventDefault();
33399         e.stopPropagation();
33400         return false;
33401     },
33402     
33403     onTouchEnd: function(e, el)
33404     {
33405 //        e.preventDefault();
33406         
33407         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33408         
33409             this.leave(e,el);
33410             
33411             return;
33412         }
33413         
33414         if(!this.bgimage.length || !this.html.length){
33415             
33416             if(this.href.length){
33417                 window.location.href = this.href;
33418             }
33419             
33420             return;
33421         }
33422         
33423         if(!this.isFitContainer){
33424             return;
33425         }
33426         
33427         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33428         
33429         window.location.href = this.href;
33430     },
33431     
33432     //selection on single brick only
33433     selectBrick : function() {
33434         
33435         if (!this.parentId) {
33436             return;
33437         }
33438         
33439         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33440         var index = m.selectedBrick.indexOf(this.id);
33441         
33442         if ( index > -1) {
33443             m.selectedBrick.splice(index,1);
33444             this.el.removeClass(this.activeClass);
33445             return;
33446         }
33447         
33448         for(var i = 0; i < m.selectedBrick.length; i++) {
33449             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33450             b.el.removeClass(b.activeClass);
33451         }
33452         
33453         m.selectedBrick = [];
33454         
33455         m.selectedBrick.push(this.id);
33456         this.el.addClass(this.activeClass);
33457         return;
33458     },
33459     
33460     isSelected : function(){
33461         return this.el.hasClass(this.activeClass);
33462         
33463     }
33464 });
33465
33466 Roo.apply(Roo.bootstrap.MasonryBrick, {
33467     
33468     //groups: {},
33469     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33470      /**
33471     * register a Masonry Brick
33472     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33473     */
33474     
33475     register : function(brick)
33476     {
33477         //this.groups[brick.id] = brick;
33478         this.groups.add(brick.id, brick);
33479     },
33480     /**
33481     * fetch a  masonry brick based on the masonry brick ID
33482     * @param {string} the masonry brick to add
33483     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33484     */
33485     
33486     get: function(brick_id) 
33487     {
33488         // if (typeof(this.groups[brick_id]) == 'undefined') {
33489         //     return false;
33490         // }
33491         // return this.groups[brick_id] ;
33492         
33493         if(this.groups.key(brick_id)) {
33494             return this.groups.key(brick_id);
33495         }
33496         
33497         return false;
33498     }
33499     
33500     
33501     
33502 });
33503
33504  /*
33505  * - LGPL
33506  *
33507  * element
33508  * 
33509  */
33510
33511 /**
33512  * @class Roo.bootstrap.Brick
33513  * @extends Roo.bootstrap.Component
33514  * Bootstrap Brick class
33515  * 
33516  * @constructor
33517  * Create a new Brick
33518  * @param {Object} config The config object
33519  */
33520
33521 Roo.bootstrap.Brick = function(config){
33522     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33523     
33524     this.addEvents({
33525         // raw events
33526         /**
33527          * @event click
33528          * When a Brick is click
33529          * @param {Roo.bootstrap.Brick} this
33530          * @param {Roo.EventObject} e
33531          */
33532         "click" : true
33533     });
33534 };
33535
33536 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33537     
33538     /**
33539      * @cfg {String} title
33540      */   
33541     title : '',
33542     /**
33543      * @cfg {String} html
33544      */   
33545     html : '',
33546     /**
33547      * @cfg {String} bgimage
33548      */   
33549     bgimage : '',
33550     /**
33551      * @cfg {String} cls
33552      */   
33553     cls : '',
33554     /**
33555      * @cfg {String} href
33556      */   
33557     href : '',
33558     /**
33559      * @cfg {String} video
33560      */   
33561     video : '',
33562     /**
33563      * @cfg {Boolean} square
33564      */   
33565     square : true,
33566     
33567     getAutoCreate : function()
33568     {
33569         var cls = 'roo-brick';
33570         
33571         if(this.href.length){
33572             cls += ' roo-brick-link';
33573         }
33574         
33575         if(this.bgimage.length){
33576             cls += ' roo-brick-image';
33577         }
33578         
33579         if(!this.html.length && !this.bgimage.length){
33580             cls += ' roo-brick-center-title';
33581         }
33582         
33583         if(!this.html.length && this.bgimage.length){
33584             cls += ' roo-brick-bottom-title';
33585         }
33586         
33587         if(this.cls){
33588             cls += ' ' + this.cls;
33589         }
33590         
33591         var cfg = {
33592             tag: (this.href.length) ? 'a' : 'div',
33593             cls: cls,
33594             cn: [
33595                 {
33596                     tag: 'div',
33597                     cls: 'roo-brick-paragraph',
33598                     cn: []
33599                 }
33600             ]
33601         };
33602         
33603         if(this.href.length){
33604             cfg.href = this.href;
33605         }
33606         
33607         var cn = cfg.cn[0].cn;
33608         
33609         if(this.title.length){
33610             cn.push({
33611                 tag: 'h4',
33612                 cls: 'roo-brick-title',
33613                 html: this.title
33614             });
33615         }
33616         
33617         if(this.html.length){
33618             cn.push({
33619                 tag: 'p',
33620                 cls: 'roo-brick-text',
33621                 html: this.html
33622             });
33623         } else {
33624             cn.cls += ' hide';
33625         }
33626         
33627         if(this.bgimage.length){
33628             cfg.cn.push({
33629                 tag: 'img',
33630                 cls: 'roo-brick-image-view',
33631                 src: this.bgimage
33632             });
33633         }
33634         
33635         return cfg;
33636     },
33637     
33638     initEvents: function() 
33639     {
33640         if(this.title.length || this.html.length){
33641             this.el.on('mouseenter'  ,this.enter, this);
33642             this.el.on('mouseleave', this.leave, this);
33643         }
33644         
33645         Roo.EventManager.onWindowResize(this.resize, this); 
33646         
33647         if(this.bgimage.length){
33648             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33649             this.imageEl.on('load', this.onImageLoad, this);
33650             return;
33651         }
33652         
33653         this.resize();
33654     },
33655     
33656     onImageLoad : function()
33657     {
33658         this.resize();
33659     },
33660     
33661     resize : function()
33662     {
33663         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33664         
33665         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33666         
33667         if(this.bgimage.length){
33668             var image = this.el.select('.roo-brick-image-view', true).first();
33669             
33670             image.setWidth(paragraph.getWidth());
33671             
33672             if(this.square){
33673                 image.setHeight(paragraph.getWidth());
33674             }
33675             
33676             this.el.setHeight(image.getHeight());
33677             paragraph.setHeight(image.getHeight());
33678             
33679         }
33680         
33681     },
33682     
33683     enter: function(e, el)
33684     {
33685         e.preventDefault();
33686         
33687         if(this.bgimage.length){
33688             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33689             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33690         }
33691     },
33692     
33693     leave: function(e, el)
33694     {
33695         e.preventDefault();
33696         
33697         if(this.bgimage.length){
33698             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33699             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33700         }
33701     }
33702     
33703 });
33704
33705  
33706
33707  /*
33708  * - LGPL
33709  *
33710  * Number field 
33711  */
33712
33713 /**
33714  * @class Roo.bootstrap.NumberField
33715  * @extends Roo.bootstrap.Input
33716  * Bootstrap NumberField class
33717  * 
33718  * 
33719  * 
33720  * 
33721  * @constructor
33722  * Create a new NumberField
33723  * @param {Object} config The config object
33724  */
33725
33726 Roo.bootstrap.NumberField = function(config){
33727     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33728 };
33729
33730 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33731     
33732     /**
33733      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33734      */
33735     allowDecimals : true,
33736     /**
33737      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33738      */
33739     decimalSeparator : ".",
33740     /**
33741      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33742      */
33743     decimalPrecision : 2,
33744     /**
33745      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33746      */
33747     allowNegative : true,
33748     
33749     /**
33750      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33751      */
33752     allowZero: true,
33753     /**
33754      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33755      */
33756     minValue : Number.NEGATIVE_INFINITY,
33757     /**
33758      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33759      */
33760     maxValue : Number.MAX_VALUE,
33761     /**
33762      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33763      */
33764     minText : "The minimum value for this field is {0}",
33765     /**
33766      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33767      */
33768     maxText : "The maximum value for this field is {0}",
33769     /**
33770      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33771      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33772      */
33773     nanText : "{0} is not a valid number",
33774     /**
33775      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33776      */
33777     thousandsDelimiter : false,
33778     /**
33779      * @cfg {String} valueAlign alignment of value
33780      */
33781     valueAlign : "left",
33782
33783     getAutoCreate : function()
33784     {
33785         var hiddenInput = {
33786             tag: 'input',
33787             type: 'hidden',
33788             id: Roo.id(),
33789             cls: 'hidden-number-input'
33790         };
33791         
33792         if (this.name) {
33793             hiddenInput.name = this.name;
33794         }
33795         
33796         this.name = '';
33797         
33798         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33799         
33800         this.name = hiddenInput.name;
33801         
33802         if(cfg.cn.length > 0) {
33803             cfg.cn.push(hiddenInput);
33804         }
33805         
33806         return cfg;
33807     },
33808
33809     // private
33810     initEvents : function()
33811     {   
33812         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33813         
33814         var allowed = "0123456789";
33815         
33816         if(this.allowDecimals){
33817             allowed += this.decimalSeparator;
33818         }
33819         
33820         if(this.allowNegative){
33821             allowed += "-";
33822         }
33823         
33824         if(this.thousandsDelimiter) {
33825             allowed += ",";
33826         }
33827         
33828         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33829         
33830         var keyPress = function(e){
33831             
33832             var k = e.getKey();
33833             
33834             var c = e.getCharCode();
33835             
33836             if(
33837                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33838                     allowed.indexOf(String.fromCharCode(c)) === -1
33839             ){
33840                 e.stopEvent();
33841                 return;
33842             }
33843             
33844             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33845                 return;
33846             }
33847             
33848             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33849                 e.stopEvent();
33850             }
33851         };
33852         
33853         this.el.on("keypress", keyPress, this);
33854     },
33855     
33856     validateValue : function(value)
33857     {
33858         
33859         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33860             return false;
33861         }
33862         
33863         var num = this.parseValue(value);
33864         
33865         if(isNaN(num)){
33866             this.markInvalid(String.format(this.nanText, value));
33867             return false;
33868         }
33869         
33870         if(num < this.minValue){
33871             this.markInvalid(String.format(this.minText, this.minValue));
33872             return false;
33873         }
33874         
33875         if(num > this.maxValue){
33876             this.markInvalid(String.format(this.maxText, this.maxValue));
33877             return false;
33878         }
33879         
33880         return true;
33881     },
33882
33883     getValue : function()
33884     {
33885         var v = this.hiddenEl().getValue();
33886         
33887         return this.fixPrecision(this.parseValue(v));
33888     },
33889
33890     parseValue : function(value)
33891     {
33892         if(this.thousandsDelimiter) {
33893             value += "";
33894             r = new RegExp(",", "g");
33895             value = value.replace(r, "");
33896         }
33897         
33898         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33899         return isNaN(value) ? '' : value;
33900     },
33901
33902     fixPrecision : function(value)
33903     {
33904         if(this.thousandsDelimiter) {
33905             value += "";
33906             r = new RegExp(",", "g");
33907             value = value.replace(r, "");
33908         }
33909         
33910         var nan = isNaN(value);
33911         
33912         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33913             return nan ? '' : value;
33914         }
33915         return parseFloat(value).toFixed(this.decimalPrecision);
33916     },
33917
33918     setValue : function(v)
33919     {
33920         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33921         
33922         this.value = v;
33923         
33924         if(this.rendered){
33925             
33926             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33927             
33928             this.inputEl().dom.value = (v == '') ? '' :
33929                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33930             
33931             if(!this.allowZero && v === '0') {
33932                 this.hiddenEl().dom.value = '';
33933                 this.inputEl().dom.value = '';
33934             }
33935             
33936             this.validate();
33937         }
33938     },
33939
33940     decimalPrecisionFcn : function(v)
33941     {
33942         return Math.floor(v);
33943     },
33944
33945     beforeBlur : function()
33946     {
33947         var v = this.parseValue(this.getRawValue());
33948         
33949         if(v || v === 0 || v === ''){
33950             this.setValue(v);
33951         }
33952     },
33953     
33954     hiddenEl : function()
33955     {
33956         return this.el.select('input.hidden-number-input',true).first();
33957     }
33958     
33959 });
33960
33961  
33962
33963 /*
33964 * Licence: LGPL
33965 */
33966
33967 /**
33968  * @class Roo.bootstrap.DocumentSlider
33969  * @extends Roo.bootstrap.Component
33970  * Bootstrap DocumentSlider class
33971  * 
33972  * @constructor
33973  * Create a new DocumentViewer
33974  * @param {Object} config The config object
33975  */
33976
33977 Roo.bootstrap.DocumentSlider = function(config){
33978     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33979     
33980     this.files = [];
33981     
33982     this.addEvents({
33983         /**
33984          * @event initial
33985          * Fire after initEvent
33986          * @param {Roo.bootstrap.DocumentSlider} this
33987          */
33988         "initial" : true,
33989         /**
33990          * @event update
33991          * Fire after update
33992          * @param {Roo.bootstrap.DocumentSlider} this
33993          */
33994         "update" : true,
33995         /**
33996          * @event click
33997          * Fire after click
33998          * @param {Roo.bootstrap.DocumentSlider} this
33999          */
34000         "click" : true
34001     });
34002 };
34003
34004 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
34005     
34006     files : false,
34007     
34008     indicator : 0,
34009     
34010     getAutoCreate : function()
34011     {
34012         var cfg = {
34013             tag : 'div',
34014             cls : 'roo-document-slider',
34015             cn : [
34016                 {
34017                     tag : 'div',
34018                     cls : 'roo-document-slider-header',
34019                     cn : [
34020                         {
34021                             tag : 'div',
34022                             cls : 'roo-document-slider-header-title'
34023                         }
34024                     ]
34025                 },
34026                 {
34027                     tag : 'div',
34028                     cls : 'roo-document-slider-body',
34029                     cn : [
34030                         {
34031                             tag : 'div',
34032                             cls : 'roo-document-slider-prev',
34033                             cn : [
34034                                 {
34035                                     tag : 'i',
34036                                     cls : 'fa fa-chevron-left'
34037                                 }
34038                             ]
34039                         },
34040                         {
34041                             tag : 'div',
34042                             cls : 'roo-document-slider-thumb',
34043                             cn : [
34044                                 {
34045                                     tag : 'img',
34046                                     cls : 'roo-document-slider-image'
34047                                 }
34048                             ]
34049                         },
34050                         {
34051                             tag : 'div',
34052                             cls : 'roo-document-slider-next',
34053                             cn : [
34054                                 {
34055                                     tag : 'i',
34056                                     cls : 'fa fa-chevron-right'
34057                                 }
34058                             ]
34059                         }
34060                     ]
34061                 }
34062             ]
34063         };
34064         
34065         return cfg;
34066     },
34067     
34068     initEvents : function()
34069     {
34070         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34071         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34072         
34073         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34074         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34075         
34076         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34077         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34078         
34079         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34080         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34081         
34082         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34083         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34084         
34085         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34086         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34087         
34088         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34089         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34090         
34091         this.thumbEl.on('click', this.onClick, this);
34092         
34093         this.prevIndicator.on('click', this.prev, this);
34094         
34095         this.nextIndicator.on('click', this.next, this);
34096         
34097     },
34098     
34099     initial : function()
34100     {
34101         if(this.files.length){
34102             this.indicator = 1;
34103             this.update()
34104         }
34105         
34106         this.fireEvent('initial', this);
34107     },
34108     
34109     update : function()
34110     {
34111         this.imageEl.attr('src', this.files[this.indicator - 1]);
34112         
34113         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34114         
34115         this.prevIndicator.show();
34116         
34117         if(this.indicator == 1){
34118             this.prevIndicator.hide();
34119         }
34120         
34121         this.nextIndicator.show();
34122         
34123         if(this.indicator == this.files.length){
34124             this.nextIndicator.hide();
34125         }
34126         
34127         this.thumbEl.scrollTo('top');
34128         
34129         this.fireEvent('update', this);
34130     },
34131     
34132     onClick : function(e)
34133     {
34134         e.preventDefault();
34135         
34136         this.fireEvent('click', this);
34137     },
34138     
34139     prev : function(e)
34140     {
34141         e.preventDefault();
34142         
34143         this.indicator = Math.max(1, this.indicator - 1);
34144         
34145         this.update();
34146     },
34147     
34148     next : function(e)
34149     {
34150         e.preventDefault();
34151         
34152         this.indicator = Math.min(this.files.length, this.indicator + 1);
34153         
34154         this.update();
34155     }
34156 });
34157 /*
34158  * - LGPL
34159  *
34160  * RadioSet
34161  *
34162  *
34163  */
34164
34165 /**
34166  * @class Roo.bootstrap.RadioSet
34167  * @extends Roo.bootstrap.Input
34168  * Bootstrap RadioSet class
34169  * @cfg {String} indicatorpos (left|right) default left
34170  * @cfg {Boolean} inline (true|false) inline the element (default true)
34171  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34172  * @constructor
34173  * Create a new RadioSet
34174  * @param {Object} config The config object
34175  */
34176
34177 Roo.bootstrap.RadioSet = function(config){
34178     
34179     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34180     
34181     this.radioes = [];
34182     
34183     Roo.bootstrap.RadioSet.register(this);
34184     
34185     this.addEvents({
34186         /**
34187         * @event check
34188         * Fires when the element is checked or unchecked.
34189         * @param {Roo.bootstrap.RadioSet} this This radio
34190         * @param {Roo.bootstrap.Radio} item The checked item
34191         */
34192        check : true,
34193        /**
34194         * @event click
34195         * Fires when the element is click.
34196         * @param {Roo.bootstrap.RadioSet} this This radio set
34197         * @param {Roo.bootstrap.Radio} item The checked item
34198         * @param {Roo.EventObject} e The event object
34199         */
34200        click : true
34201     });
34202     
34203 };
34204
34205 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34206
34207     radioes : false,
34208     
34209     inline : true,
34210     
34211     weight : '',
34212     
34213     indicatorpos : 'left',
34214     
34215     getAutoCreate : function()
34216     {
34217         var label = {
34218             tag : 'label',
34219             cls : 'roo-radio-set-label',
34220             cn : [
34221                 {
34222                     tag : 'span',
34223                     html : this.fieldLabel
34224                 }
34225             ]
34226         };
34227         if (Roo.bootstrap.version == 3) {
34228             
34229             
34230             if(this.indicatorpos == 'left'){
34231                 label.cn.unshift({
34232                     tag : 'i',
34233                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34234                     tooltip : 'This field is required'
34235                 });
34236             } else {
34237                 label.cn.push({
34238                     tag : 'i',
34239                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34240                     tooltip : 'This field is required'
34241                 });
34242             }
34243         }
34244         var items = {
34245             tag : 'div',
34246             cls : 'roo-radio-set-items'
34247         };
34248         
34249         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34250         
34251         if (align === 'left' && this.fieldLabel.length) {
34252             
34253             items = {
34254                 cls : "roo-radio-set-right", 
34255                 cn: [
34256                     items
34257                 ]
34258             };
34259             
34260             if(this.labelWidth > 12){
34261                 label.style = "width: " + this.labelWidth + 'px';
34262             }
34263             
34264             if(this.labelWidth < 13 && this.labelmd == 0){
34265                 this.labelmd = this.labelWidth;
34266             }
34267             
34268             if(this.labellg > 0){
34269                 label.cls += ' col-lg-' + this.labellg;
34270                 items.cls += ' col-lg-' + (12 - this.labellg);
34271             }
34272             
34273             if(this.labelmd > 0){
34274                 label.cls += ' col-md-' + this.labelmd;
34275                 items.cls += ' col-md-' + (12 - this.labelmd);
34276             }
34277             
34278             if(this.labelsm > 0){
34279                 label.cls += ' col-sm-' + this.labelsm;
34280                 items.cls += ' col-sm-' + (12 - this.labelsm);
34281             }
34282             
34283             if(this.labelxs > 0){
34284                 label.cls += ' col-xs-' + this.labelxs;
34285                 items.cls += ' col-xs-' + (12 - this.labelxs);
34286             }
34287         }
34288         
34289         var cfg = {
34290             tag : 'div',
34291             cls : 'roo-radio-set',
34292             cn : [
34293                 {
34294                     tag : 'input',
34295                     cls : 'roo-radio-set-input',
34296                     type : 'hidden',
34297                     name : this.name,
34298                     value : this.value ? this.value :  ''
34299                 },
34300                 label,
34301                 items
34302             ]
34303         };
34304         
34305         if(this.weight.length){
34306             cfg.cls += ' roo-radio-' + this.weight;
34307         }
34308         
34309         if(this.inline) {
34310             cfg.cls += ' roo-radio-set-inline';
34311         }
34312         
34313         var settings=this;
34314         ['xs','sm','md','lg'].map(function(size){
34315             if (settings[size]) {
34316                 cfg.cls += ' col-' + size + '-' + settings[size];
34317             }
34318         });
34319         
34320         return cfg;
34321         
34322     },
34323
34324     initEvents : function()
34325     {
34326         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34327         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34328         
34329         if(!this.fieldLabel.length){
34330             this.labelEl.hide();
34331         }
34332         
34333         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34334         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34335         
34336         this.indicator = this.indicatorEl();
34337         
34338         if(this.indicator){
34339             this.indicator.addClass('invisible');
34340         }
34341         
34342         this.originalValue = this.getValue();
34343         
34344     },
34345     
34346     inputEl: function ()
34347     {
34348         return this.el.select('.roo-radio-set-input', true).first();
34349     },
34350     
34351     getChildContainer : function()
34352     {
34353         return this.itemsEl;
34354     },
34355     
34356     register : function(item)
34357     {
34358         this.radioes.push(item);
34359         
34360     },
34361     
34362     validate : function()
34363     {   
34364         if(this.getVisibilityEl().hasClass('hidden')){
34365             return true;
34366         }
34367         
34368         var valid = false;
34369         
34370         Roo.each(this.radioes, function(i){
34371             if(!i.checked){
34372                 return;
34373             }
34374             
34375             valid = true;
34376             return false;
34377         });
34378         
34379         if(this.allowBlank) {
34380             return true;
34381         }
34382         
34383         if(this.disabled || valid){
34384             this.markValid();
34385             return true;
34386         }
34387         
34388         this.markInvalid();
34389         return false;
34390         
34391     },
34392     
34393     markValid : function()
34394     {
34395         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34396             this.indicatorEl().removeClass('visible');
34397             this.indicatorEl().addClass('invisible');
34398         }
34399         
34400         
34401         if (Roo.bootstrap.version == 3) {
34402             this.el.removeClass([this.invalidClass, this.validClass]);
34403             this.el.addClass(this.validClass);
34404         } else {
34405             this.el.removeClass(['is-invalid','is-valid']);
34406             this.el.addClass(['is-valid']);
34407         }
34408         this.fireEvent('valid', this);
34409     },
34410     
34411     markInvalid : function(msg)
34412     {
34413         if(this.allowBlank || this.disabled){
34414             return;
34415         }
34416         
34417         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34418             this.indicatorEl().removeClass('invisible');
34419             this.indicatorEl().addClass('visible');
34420         }
34421         if (Roo.bootstrap.version == 3) {
34422             this.el.removeClass([this.invalidClass, this.validClass]);
34423             this.el.addClass(this.invalidClass);
34424         } else {
34425             this.el.removeClass(['is-invalid','is-valid']);
34426             this.el.addClass(['is-invalid']);
34427         }
34428         
34429         this.fireEvent('invalid', this, msg);
34430         
34431     },
34432     
34433     setValue : function(v, suppressEvent)
34434     {   
34435         if(this.value === v){
34436             return;
34437         }
34438         
34439         this.value = v;
34440         
34441         if(this.rendered){
34442             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34443         }
34444         
34445         Roo.each(this.radioes, function(i){
34446             i.checked = false;
34447             i.el.removeClass('checked');
34448         });
34449         
34450         Roo.each(this.radioes, function(i){
34451             
34452             if(i.value === v || i.value.toString() === v.toString()){
34453                 i.checked = true;
34454                 i.el.addClass('checked');
34455                 
34456                 if(suppressEvent !== true){
34457                     this.fireEvent('check', this, i);
34458                 }
34459                 
34460                 return false;
34461             }
34462             
34463         }, this);
34464         
34465         this.validate();
34466     },
34467     
34468     clearInvalid : function(){
34469         
34470         if(!this.el || this.preventMark){
34471             return;
34472         }
34473         
34474         this.el.removeClass([this.invalidClass]);
34475         
34476         this.fireEvent('valid', this);
34477     }
34478     
34479 });
34480
34481 Roo.apply(Roo.bootstrap.RadioSet, {
34482     
34483     groups: {},
34484     
34485     register : function(set)
34486     {
34487         this.groups[set.name] = set;
34488     },
34489     
34490     get: function(name) 
34491     {
34492         if (typeof(this.groups[name]) == 'undefined') {
34493             return false;
34494         }
34495         
34496         return this.groups[name] ;
34497     }
34498     
34499 });
34500 /*
34501  * Based on:
34502  * Ext JS Library 1.1.1
34503  * Copyright(c) 2006-2007, Ext JS, LLC.
34504  *
34505  * Originally Released Under LGPL - original licence link has changed is not relivant.
34506  *
34507  * Fork - LGPL
34508  * <script type="text/javascript">
34509  */
34510
34511
34512 /**
34513  * @class Roo.bootstrap.SplitBar
34514  * @extends Roo.util.Observable
34515  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34516  * <br><br>
34517  * Usage:
34518  * <pre><code>
34519 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34520                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34521 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34522 split.minSize = 100;
34523 split.maxSize = 600;
34524 split.animate = true;
34525 split.on('moved', splitterMoved);
34526 </code></pre>
34527  * @constructor
34528  * Create a new SplitBar
34529  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34530  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34531  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34532  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34533                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34534                         position of the SplitBar).
34535  */
34536 Roo.bootstrap.SplitBar = function(cfg){
34537     
34538     /** @private */
34539     
34540     //{
34541     //  dragElement : elm
34542     //  resizingElement: el,
34543         // optional..
34544     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34545     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34546         // existingProxy ???
34547     //}
34548     
34549     this.el = Roo.get(cfg.dragElement, true);
34550     this.el.dom.unselectable = "on";
34551     /** @private */
34552     this.resizingEl = Roo.get(cfg.resizingElement, true);
34553
34554     /**
34555      * @private
34556      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34557      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34558      * @type Number
34559      */
34560     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34561     
34562     /**
34563      * The minimum size of the resizing element. (Defaults to 0)
34564      * @type Number
34565      */
34566     this.minSize = 0;
34567     
34568     /**
34569      * The maximum size of the resizing element. (Defaults to 2000)
34570      * @type Number
34571      */
34572     this.maxSize = 2000;
34573     
34574     /**
34575      * Whether to animate the transition to the new size
34576      * @type Boolean
34577      */
34578     this.animate = false;
34579     
34580     /**
34581      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34582      * @type Boolean
34583      */
34584     this.useShim = false;
34585     
34586     /** @private */
34587     this.shim = null;
34588     
34589     if(!cfg.existingProxy){
34590         /** @private */
34591         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34592     }else{
34593         this.proxy = Roo.get(cfg.existingProxy).dom;
34594     }
34595     /** @private */
34596     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34597     
34598     /** @private */
34599     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34600     
34601     /** @private */
34602     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34603     
34604     /** @private */
34605     this.dragSpecs = {};
34606     
34607     /**
34608      * @private The adapter to use to positon and resize elements
34609      */
34610     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34611     this.adapter.init(this);
34612     
34613     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34614         /** @private */
34615         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34616         this.el.addClass("roo-splitbar-h");
34617     }else{
34618         /** @private */
34619         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34620         this.el.addClass("roo-splitbar-v");
34621     }
34622     
34623     this.addEvents({
34624         /**
34625          * @event resize
34626          * Fires when the splitter is moved (alias for {@link #event-moved})
34627          * @param {Roo.bootstrap.SplitBar} this
34628          * @param {Number} newSize the new width or height
34629          */
34630         "resize" : true,
34631         /**
34632          * @event moved
34633          * Fires when the splitter is moved
34634          * @param {Roo.bootstrap.SplitBar} this
34635          * @param {Number} newSize the new width or height
34636          */
34637         "moved" : true,
34638         /**
34639          * @event beforeresize
34640          * Fires before the splitter is dragged
34641          * @param {Roo.bootstrap.SplitBar} this
34642          */
34643         "beforeresize" : true,
34644
34645         "beforeapply" : true
34646     });
34647
34648     Roo.util.Observable.call(this);
34649 };
34650
34651 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34652     onStartProxyDrag : function(x, y){
34653         this.fireEvent("beforeresize", this);
34654         if(!this.overlay){
34655             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34656             o.unselectable();
34657             o.enableDisplayMode("block");
34658             // all splitbars share the same overlay
34659             Roo.bootstrap.SplitBar.prototype.overlay = o;
34660         }
34661         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34662         this.overlay.show();
34663         Roo.get(this.proxy).setDisplayed("block");
34664         var size = this.adapter.getElementSize(this);
34665         this.activeMinSize = this.getMinimumSize();;
34666         this.activeMaxSize = this.getMaximumSize();;
34667         var c1 = size - this.activeMinSize;
34668         var c2 = Math.max(this.activeMaxSize - size, 0);
34669         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34670             this.dd.resetConstraints();
34671             this.dd.setXConstraint(
34672                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34673                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34674             );
34675             this.dd.setYConstraint(0, 0);
34676         }else{
34677             this.dd.resetConstraints();
34678             this.dd.setXConstraint(0, 0);
34679             this.dd.setYConstraint(
34680                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34681                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34682             );
34683          }
34684         this.dragSpecs.startSize = size;
34685         this.dragSpecs.startPoint = [x, y];
34686         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34687     },
34688     
34689     /** 
34690      * @private Called after the drag operation by the DDProxy
34691      */
34692     onEndProxyDrag : function(e){
34693         Roo.get(this.proxy).setDisplayed(false);
34694         var endPoint = Roo.lib.Event.getXY(e);
34695         if(this.overlay){
34696             this.overlay.hide();
34697         }
34698         var newSize;
34699         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34700             newSize = this.dragSpecs.startSize + 
34701                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34702                     endPoint[0] - this.dragSpecs.startPoint[0] :
34703                     this.dragSpecs.startPoint[0] - endPoint[0]
34704                 );
34705         }else{
34706             newSize = this.dragSpecs.startSize + 
34707                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34708                     endPoint[1] - this.dragSpecs.startPoint[1] :
34709                     this.dragSpecs.startPoint[1] - endPoint[1]
34710                 );
34711         }
34712         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34713         if(newSize != this.dragSpecs.startSize){
34714             if(this.fireEvent('beforeapply', this, newSize) !== false){
34715                 this.adapter.setElementSize(this, newSize);
34716                 this.fireEvent("moved", this, newSize);
34717                 this.fireEvent("resize", this, newSize);
34718             }
34719         }
34720     },
34721     
34722     /**
34723      * Get the adapter this SplitBar uses
34724      * @return The adapter object
34725      */
34726     getAdapter : function(){
34727         return this.adapter;
34728     },
34729     
34730     /**
34731      * Set the adapter this SplitBar uses
34732      * @param {Object} adapter A SplitBar adapter object
34733      */
34734     setAdapter : function(adapter){
34735         this.adapter = adapter;
34736         this.adapter.init(this);
34737     },
34738     
34739     /**
34740      * Gets the minimum size for the resizing element
34741      * @return {Number} The minimum size
34742      */
34743     getMinimumSize : function(){
34744         return this.minSize;
34745     },
34746     
34747     /**
34748      * Sets the minimum size for the resizing element
34749      * @param {Number} minSize The minimum size
34750      */
34751     setMinimumSize : function(minSize){
34752         this.minSize = minSize;
34753     },
34754     
34755     /**
34756      * Gets the maximum size for the resizing element
34757      * @return {Number} The maximum size
34758      */
34759     getMaximumSize : function(){
34760         return this.maxSize;
34761     },
34762     
34763     /**
34764      * Sets the maximum size for the resizing element
34765      * @param {Number} maxSize The maximum size
34766      */
34767     setMaximumSize : function(maxSize){
34768         this.maxSize = maxSize;
34769     },
34770     
34771     /**
34772      * Sets the initialize size for the resizing element
34773      * @param {Number} size The initial size
34774      */
34775     setCurrentSize : function(size){
34776         var oldAnimate = this.animate;
34777         this.animate = false;
34778         this.adapter.setElementSize(this, size);
34779         this.animate = oldAnimate;
34780     },
34781     
34782     /**
34783      * Destroy this splitbar. 
34784      * @param {Boolean} removeEl True to remove the element
34785      */
34786     destroy : function(removeEl){
34787         if(this.shim){
34788             this.shim.remove();
34789         }
34790         this.dd.unreg();
34791         this.proxy.parentNode.removeChild(this.proxy);
34792         if(removeEl){
34793             this.el.remove();
34794         }
34795     }
34796 });
34797
34798 /**
34799  * @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.
34800  */
34801 Roo.bootstrap.SplitBar.createProxy = function(dir){
34802     var proxy = new Roo.Element(document.createElement("div"));
34803     proxy.unselectable();
34804     var cls = 'roo-splitbar-proxy';
34805     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34806     document.body.appendChild(proxy.dom);
34807     return proxy.dom;
34808 };
34809
34810 /** 
34811  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34812  * Default Adapter. It assumes the splitter and resizing element are not positioned
34813  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34814  */
34815 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34816 };
34817
34818 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34819     // do nothing for now
34820     init : function(s){
34821     
34822     },
34823     /**
34824      * Called before drag operations to get the current size of the resizing element. 
34825      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34826      */
34827      getElementSize : function(s){
34828         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34829             return s.resizingEl.getWidth();
34830         }else{
34831             return s.resizingEl.getHeight();
34832         }
34833     },
34834     
34835     /**
34836      * Called after drag operations to set the size of the resizing element.
34837      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34838      * @param {Number} newSize The new size to set
34839      * @param {Function} onComplete A function to be invoked when resizing is complete
34840      */
34841     setElementSize : function(s, newSize, onComplete){
34842         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34843             if(!s.animate){
34844                 s.resizingEl.setWidth(newSize);
34845                 if(onComplete){
34846                     onComplete(s, newSize);
34847                 }
34848             }else{
34849                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34850             }
34851         }else{
34852             
34853             if(!s.animate){
34854                 s.resizingEl.setHeight(newSize);
34855                 if(onComplete){
34856                     onComplete(s, newSize);
34857                 }
34858             }else{
34859                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34860             }
34861         }
34862     }
34863 };
34864
34865 /** 
34866  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34867  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34868  * Adapter that  moves the splitter element to align with the resized sizing element. 
34869  * Used with an absolute positioned SplitBar.
34870  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34871  * document.body, make sure you assign an id to the body element.
34872  */
34873 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34874     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34875     this.container = Roo.get(container);
34876 };
34877
34878 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34879     init : function(s){
34880         this.basic.init(s);
34881     },
34882     
34883     getElementSize : function(s){
34884         return this.basic.getElementSize(s);
34885     },
34886     
34887     setElementSize : function(s, newSize, onComplete){
34888         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34889     },
34890     
34891     moveSplitter : function(s){
34892         var yes = Roo.bootstrap.SplitBar;
34893         switch(s.placement){
34894             case yes.LEFT:
34895                 s.el.setX(s.resizingEl.getRight());
34896                 break;
34897             case yes.RIGHT:
34898                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34899                 break;
34900             case yes.TOP:
34901                 s.el.setY(s.resizingEl.getBottom());
34902                 break;
34903             case yes.BOTTOM:
34904                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34905                 break;
34906         }
34907     }
34908 };
34909
34910 /**
34911  * Orientation constant - Create a vertical SplitBar
34912  * @static
34913  * @type Number
34914  */
34915 Roo.bootstrap.SplitBar.VERTICAL = 1;
34916
34917 /**
34918  * Orientation constant - Create a horizontal SplitBar
34919  * @static
34920  * @type Number
34921  */
34922 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34923
34924 /**
34925  * Placement constant - The resizing element is to the left of the splitter element
34926  * @static
34927  * @type Number
34928  */
34929 Roo.bootstrap.SplitBar.LEFT = 1;
34930
34931 /**
34932  * Placement constant - The resizing element is to the right of the splitter element
34933  * @static
34934  * @type Number
34935  */
34936 Roo.bootstrap.SplitBar.RIGHT = 2;
34937
34938 /**
34939  * Placement constant - The resizing element is positioned above the splitter element
34940  * @static
34941  * @type Number
34942  */
34943 Roo.bootstrap.SplitBar.TOP = 3;
34944
34945 /**
34946  * Placement constant - The resizing element is positioned under splitter element
34947  * @static
34948  * @type Number
34949  */
34950 Roo.bootstrap.SplitBar.BOTTOM = 4;
34951 Roo.namespace("Roo.bootstrap.layout");/*
34952  * Based on:
34953  * Ext JS Library 1.1.1
34954  * Copyright(c) 2006-2007, Ext JS, LLC.
34955  *
34956  * Originally Released Under LGPL - original licence link has changed is not relivant.
34957  *
34958  * Fork - LGPL
34959  * <script type="text/javascript">
34960  */
34961
34962 /**
34963  * @class Roo.bootstrap.layout.Manager
34964  * @extends Roo.bootstrap.Component
34965  * Base class for layout managers.
34966  */
34967 Roo.bootstrap.layout.Manager = function(config)
34968 {
34969     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34970
34971
34972
34973
34974
34975     /** false to disable window resize monitoring @type Boolean */
34976     this.monitorWindowResize = true;
34977     this.regions = {};
34978     this.addEvents({
34979         /**
34980          * @event layout
34981          * Fires when a layout is performed.
34982          * @param {Roo.LayoutManager} this
34983          */
34984         "layout" : true,
34985         /**
34986          * @event regionresized
34987          * Fires when the user resizes a region.
34988          * @param {Roo.LayoutRegion} region The resized region
34989          * @param {Number} newSize The new size (width for east/west, height for north/south)
34990          */
34991         "regionresized" : true,
34992         /**
34993          * @event regioncollapsed
34994          * Fires when a region is collapsed.
34995          * @param {Roo.LayoutRegion} region The collapsed region
34996          */
34997         "regioncollapsed" : true,
34998         /**
34999          * @event regionexpanded
35000          * Fires when a region is expanded.
35001          * @param {Roo.LayoutRegion} region The expanded region
35002          */
35003         "regionexpanded" : true
35004     });
35005     this.updating = false;
35006
35007     if (config.el) {
35008         this.el = Roo.get(config.el);
35009         this.initEvents();
35010     }
35011
35012 };
35013
35014 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35015
35016
35017     regions : null,
35018
35019     monitorWindowResize : true,
35020
35021
35022     updating : false,
35023
35024
35025     onRender : function(ct, position)
35026     {
35027         if(!this.el){
35028             this.el = Roo.get(ct);
35029             this.initEvents();
35030         }
35031         //this.fireEvent('render',this);
35032     },
35033
35034
35035     initEvents: function()
35036     {
35037
35038
35039         // ie scrollbar fix
35040         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35041             document.body.scroll = "no";
35042         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35043             this.el.position('relative');
35044         }
35045         this.id = this.el.id;
35046         this.el.addClass("roo-layout-container");
35047         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35048         if(this.el.dom != document.body ) {
35049             this.el.on('resize', this.layout,this);
35050             this.el.on('show', this.layout,this);
35051         }
35052
35053     },
35054
35055     /**
35056      * Returns true if this layout is currently being updated
35057      * @return {Boolean}
35058      */
35059     isUpdating : function(){
35060         return this.updating;
35061     },
35062
35063     /**
35064      * Suspend the LayoutManager from doing auto-layouts while
35065      * making multiple add or remove calls
35066      */
35067     beginUpdate : function(){
35068         this.updating = true;
35069     },
35070
35071     /**
35072      * Restore auto-layouts and optionally disable the manager from performing a layout
35073      * @param {Boolean} noLayout true to disable a layout update
35074      */
35075     endUpdate : function(noLayout){
35076         this.updating = false;
35077         if(!noLayout){
35078             this.layout();
35079         }
35080     },
35081
35082     layout: function(){
35083         // abstract...
35084     },
35085
35086     onRegionResized : function(region, newSize){
35087         this.fireEvent("regionresized", region, newSize);
35088         this.layout();
35089     },
35090
35091     onRegionCollapsed : function(region){
35092         this.fireEvent("regioncollapsed", region);
35093     },
35094
35095     onRegionExpanded : function(region){
35096         this.fireEvent("regionexpanded", region);
35097     },
35098
35099     /**
35100      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35101      * performs box-model adjustments.
35102      * @return {Object} The size as an object {width: (the width), height: (the height)}
35103      */
35104     getViewSize : function()
35105     {
35106         var size;
35107         if(this.el.dom != document.body){
35108             size = this.el.getSize();
35109         }else{
35110             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35111         }
35112         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35113         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35114         return size;
35115     },
35116
35117     /**
35118      * Returns the Element this layout is bound to.
35119      * @return {Roo.Element}
35120      */
35121     getEl : function(){
35122         return this.el;
35123     },
35124
35125     /**
35126      * Returns the specified region.
35127      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35128      * @return {Roo.LayoutRegion}
35129      */
35130     getRegion : function(target){
35131         return this.regions[target.toLowerCase()];
35132     },
35133
35134     onWindowResize : function(){
35135         if(this.monitorWindowResize){
35136             this.layout();
35137         }
35138     }
35139 });
35140 /*
35141  * Based on:
35142  * Ext JS Library 1.1.1
35143  * Copyright(c) 2006-2007, Ext JS, LLC.
35144  *
35145  * Originally Released Under LGPL - original licence link has changed is not relivant.
35146  *
35147  * Fork - LGPL
35148  * <script type="text/javascript">
35149  */
35150 /**
35151  * @class Roo.bootstrap.layout.Border
35152  * @extends Roo.bootstrap.layout.Manager
35153  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35154  * please see: examples/bootstrap/nested.html<br><br>
35155  
35156 <b>The container the layout is rendered into can be either the body element or any other element.
35157 If it is not the body element, the container needs to either be an absolute positioned element,
35158 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35159 the container size if it is not the body element.</b>
35160
35161 * @constructor
35162 * Create a new Border
35163 * @param {Object} config Configuration options
35164  */
35165 Roo.bootstrap.layout.Border = function(config){
35166     config = config || {};
35167     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35168     
35169     
35170     
35171     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35172         if(config[region]){
35173             config[region].region = region;
35174             this.addRegion(config[region]);
35175         }
35176     },this);
35177     
35178 };
35179
35180 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35181
35182 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35183     
35184     parent : false, // this might point to a 'nest' or a ???
35185     
35186     /**
35187      * Creates and adds a new region if it doesn't already exist.
35188      * @param {String} target The target region key (north, south, east, west or center).
35189      * @param {Object} config The regions config object
35190      * @return {BorderLayoutRegion} The new region
35191      */
35192     addRegion : function(config)
35193     {
35194         if(!this.regions[config.region]){
35195             var r = this.factory(config);
35196             this.bindRegion(r);
35197         }
35198         return this.regions[config.region];
35199     },
35200
35201     // private (kinda)
35202     bindRegion : function(r){
35203         this.regions[r.config.region] = r;
35204         
35205         r.on("visibilitychange",    this.layout, this);
35206         r.on("paneladded",          this.layout, this);
35207         r.on("panelremoved",        this.layout, this);
35208         r.on("invalidated",         this.layout, this);
35209         r.on("resized",             this.onRegionResized, this);
35210         r.on("collapsed",           this.onRegionCollapsed, this);
35211         r.on("expanded",            this.onRegionExpanded, this);
35212     },
35213
35214     /**
35215      * Performs a layout update.
35216      */
35217     layout : function()
35218     {
35219         if(this.updating) {
35220             return;
35221         }
35222         
35223         // render all the rebions if they have not been done alreayd?
35224         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35225             if(this.regions[region] && !this.regions[region].bodyEl){
35226                 this.regions[region].onRender(this.el)
35227             }
35228         },this);
35229         
35230         var size = this.getViewSize();
35231         var w = size.width;
35232         var h = size.height;
35233         var centerW = w;
35234         var centerH = h;
35235         var centerY = 0;
35236         var centerX = 0;
35237         //var x = 0, y = 0;
35238
35239         var rs = this.regions;
35240         var north = rs["north"];
35241         var south = rs["south"]; 
35242         var west = rs["west"];
35243         var east = rs["east"];
35244         var center = rs["center"];
35245         //if(this.hideOnLayout){ // not supported anymore
35246             //c.el.setStyle("display", "none");
35247         //}
35248         if(north && north.isVisible()){
35249             var b = north.getBox();
35250             var m = north.getMargins();
35251             b.width = w - (m.left+m.right);
35252             b.x = m.left;
35253             b.y = m.top;
35254             centerY = b.height + b.y + m.bottom;
35255             centerH -= centerY;
35256             north.updateBox(this.safeBox(b));
35257         }
35258         if(south && south.isVisible()){
35259             var b = south.getBox();
35260             var m = south.getMargins();
35261             b.width = w - (m.left+m.right);
35262             b.x = m.left;
35263             var totalHeight = (b.height + m.top + m.bottom);
35264             b.y = h - totalHeight + m.top;
35265             centerH -= totalHeight;
35266             south.updateBox(this.safeBox(b));
35267         }
35268         if(west && west.isVisible()){
35269             var b = west.getBox();
35270             var m = west.getMargins();
35271             b.height = centerH - (m.top+m.bottom);
35272             b.x = m.left;
35273             b.y = centerY + m.top;
35274             var totalWidth = (b.width + m.left + m.right);
35275             centerX += totalWidth;
35276             centerW -= totalWidth;
35277             west.updateBox(this.safeBox(b));
35278         }
35279         if(east && east.isVisible()){
35280             var b = east.getBox();
35281             var m = east.getMargins();
35282             b.height = centerH - (m.top+m.bottom);
35283             var totalWidth = (b.width + m.left + m.right);
35284             b.x = w - totalWidth + m.left;
35285             b.y = centerY + m.top;
35286             centerW -= totalWidth;
35287             east.updateBox(this.safeBox(b));
35288         }
35289         if(center){
35290             var m = center.getMargins();
35291             var centerBox = {
35292                 x: centerX + m.left,
35293                 y: centerY + m.top,
35294                 width: centerW - (m.left+m.right),
35295                 height: centerH - (m.top+m.bottom)
35296             };
35297             //if(this.hideOnLayout){
35298                 //center.el.setStyle("display", "block");
35299             //}
35300             center.updateBox(this.safeBox(centerBox));
35301         }
35302         this.el.repaint();
35303         this.fireEvent("layout", this);
35304     },
35305
35306     // private
35307     safeBox : function(box){
35308         box.width = Math.max(0, box.width);
35309         box.height = Math.max(0, box.height);
35310         return box;
35311     },
35312
35313     /**
35314      * Adds a ContentPanel (or subclass) to this layout.
35315      * @param {String} target The target region key (north, south, east, west or center).
35316      * @param {Roo.ContentPanel} panel The panel to add
35317      * @return {Roo.ContentPanel} The added panel
35318      */
35319     add : function(target, panel){
35320          
35321         target = target.toLowerCase();
35322         return this.regions[target].add(panel);
35323     },
35324
35325     /**
35326      * Remove a ContentPanel (or subclass) to this layout.
35327      * @param {String} target The target region key (north, south, east, west or center).
35328      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35329      * @return {Roo.ContentPanel} The removed panel
35330      */
35331     remove : function(target, panel){
35332         target = target.toLowerCase();
35333         return this.regions[target].remove(panel);
35334     },
35335
35336     /**
35337      * Searches all regions for a panel with the specified id
35338      * @param {String} panelId
35339      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35340      */
35341     findPanel : function(panelId){
35342         var rs = this.regions;
35343         for(var target in rs){
35344             if(typeof rs[target] != "function"){
35345                 var p = rs[target].getPanel(panelId);
35346                 if(p){
35347                     return p;
35348                 }
35349             }
35350         }
35351         return null;
35352     },
35353
35354     /**
35355      * Searches all regions for a panel with the specified id and activates (shows) it.
35356      * @param {String/ContentPanel} panelId The panels id or the panel itself
35357      * @return {Roo.ContentPanel} The shown panel or null
35358      */
35359     showPanel : function(panelId) {
35360       var rs = this.regions;
35361       for(var target in rs){
35362          var r = rs[target];
35363          if(typeof r != "function"){
35364             if(r.hasPanel(panelId)){
35365                return r.showPanel(panelId);
35366             }
35367          }
35368       }
35369       return null;
35370    },
35371
35372    /**
35373      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35374      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35375      */
35376    /*
35377     restoreState : function(provider){
35378         if(!provider){
35379             provider = Roo.state.Manager;
35380         }
35381         var sm = new Roo.LayoutStateManager();
35382         sm.init(this, provider);
35383     },
35384 */
35385  
35386  
35387     /**
35388      * Adds a xtype elements to the layout.
35389      * <pre><code>
35390
35391 layout.addxtype({
35392        xtype : 'ContentPanel',
35393        region: 'west',
35394        items: [ .... ]
35395    }
35396 );
35397
35398 layout.addxtype({
35399         xtype : 'NestedLayoutPanel',
35400         region: 'west',
35401         layout: {
35402            center: { },
35403            west: { }   
35404         },
35405         items : [ ... list of content panels or nested layout panels.. ]
35406    }
35407 );
35408 </code></pre>
35409      * @param {Object} cfg Xtype definition of item to add.
35410      */
35411     addxtype : function(cfg)
35412     {
35413         // basically accepts a pannel...
35414         // can accept a layout region..!?!?
35415         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35416         
35417         
35418         // theory?  children can only be panels??
35419         
35420         //if (!cfg.xtype.match(/Panel$/)) {
35421         //    return false;
35422         //}
35423         var ret = false;
35424         
35425         if (typeof(cfg.region) == 'undefined') {
35426             Roo.log("Failed to add Panel, region was not set");
35427             Roo.log(cfg);
35428             return false;
35429         }
35430         var region = cfg.region;
35431         delete cfg.region;
35432         
35433           
35434         var xitems = [];
35435         if (cfg.items) {
35436             xitems = cfg.items;
35437             delete cfg.items;
35438         }
35439         var nb = false;
35440         
35441         if ( region == 'center') {
35442             Roo.log("Center: " + cfg.title);
35443         }
35444         
35445         
35446         switch(cfg.xtype) 
35447         {
35448             case 'Content':  // ContentPanel (el, cfg)
35449             case 'Scroll':  // ContentPanel (el, cfg)
35450             case 'View': 
35451                 cfg.autoCreate = cfg.autoCreate || true;
35452                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35453                 //} else {
35454                 //    var el = this.el.createChild();
35455                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35456                 //}
35457                 
35458                 this.add(region, ret);
35459                 break;
35460             
35461             /*
35462             case 'TreePanel': // our new panel!
35463                 cfg.el = this.el.createChild();
35464                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35465                 this.add(region, ret);
35466                 break;
35467             */
35468             
35469             case 'Nest': 
35470                 // create a new Layout (which is  a Border Layout...
35471                 
35472                 var clayout = cfg.layout;
35473                 clayout.el  = this.el.createChild();
35474                 clayout.items   = clayout.items  || [];
35475                 
35476                 delete cfg.layout;
35477                 
35478                 // replace this exitems with the clayout ones..
35479                 xitems = clayout.items;
35480                  
35481                 // force background off if it's in center...
35482                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35483                     cfg.background = false;
35484                 }
35485                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35486                 
35487                 
35488                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35489                 //console.log('adding nested layout panel '  + cfg.toSource());
35490                 this.add(region, ret);
35491                 nb = {}; /// find first...
35492                 break;
35493             
35494             case 'Grid':
35495                 
35496                 // needs grid and region
35497                 
35498                 //var el = this.getRegion(region).el.createChild();
35499                 /*
35500                  *var el = this.el.createChild();
35501                 // create the grid first...
35502                 cfg.grid.container = el;
35503                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35504                 */
35505                 
35506                 if (region == 'center' && this.active ) {
35507                     cfg.background = false;
35508                 }
35509                 
35510                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35511                 
35512                 this.add(region, ret);
35513                 /*
35514                 if (cfg.background) {
35515                     // render grid on panel activation (if panel background)
35516                     ret.on('activate', function(gp) {
35517                         if (!gp.grid.rendered) {
35518                     //        gp.grid.render(el);
35519                         }
35520                     });
35521                 } else {
35522                   //  cfg.grid.render(el);
35523                 }
35524                 */
35525                 break;
35526            
35527            
35528             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35529                 // it was the old xcomponent building that caused this before.
35530                 // espeically if border is the top element in the tree.
35531                 ret = this;
35532                 break; 
35533                 
35534                     
35535                 
35536                 
35537                 
35538             default:
35539                 /*
35540                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35541                     
35542                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35543                     this.add(region, ret);
35544                 } else {
35545                 */
35546                     Roo.log(cfg);
35547                     throw "Can not add '" + cfg.xtype + "' to Border";
35548                     return null;
35549              
35550                                 
35551              
35552         }
35553         this.beginUpdate();
35554         // add children..
35555         var region = '';
35556         var abn = {};
35557         Roo.each(xitems, function(i)  {
35558             region = nb && i.region ? i.region : false;
35559             
35560             var add = ret.addxtype(i);
35561            
35562             if (region) {
35563                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35564                 if (!i.background) {
35565                     abn[region] = nb[region] ;
35566                 }
35567             }
35568             
35569         });
35570         this.endUpdate();
35571
35572         // make the last non-background panel active..
35573         //if (nb) { Roo.log(abn); }
35574         if (nb) {
35575             
35576             for(var r in abn) {
35577                 region = this.getRegion(r);
35578                 if (region) {
35579                     // tried using nb[r], but it does not work..
35580                      
35581                     region.showPanel(abn[r]);
35582                    
35583                 }
35584             }
35585         }
35586         return ret;
35587         
35588     },
35589     
35590     
35591 // private
35592     factory : function(cfg)
35593     {
35594         
35595         var validRegions = Roo.bootstrap.layout.Border.regions;
35596
35597         var target = cfg.region;
35598         cfg.mgr = this;
35599         
35600         var r = Roo.bootstrap.layout;
35601         Roo.log(target);
35602         switch(target){
35603             case "north":
35604                 return new r.North(cfg);
35605             case "south":
35606                 return new r.South(cfg);
35607             case "east":
35608                 return new r.East(cfg);
35609             case "west":
35610                 return new r.West(cfg);
35611             case "center":
35612                 return new r.Center(cfg);
35613         }
35614         throw 'Layout region "'+target+'" not supported.';
35615     }
35616     
35617     
35618 });
35619  /*
35620  * Based on:
35621  * Ext JS Library 1.1.1
35622  * Copyright(c) 2006-2007, Ext JS, LLC.
35623  *
35624  * Originally Released Under LGPL - original licence link has changed is not relivant.
35625  *
35626  * Fork - LGPL
35627  * <script type="text/javascript">
35628  */
35629  
35630 /**
35631  * @class Roo.bootstrap.layout.Basic
35632  * @extends Roo.util.Observable
35633  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35634  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35635  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35636  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35637  * @cfg {string}   region  the region that it inhabits..
35638  * @cfg {bool}   skipConfig skip config?
35639  * 
35640
35641  */
35642 Roo.bootstrap.layout.Basic = function(config){
35643     
35644     this.mgr = config.mgr;
35645     
35646     this.position = config.region;
35647     
35648     var skipConfig = config.skipConfig;
35649     
35650     this.events = {
35651         /**
35652          * @scope Roo.BasicLayoutRegion
35653          */
35654         
35655         /**
35656          * @event beforeremove
35657          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35658          * @param {Roo.LayoutRegion} this
35659          * @param {Roo.ContentPanel} panel The panel
35660          * @param {Object} e The cancel event object
35661          */
35662         "beforeremove" : true,
35663         /**
35664          * @event invalidated
35665          * Fires when the layout for this region is changed.
35666          * @param {Roo.LayoutRegion} this
35667          */
35668         "invalidated" : true,
35669         /**
35670          * @event visibilitychange
35671          * Fires when this region is shown or hidden 
35672          * @param {Roo.LayoutRegion} this
35673          * @param {Boolean} visibility true or false
35674          */
35675         "visibilitychange" : true,
35676         /**
35677          * @event paneladded
35678          * Fires when a panel is added. 
35679          * @param {Roo.LayoutRegion} this
35680          * @param {Roo.ContentPanel} panel The panel
35681          */
35682         "paneladded" : true,
35683         /**
35684          * @event panelremoved
35685          * Fires when a panel is removed. 
35686          * @param {Roo.LayoutRegion} this
35687          * @param {Roo.ContentPanel} panel The panel
35688          */
35689         "panelremoved" : true,
35690         /**
35691          * @event beforecollapse
35692          * Fires when this region before collapse.
35693          * @param {Roo.LayoutRegion} this
35694          */
35695         "beforecollapse" : true,
35696         /**
35697          * @event collapsed
35698          * Fires when this region is collapsed.
35699          * @param {Roo.LayoutRegion} this
35700          */
35701         "collapsed" : true,
35702         /**
35703          * @event expanded
35704          * Fires when this region is expanded.
35705          * @param {Roo.LayoutRegion} this
35706          */
35707         "expanded" : true,
35708         /**
35709          * @event slideshow
35710          * Fires when this region is slid into view.
35711          * @param {Roo.LayoutRegion} this
35712          */
35713         "slideshow" : true,
35714         /**
35715          * @event slidehide
35716          * Fires when this region slides out of view. 
35717          * @param {Roo.LayoutRegion} this
35718          */
35719         "slidehide" : true,
35720         /**
35721          * @event panelactivated
35722          * Fires when a panel is activated. 
35723          * @param {Roo.LayoutRegion} this
35724          * @param {Roo.ContentPanel} panel The activated panel
35725          */
35726         "panelactivated" : true,
35727         /**
35728          * @event resized
35729          * Fires when the user resizes this region. 
35730          * @param {Roo.LayoutRegion} this
35731          * @param {Number} newSize The new size (width for east/west, height for north/south)
35732          */
35733         "resized" : true
35734     };
35735     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35736     this.panels = new Roo.util.MixedCollection();
35737     this.panels.getKey = this.getPanelId.createDelegate(this);
35738     this.box = null;
35739     this.activePanel = null;
35740     // ensure listeners are added...
35741     
35742     if (config.listeners || config.events) {
35743         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35744             listeners : config.listeners || {},
35745             events : config.events || {}
35746         });
35747     }
35748     
35749     if(skipConfig !== true){
35750         this.applyConfig(config);
35751     }
35752 };
35753
35754 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35755 {
35756     getPanelId : function(p){
35757         return p.getId();
35758     },
35759     
35760     applyConfig : function(config){
35761         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35762         this.config = config;
35763         
35764     },
35765     
35766     /**
35767      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35768      * the width, for horizontal (north, south) the height.
35769      * @param {Number} newSize The new width or height
35770      */
35771     resizeTo : function(newSize){
35772         var el = this.el ? this.el :
35773                  (this.activePanel ? this.activePanel.getEl() : null);
35774         if(el){
35775             switch(this.position){
35776                 case "east":
35777                 case "west":
35778                     el.setWidth(newSize);
35779                     this.fireEvent("resized", this, newSize);
35780                 break;
35781                 case "north":
35782                 case "south":
35783                     el.setHeight(newSize);
35784                     this.fireEvent("resized", this, newSize);
35785                 break;                
35786             }
35787         }
35788     },
35789     
35790     getBox : function(){
35791         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35792     },
35793     
35794     getMargins : function(){
35795         return this.margins;
35796     },
35797     
35798     updateBox : function(box){
35799         this.box = box;
35800         var el = this.activePanel.getEl();
35801         el.dom.style.left = box.x + "px";
35802         el.dom.style.top = box.y + "px";
35803         this.activePanel.setSize(box.width, box.height);
35804     },
35805     
35806     /**
35807      * Returns the container element for this region.
35808      * @return {Roo.Element}
35809      */
35810     getEl : function(){
35811         return this.activePanel;
35812     },
35813     
35814     /**
35815      * Returns true if this region is currently visible.
35816      * @return {Boolean}
35817      */
35818     isVisible : function(){
35819         return this.activePanel ? true : false;
35820     },
35821     
35822     setActivePanel : function(panel){
35823         panel = this.getPanel(panel);
35824         if(this.activePanel && this.activePanel != panel){
35825             this.activePanel.setActiveState(false);
35826             this.activePanel.getEl().setLeftTop(-10000,-10000);
35827         }
35828         this.activePanel = panel;
35829         panel.setActiveState(true);
35830         if(this.box){
35831             panel.setSize(this.box.width, this.box.height);
35832         }
35833         this.fireEvent("panelactivated", this, panel);
35834         this.fireEvent("invalidated");
35835     },
35836     
35837     /**
35838      * Show the specified panel.
35839      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35840      * @return {Roo.ContentPanel} The shown panel or null
35841      */
35842     showPanel : function(panel){
35843         panel = this.getPanel(panel);
35844         if(panel){
35845             this.setActivePanel(panel);
35846         }
35847         return panel;
35848     },
35849     
35850     /**
35851      * Get the active panel for this region.
35852      * @return {Roo.ContentPanel} The active panel or null
35853      */
35854     getActivePanel : function(){
35855         return this.activePanel;
35856     },
35857     
35858     /**
35859      * Add the passed ContentPanel(s)
35860      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35861      * @return {Roo.ContentPanel} The panel added (if only one was added)
35862      */
35863     add : function(panel){
35864         if(arguments.length > 1){
35865             for(var i = 0, len = arguments.length; i < len; i++) {
35866                 this.add(arguments[i]);
35867             }
35868             return null;
35869         }
35870         if(this.hasPanel(panel)){
35871             this.showPanel(panel);
35872             return panel;
35873         }
35874         var el = panel.getEl();
35875         if(el.dom.parentNode != this.mgr.el.dom){
35876             this.mgr.el.dom.appendChild(el.dom);
35877         }
35878         if(panel.setRegion){
35879             panel.setRegion(this);
35880         }
35881         this.panels.add(panel);
35882         el.setStyle("position", "absolute");
35883         if(!panel.background){
35884             this.setActivePanel(panel);
35885             if(this.config.initialSize && this.panels.getCount()==1){
35886                 this.resizeTo(this.config.initialSize);
35887             }
35888         }
35889         this.fireEvent("paneladded", this, panel);
35890         return panel;
35891     },
35892     
35893     /**
35894      * Returns true if the panel is in this region.
35895      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35896      * @return {Boolean}
35897      */
35898     hasPanel : function(panel){
35899         if(typeof panel == "object"){ // must be panel obj
35900             panel = panel.getId();
35901         }
35902         return this.getPanel(panel) ? true : false;
35903     },
35904     
35905     /**
35906      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35907      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35908      * @param {Boolean} preservePanel Overrides the config preservePanel option
35909      * @return {Roo.ContentPanel} The panel that was removed
35910      */
35911     remove : function(panel, preservePanel){
35912         panel = this.getPanel(panel);
35913         if(!panel){
35914             return null;
35915         }
35916         var e = {};
35917         this.fireEvent("beforeremove", this, panel, e);
35918         if(e.cancel === true){
35919             return null;
35920         }
35921         var panelId = panel.getId();
35922         this.panels.removeKey(panelId);
35923         return panel;
35924     },
35925     
35926     /**
35927      * Returns the panel specified or null if it's not in this region.
35928      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35929      * @return {Roo.ContentPanel}
35930      */
35931     getPanel : function(id){
35932         if(typeof id == "object"){ // must be panel obj
35933             return id;
35934         }
35935         return this.panels.get(id);
35936     },
35937     
35938     /**
35939      * Returns this regions position (north/south/east/west/center).
35940      * @return {String} 
35941      */
35942     getPosition: function(){
35943         return this.position;    
35944     }
35945 });/*
35946  * Based on:
35947  * Ext JS Library 1.1.1
35948  * Copyright(c) 2006-2007, Ext JS, LLC.
35949  *
35950  * Originally Released Under LGPL - original licence link has changed is not relivant.
35951  *
35952  * Fork - LGPL
35953  * <script type="text/javascript">
35954  */
35955  
35956 /**
35957  * @class Roo.bootstrap.layout.Region
35958  * @extends Roo.bootstrap.layout.Basic
35959  * This class represents a region in a layout manager.
35960  
35961  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35962  * @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})
35963  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35964  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35965  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35966  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35967  * @cfg {String}    title           The title for the region (overrides panel titles)
35968  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35969  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35970  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35971  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35972  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35973  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35974  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35975  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35976  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35977  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35978
35979  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35980  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35981  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35982  * @cfg {Number}    width           For East/West panels
35983  * @cfg {Number}    height          For North/South panels
35984  * @cfg {Boolean}   split           To show the splitter
35985  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35986  * 
35987  * @cfg {string}   cls             Extra CSS classes to add to region
35988  * 
35989  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35990  * @cfg {string}   region  the region that it inhabits..
35991  *
35992
35993  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35994  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35995
35996  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35997  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35998  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35999  */
36000 Roo.bootstrap.layout.Region = function(config)
36001 {
36002     this.applyConfig(config);
36003
36004     var mgr = config.mgr;
36005     var pos = config.region;
36006     config.skipConfig = true;
36007     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36008     
36009     if (mgr.el) {
36010         this.onRender(mgr.el);   
36011     }
36012      
36013     this.visible = true;
36014     this.collapsed = false;
36015     this.unrendered_panels = [];
36016 };
36017
36018 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36019
36020     position: '', // set by wrapper (eg. north/south etc..)
36021     unrendered_panels : null,  // unrendered panels.
36022     
36023     tabPosition : false,
36024     
36025     mgr: false, // points to 'Border'
36026     
36027     
36028     createBody : function(){
36029         /** This region's body element 
36030         * @type Roo.Element */
36031         this.bodyEl = this.el.createChild({
36032                 tag: "div",
36033                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36034         });
36035     },
36036
36037     onRender: function(ctr, pos)
36038     {
36039         var dh = Roo.DomHelper;
36040         /** This region's container element 
36041         * @type Roo.Element */
36042         this.el = dh.append(ctr.dom, {
36043                 tag: "div",
36044                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36045             }, true);
36046         /** This region's title element 
36047         * @type Roo.Element */
36048     
36049         this.titleEl = dh.append(this.el.dom,  {
36050                 tag: "div",
36051                 unselectable: "on",
36052                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36053                 children:[
36054                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36055                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36056                 ]
36057             }, true);
36058         
36059         this.titleEl.enableDisplayMode();
36060         /** This region's title text element 
36061         * @type HTMLElement */
36062         this.titleTextEl = this.titleEl.dom.firstChild;
36063         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36064         /*
36065         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36066         this.closeBtn.enableDisplayMode();
36067         this.closeBtn.on("click", this.closeClicked, this);
36068         this.closeBtn.hide();
36069     */
36070         this.createBody(this.config);
36071         if(this.config.hideWhenEmpty){
36072             this.hide();
36073             this.on("paneladded", this.validateVisibility, this);
36074             this.on("panelremoved", this.validateVisibility, this);
36075         }
36076         if(this.autoScroll){
36077             this.bodyEl.setStyle("overflow", "auto");
36078         }else{
36079             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36080         }
36081         //if(c.titlebar !== false){
36082             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36083                 this.titleEl.hide();
36084             }else{
36085                 this.titleEl.show();
36086                 if(this.config.title){
36087                     this.titleTextEl.innerHTML = this.config.title;
36088                 }
36089             }
36090         //}
36091         if(this.config.collapsed){
36092             this.collapse(true);
36093         }
36094         if(this.config.hidden){
36095             this.hide();
36096         }
36097         
36098         if (this.unrendered_panels && this.unrendered_panels.length) {
36099             for (var i =0;i< this.unrendered_panels.length; i++) {
36100                 this.add(this.unrendered_panels[i]);
36101             }
36102             this.unrendered_panels = null;
36103             
36104         }
36105         
36106     },
36107     
36108     applyConfig : function(c)
36109     {
36110         /*
36111          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36112             var dh = Roo.DomHelper;
36113             if(c.titlebar !== false){
36114                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36115                 this.collapseBtn.on("click", this.collapse, this);
36116                 this.collapseBtn.enableDisplayMode();
36117                 /*
36118                 if(c.showPin === true || this.showPin){
36119                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36120                     this.stickBtn.enableDisplayMode();
36121                     this.stickBtn.on("click", this.expand, this);
36122                     this.stickBtn.hide();
36123                 }
36124                 
36125             }
36126             */
36127             /** This region's collapsed element
36128             * @type Roo.Element */
36129             /*
36130              *
36131             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36132                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36133             ]}, true);
36134             
36135             if(c.floatable !== false){
36136                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36137                this.collapsedEl.on("click", this.collapseClick, this);
36138             }
36139
36140             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36141                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36142                    id: "message", unselectable: "on", style:{"float":"left"}});
36143                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36144              }
36145             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36146             this.expandBtn.on("click", this.expand, this);
36147             
36148         }
36149         
36150         if(this.collapseBtn){
36151             this.collapseBtn.setVisible(c.collapsible == true);
36152         }
36153         
36154         this.cmargins = c.cmargins || this.cmargins ||
36155                          (this.position == "west" || this.position == "east" ?
36156                              {top: 0, left: 2, right:2, bottom: 0} :
36157                              {top: 2, left: 0, right:0, bottom: 2});
36158         */
36159         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36160         
36161         
36162         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36163         
36164         this.autoScroll = c.autoScroll || false;
36165         
36166         
36167        
36168         
36169         this.duration = c.duration || .30;
36170         this.slideDuration = c.slideDuration || .45;
36171         this.config = c;
36172        
36173     },
36174     /**
36175      * Returns true if this region is currently visible.
36176      * @return {Boolean}
36177      */
36178     isVisible : function(){
36179         return this.visible;
36180     },
36181
36182     /**
36183      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36184      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36185      */
36186     //setCollapsedTitle : function(title){
36187     //    title = title || "&#160;";
36188      //   if(this.collapsedTitleTextEl){
36189       //      this.collapsedTitleTextEl.innerHTML = title;
36190        // }
36191     //},
36192
36193     getBox : function(){
36194         var b;
36195       //  if(!this.collapsed){
36196             b = this.el.getBox(false, true);
36197        // }else{
36198           //  b = this.collapsedEl.getBox(false, true);
36199         //}
36200         return b;
36201     },
36202
36203     getMargins : function(){
36204         return this.margins;
36205         //return this.collapsed ? this.cmargins : this.margins;
36206     },
36207 /*
36208     highlight : function(){
36209         this.el.addClass("x-layout-panel-dragover");
36210     },
36211
36212     unhighlight : function(){
36213         this.el.removeClass("x-layout-panel-dragover");
36214     },
36215 */
36216     updateBox : function(box)
36217     {
36218         if (!this.bodyEl) {
36219             return; // not rendered yet..
36220         }
36221         
36222         this.box = box;
36223         if(!this.collapsed){
36224             this.el.dom.style.left = box.x + "px";
36225             this.el.dom.style.top = box.y + "px";
36226             this.updateBody(box.width, box.height);
36227         }else{
36228             this.collapsedEl.dom.style.left = box.x + "px";
36229             this.collapsedEl.dom.style.top = box.y + "px";
36230             this.collapsedEl.setSize(box.width, box.height);
36231         }
36232         if(this.tabs){
36233             this.tabs.autoSizeTabs();
36234         }
36235     },
36236
36237     updateBody : function(w, h)
36238     {
36239         if(w !== null){
36240             this.el.setWidth(w);
36241             w -= this.el.getBorderWidth("rl");
36242             if(this.config.adjustments){
36243                 w += this.config.adjustments[0];
36244             }
36245         }
36246         if(h !== null && h > 0){
36247             this.el.setHeight(h);
36248             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36249             h -= this.el.getBorderWidth("tb");
36250             if(this.config.adjustments){
36251                 h += this.config.adjustments[1];
36252             }
36253             this.bodyEl.setHeight(h);
36254             if(this.tabs){
36255                 h = this.tabs.syncHeight(h);
36256             }
36257         }
36258         if(this.panelSize){
36259             w = w !== null ? w : this.panelSize.width;
36260             h = h !== null ? h : this.panelSize.height;
36261         }
36262         if(this.activePanel){
36263             var el = this.activePanel.getEl();
36264             w = w !== null ? w : el.getWidth();
36265             h = h !== null ? h : el.getHeight();
36266             this.panelSize = {width: w, height: h};
36267             this.activePanel.setSize(w, h);
36268         }
36269         if(Roo.isIE && this.tabs){
36270             this.tabs.el.repaint();
36271         }
36272     },
36273
36274     /**
36275      * Returns the container element for this region.
36276      * @return {Roo.Element}
36277      */
36278     getEl : function(){
36279         return this.el;
36280     },
36281
36282     /**
36283      * Hides this region.
36284      */
36285     hide : function(){
36286         //if(!this.collapsed){
36287             this.el.dom.style.left = "-2000px";
36288             this.el.hide();
36289         //}else{
36290          //   this.collapsedEl.dom.style.left = "-2000px";
36291          //   this.collapsedEl.hide();
36292        // }
36293         this.visible = false;
36294         this.fireEvent("visibilitychange", this, false);
36295     },
36296
36297     /**
36298      * Shows this region if it was previously hidden.
36299      */
36300     show : function(){
36301         //if(!this.collapsed){
36302             this.el.show();
36303         //}else{
36304         //    this.collapsedEl.show();
36305        // }
36306         this.visible = true;
36307         this.fireEvent("visibilitychange", this, true);
36308     },
36309 /*
36310     closeClicked : function(){
36311         if(this.activePanel){
36312             this.remove(this.activePanel);
36313         }
36314     },
36315
36316     collapseClick : function(e){
36317         if(this.isSlid){
36318            e.stopPropagation();
36319            this.slideIn();
36320         }else{
36321            e.stopPropagation();
36322            this.slideOut();
36323         }
36324     },
36325 */
36326     /**
36327      * Collapses this region.
36328      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36329      */
36330     /*
36331     collapse : function(skipAnim, skipCheck = false){
36332         if(this.collapsed) {
36333             return;
36334         }
36335         
36336         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36337             
36338             this.collapsed = true;
36339             if(this.split){
36340                 this.split.el.hide();
36341             }
36342             if(this.config.animate && skipAnim !== true){
36343                 this.fireEvent("invalidated", this);
36344                 this.animateCollapse();
36345             }else{
36346                 this.el.setLocation(-20000,-20000);
36347                 this.el.hide();
36348                 this.collapsedEl.show();
36349                 this.fireEvent("collapsed", this);
36350                 this.fireEvent("invalidated", this);
36351             }
36352         }
36353         
36354     },
36355 */
36356     animateCollapse : function(){
36357         // overridden
36358     },
36359
36360     /**
36361      * Expands this region if it was previously collapsed.
36362      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36363      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36364      */
36365     /*
36366     expand : function(e, skipAnim){
36367         if(e) {
36368             e.stopPropagation();
36369         }
36370         if(!this.collapsed || this.el.hasActiveFx()) {
36371             return;
36372         }
36373         if(this.isSlid){
36374             this.afterSlideIn();
36375             skipAnim = true;
36376         }
36377         this.collapsed = false;
36378         if(this.config.animate && skipAnim !== true){
36379             this.animateExpand();
36380         }else{
36381             this.el.show();
36382             if(this.split){
36383                 this.split.el.show();
36384             }
36385             this.collapsedEl.setLocation(-2000,-2000);
36386             this.collapsedEl.hide();
36387             this.fireEvent("invalidated", this);
36388             this.fireEvent("expanded", this);
36389         }
36390     },
36391 */
36392     animateExpand : function(){
36393         // overridden
36394     },
36395
36396     initTabs : function()
36397     {
36398         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36399         
36400         var ts = new Roo.bootstrap.panel.Tabs({
36401             el: this.bodyEl.dom,
36402             region : this,
36403             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36404             disableTooltips: this.config.disableTabTips,
36405             toolbar : this.config.toolbar
36406         });
36407         
36408         if(this.config.hideTabs){
36409             ts.stripWrap.setDisplayed(false);
36410         }
36411         this.tabs = ts;
36412         ts.resizeTabs = this.config.resizeTabs === true;
36413         ts.minTabWidth = this.config.minTabWidth || 40;
36414         ts.maxTabWidth = this.config.maxTabWidth || 250;
36415         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36416         ts.monitorResize = false;
36417         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36418         ts.bodyEl.addClass('roo-layout-tabs-body');
36419         this.panels.each(this.initPanelAsTab, this);
36420     },
36421
36422     initPanelAsTab : function(panel){
36423         var ti = this.tabs.addTab(
36424             panel.getEl().id,
36425             panel.getTitle(),
36426             null,
36427             this.config.closeOnTab && panel.isClosable(),
36428             panel.tpl
36429         );
36430         if(panel.tabTip !== undefined){
36431             ti.setTooltip(panel.tabTip);
36432         }
36433         ti.on("activate", function(){
36434               this.setActivePanel(panel);
36435         }, this);
36436         
36437         if(this.config.closeOnTab){
36438             ti.on("beforeclose", function(t, e){
36439                 e.cancel = true;
36440                 this.remove(panel);
36441             }, this);
36442         }
36443         
36444         panel.tabItem = ti;
36445         
36446         return ti;
36447     },
36448
36449     updatePanelTitle : function(panel, title)
36450     {
36451         if(this.activePanel == panel){
36452             this.updateTitle(title);
36453         }
36454         if(this.tabs){
36455             var ti = this.tabs.getTab(panel.getEl().id);
36456             ti.setText(title);
36457             if(panel.tabTip !== undefined){
36458                 ti.setTooltip(panel.tabTip);
36459             }
36460         }
36461     },
36462
36463     updateTitle : function(title){
36464         if(this.titleTextEl && !this.config.title){
36465             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36466         }
36467     },
36468
36469     setActivePanel : function(panel)
36470     {
36471         panel = this.getPanel(panel);
36472         if(this.activePanel && this.activePanel != panel){
36473             if(this.activePanel.setActiveState(false) === false){
36474                 return;
36475             }
36476         }
36477         this.activePanel = panel;
36478         panel.setActiveState(true);
36479         if(this.panelSize){
36480             panel.setSize(this.panelSize.width, this.panelSize.height);
36481         }
36482         if(this.closeBtn){
36483             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36484         }
36485         this.updateTitle(panel.getTitle());
36486         if(this.tabs){
36487             this.fireEvent("invalidated", this);
36488         }
36489         this.fireEvent("panelactivated", this, panel);
36490     },
36491
36492     /**
36493      * Shows the specified panel.
36494      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36495      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36496      */
36497     showPanel : function(panel)
36498     {
36499         panel = this.getPanel(panel);
36500         if(panel){
36501             if(this.tabs){
36502                 var tab = this.tabs.getTab(panel.getEl().id);
36503                 if(tab.isHidden()){
36504                     this.tabs.unhideTab(tab.id);
36505                 }
36506                 tab.activate();
36507             }else{
36508                 this.setActivePanel(panel);
36509             }
36510         }
36511         return panel;
36512     },
36513
36514     /**
36515      * Get the active panel for this region.
36516      * @return {Roo.ContentPanel} The active panel or null
36517      */
36518     getActivePanel : function(){
36519         return this.activePanel;
36520     },
36521
36522     validateVisibility : function(){
36523         if(this.panels.getCount() < 1){
36524             this.updateTitle("&#160;");
36525             this.closeBtn.hide();
36526             this.hide();
36527         }else{
36528             if(!this.isVisible()){
36529                 this.show();
36530             }
36531         }
36532     },
36533
36534     /**
36535      * Adds the passed ContentPanel(s) to this region.
36536      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36537      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36538      */
36539     add : function(panel)
36540     {
36541         if(arguments.length > 1){
36542             for(var i = 0, len = arguments.length; i < len; i++) {
36543                 this.add(arguments[i]);
36544             }
36545             return null;
36546         }
36547         
36548         // if we have not been rendered yet, then we can not really do much of this..
36549         if (!this.bodyEl) {
36550             this.unrendered_panels.push(panel);
36551             return panel;
36552         }
36553         
36554         
36555         
36556         
36557         if(this.hasPanel(panel)){
36558             this.showPanel(panel);
36559             return panel;
36560         }
36561         panel.setRegion(this);
36562         this.panels.add(panel);
36563        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36564             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36565             // and hide them... ???
36566             this.bodyEl.dom.appendChild(panel.getEl().dom);
36567             if(panel.background !== true){
36568                 this.setActivePanel(panel);
36569             }
36570             this.fireEvent("paneladded", this, panel);
36571             return panel;
36572         }
36573         */
36574         if(!this.tabs){
36575             this.initTabs();
36576         }else{
36577             this.initPanelAsTab(panel);
36578         }
36579         
36580         
36581         if(panel.background !== true){
36582             this.tabs.activate(panel.getEl().id);
36583         }
36584         this.fireEvent("paneladded", this, panel);
36585         return panel;
36586     },
36587
36588     /**
36589      * Hides the tab for the specified panel.
36590      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36591      */
36592     hidePanel : function(panel){
36593         if(this.tabs && (panel = this.getPanel(panel))){
36594             this.tabs.hideTab(panel.getEl().id);
36595         }
36596     },
36597
36598     /**
36599      * Unhides the tab for a previously hidden panel.
36600      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36601      */
36602     unhidePanel : function(panel){
36603         if(this.tabs && (panel = this.getPanel(panel))){
36604             this.tabs.unhideTab(panel.getEl().id);
36605         }
36606     },
36607
36608     clearPanels : function(){
36609         while(this.panels.getCount() > 0){
36610              this.remove(this.panels.first());
36611         }
36612     },
36613
36614     /**
36615      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36616      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36617      * @param {Boolean} preservePanel Overrides the config preservePanel option
36618      * @return {Roo.ContentPanel} The panel that was removed
36619      */
36620     remove : function(panel, preservePanel)
36621     {
36622         panel = this.getPanel(panel);
36623         if(!panel){
36624             return null;
36625         }
36626         var e = {};
36627         this.fireEvent("beforeremove", this, panel, e);
36628         if(e.cancel === true){
36629             return null;
36630         }
36631         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36632         var panelId = panel.getId();
36633         this.panels.removeKey(panelId);
36634         if(preservePanel){
36635             document.body.appendChild(panel.getEl().dom);
36636         }
36637         if(this.tabs){
36638             this.tabs.removeTab(panel.getEl().id);
36639         }else if (!preservePanel){
36640             this.bodyEl.dom.removeChild(panel.getEl().dom);
36641         }
36642         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36643             var p = this.panels.first();
36644             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36645             tempEl.appendChild(p.getEl().dom);
36646             this.bodyEl.update("");
36647             this.bodyEl.dom.appendChild(p.getEl().dom);
36648             tempEl = null;
36649             this.updateTitle(p.getTitle());
36650             this.tabs = null;
36651             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36652             this.setActivePanel(p);
36653         }
36654         panel.setRegion(null);
36655         if(this.activePanel == panel){
36656             this.activePanel = null;
36657         }
36658         if(this.config.autoDestroy !== false && preservePanel !== true){
36659             try{panel.destroy();}catch(e){}
36660         }
36661         this.fireEvent("panelremoved", this, panel);
36662         return panel;
36663     },
36664
36665     /**
36666      * Returns the TabPanel component used by this region
36667      * @return {Roo.TabPanel}
36668      */
36669     getTabs : function(){
36670         return this.tabs;
36671     },
36672
36673     createTool : function(parentEl, className){
36674         var btn = Roo.DomHelper.append(parentEl, {
36675             tag: "div",
36676             cls: "x-layout-tools-button",
36677             children: [ {
36678                 tag: "div",
36679                 cls: "roo-layout-tools-button-inner " + className,
36680                 html: "&#160;"
36681             }]
36682         }, true);
36683         btn.addClassOnOver("roo-layout-tools-button-over");
36684         return btn;
36685     }
36686 });/*
36687  * Based on:
36688  * Ext JS Library 1.1.1
36689  * Copyright(c) 2006-2007, Ext JS, LLC.
36690  *
36691  * Originally Released Under LGPL - original licence link has changed is not relivant.
36692  *
36693  * Fork - LGPL
36694  * <script type="text/javascript">
36695  */
36696  
36697
36698
36699 /**
36700  * @class Roo.SplitLayoutRegion
36701  * @extends Roo.LayoutRegion
36702  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36703  */
36704 Roo.bootstrap.layout.Split = function(config){
36705     this.cursor = config.cursor;
36706     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36707 };
36708
36709 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36710 {
36711     splitTip : "Drag to resize.",
36712     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36713     useSplitTips : false,
36714
36715     applyConfig : function(config){
36716         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36717     },
36718     
36719     onRender : function(ctr,pos) {
36720         
36721         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36722         if(!this.config.split){
36723             return;
36724         }
36725         if(!this.split){
36726             
36727             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36728                             tag: "div",
36729                             id: this.el.id + "-split",
36730                             cls: "roo-layout-split roo-layout-split-"+this.position,
36731                             html: "&#160;"
36732             });
36733             /** The SplitBar for this region 
36734             * @type Roo.SplitBar */
36735             // does not exist yet...
36736             Roo.log([this.position, this.orientation]);
36737             
36738             this.split = new Roo.bootstrap.SplitBar({
36739                 dragElement : splitEl,
36740                 resizingElement: this.el,
36741                 orientation : this.orientation
36742             });
36743             
36744             this.split.on("moved", this.onSplitMove, this);
36745             this.split.useShim = this.config.useShim === true;
36746             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36747             if(this.useSplitTips){
36748                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36749             }
36750             //if(config.collapsible){
36751             //    this.split.el.on("dblclick", this.collapse,  this);
36752             //}
36753         }
36754         if(typeof this.config.minSize != "undefined"){
36755             this.split.minSize = this.config.minSize;
36756         }
36757         if(typeof this.config.maxSize != "undefined"){
36758             this.split.maxSize = this.config.maxSize;
36759         }
36760         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36761             this.hideSplitter();
36762         }
36763         
36764     },
36765
36766     getHMaxSize : function(){
36767          var cmax = this.config.maxSize || 10000;
36768          var center = this.mgr.getRegion("center");
36769          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36770     },
36771
36772     getVMaxSize : function(){
36773          var cmax = this.config.maxSize || 10000;
36774          var center = this.mgr.getRegion("center");
36775          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36776     },
36777
36778     onSplitMove : function(split, newSize){
36779         this.fireEvent("resized", this, newSize);
36780     },
36781     
36782     /** 
36783      * Returns the {@link Roo.SplitBar} for this region.
36784      * @return {Roo.SplitBar}
36785      */
36786     getSplitBar : function(){
36787         return this.split;
36788     },
36789     
36790     hide : function(){
36791         this.hideSplitter();
36792         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36793     },
36794
36795     hideSplitter : function(){
36796         if(this.split){
36797             this.split.el.setLocation(-2000,-2000);
36798             this.split.el.hide();
36799         }
36800     },
36801
36802     show : function(){
36803         if(this.split){
36804             this.split.el.show();
36805         }
36806         Roo.bootstrap.layout.Split.superclass.show.call(this);
36807     },
36808     
36809     beforeSlide: function(){
36810         if(Roo.isGecko){// firefox overflow auto bug workaround
36811             this.bodyEl.clip();
36812             if(this.tabs) {
36813                 this.tabs.bodyEl.clip();
36814             }
36815             if(this.activePanel){
36816                 this.activePanel.getEl().clip();
36817                 
36818                 if(this.activePanel.beforeSlide){
36819                     this.activePanel.beforeSlide();
36820                 }
36821             }
36822         }
36823     },
36824     
36825     afterSlide : function(){
36826         if(Roo.isGecko){// firefox overflow auto bug workaround
36827             this.bodyEl.unclip();
36828             if(this.tabs) {
36829                 this.tabs.bodyEl.unclip();
36830             }
36831             if(this.activePanel){
36832                 this.activePanel.getEl().unclip();
36833                 if(this.activePanel.afterSlide){
36834                     this.activePanel.afterSlide();
36835                 }
36836             }
36837         }
36838     },
36839
36840     initAutoHide : function(){
36841         if(this.autoHide !== false){
36842             if(!this.autoHideHd){
36843                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36844                 this.autoHideHd = {
36845                     "mouseout": function(e){
36846                         if(!e.within(this.el, true)){
36847                             st.delay(500);
36848                         }
36849                     },
36850                     "mouseover" : function(e){
36851                         st.cancel();
36852                     },
36853                     scope : this
36854                 };
36855             }
36856             this.el.on(this.autoHideHd);
36857         }
36858     },
36859
36860     clearAutoHide : function(){
36861         if(this.autoHide !== false){
36862             this.el.un("mouseout", this.autoHideHd.mouseout);
36863             this.el.un("mouseover", this.autoHideHd.mouseover);
36864         }
36865     },
36866
36867     clearMonitor : function(){
36868         Roo.get(document).un("click", this.slideInIf, this);
36869     },
36870
36871     // these names are backwards but not changed for compat
36872     slideOut : function(){
36873         if(this.isSlid || this.el.hasActiveFx()){
36874             return;
36875         }
36876         this.isSlid = true;
36877         if(this.collapseBtn){
36878             this.collapseBtn.hide();
36879         }
36880         this.closeBtnState = this.closeBtn.getStyle('display');
36881         this.closeBtn.hide();
36882         if(this.stickBtn){
36883             this.stickBtn.show();
36884         }
36885         this.el.show();
36886         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36887         this.beforeSlide();
36888         this.el.setStyle("z-index", 10001);
36889         this.el.slideIn(this.getSlideAnchor(), {
36890             callback: function(){
36891                 this.afterSlide();
36892                 this.initAutoHide();
36893                 Roo.get(document).on("click", this.slideInIf, this);
36894                 this.fireEvent("slideshow", this);
36895             },
36896             scope: this,
36897             block: true
36898         });
36899     },
36900
36901     afterSlideIn : function(){
36902         this.clearAutoHide();
36903         this.isSlid = false;
36904         this.clearMonitor();
36905         this.el.setStyle("z-index", "");
36906         if(this.collapseBtn){
36907             this.collapseBtn.show();
36908         }
36909         this.closeBtn.setStyle('display', this.closeBtnState);
36910         if(this.stickBtn){
36911             this.stickBtn.hide();
36912         }
36913         this.fireEvent("slidehide", this);
36914     },
36915
36916     slideIn : function(cb){
36917         if(!this.isSlid || this.el.hasActiveFx()){
36918             Roo.callback(cb);
36919             return;
36920         }
36921         this.isSlid = false;
36922         this.beforeSlide();
36923         this.el.slideOut(this.getSlideAnchor(), {
36924             callback: function(){
36925                 this.el.setLeftTop(-10000, -10000);
36926                 this.afterSlide();
36927                 this.afterSlideIn();
36928                 Roo.callback(cb);
36929             },
36930             scope: this,
36931             block: true
36932         });
36933     },
36934     
36935     slideInIf : function(e){
36936         if(!e.within(this.el)){
36937             this.slideIn();
36938         }
36939     },
36940
36941     animateCollapse : function(){
36942         this.beforeSlide();
36943         this.el.setStyle("z-index", 20000);
36944         var anchor = this.getSlideAnchor();
36945         this.el.slideOut(anchor, {
36946             callback : function(){
36947                 this.el.setStyle("z-index", "");
36948                 this.collapsedEl.slideIn(anchor, {duration:.3});
36949                 this.afterSlide();
36950                 this.el.setLocation(-10000,-10000);
36951                 this.el.hide();
36952                 this.fireEvent("collapsed", this);
36953             },
36954             scope: this,
36955             block: true
36956         });
36957     },
36958
36959     animateExpand : function(){
36960         this.beforeSlide();
36961         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36962         this.el.setStyle("z-index", 20000);
36963         this.collapsedEl.hide({
36964             duration:.1
36965         });
36966         this.el.slideIn(this.getSlideAnchor(), {
36967             callback : function(){
36968                 this.el.setStyle("z-index", "");
36969                 this.afterSlide();
36970                 if(this.split){
36971                     this.split.el.show();
36972                 }
36973                 this.fireEvent("invalidated", this);
36974                 this.fireEvent("expanded", this);
36975             },
36976             scope: this,
36977             block: true
36978         });
36979     },
36980
36981     anchors : {
36982         "west" : "left",
36983         "east" : "right",
36984         "north" : "top",
36985         "south" : "bottom"
36986     },
36987
36988     sanchors : {
36989         "west" : "l",
36990         "east" : "r",
36991         "north" : "t",
36992         "south" : "b"
36993     },
36994
36995     canchors : {
36996         "west" : "tl-tr",
36997         "east" : "tr-tl",
36998         "north" : "tl-bl",
36999         "south" : "bl-tl"
37000     },
37001
37002     getAnchor : function(){
37003         return this.anchors[this.position];
37004     },
37005
37006     getCollapseAnchor : function(){
37007         return this.canchors[this.position];
37008     },
37009
37010     getSlideAnchor : function(){
37011         return this.sanchors[this.position];
37012     },
37013
37014     getAlignAdj : function(){
37015         var cm = this.cmargins;
37016         switch(this.position){
37017             case "west":
37018                 return [0, 0];
37019             break;
37020             case "east":
37021                 return [0, 0];
37022             break;
37023             case "north":
37024                 return [0, 0];
37025             break;
37026             case "south":
37027                 return [0, 0];
37028             break;
37029         }
37030     },
37031
37032     getExpandAdj : function(){
37033         var c = this.collapsedEl, cm = this.cmargins;
37034         switch(this.position){
37035             case "west":
37036                 return [-(cm.right+c.getWidth()+cm.left), 0];
37037             break;
37038             case "east":
37039                 return [cm.right+c.getWidth()+cm.left, 0];
37040             break;
37041             case "north":
37042                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37043             break;
37044             case "south":
37045                 return [0, cm.top+cm.bottom+c.getHeight()];
37046             break;
37047         }
37048     }
37049 });/*
37050  * Based on:
37051  * Ext JS Library 1.1.1
37052  * Copyright(c) 2006-2007, Ext JS, LLC.
37053  *
37054  * Originally Released Under LGPL - original licence link has changed is not relivant.
37055  *
37056  * Fork - LGPL
37057  * <script type="text/javascript">
37058  */
37059 /*
37060  * These classes are private internal classes
37061  */
37062 Roo.bootstrap.layout.Center = function(config){
37063     config.region = "center";
37064     Roo.bootstrap.layout.Region.call(this, config);
37065     this.visible = true;
37066     this.minWidth = config.minWidth || 20;
37067     this.minHeight = config.minHeight || 20;
37068 };
37069
37070 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37071     hide : function(){
37072         // center panel can't be hidden
37073     },
37074     
37075     show : function(){
37076         // center panel can't be hidden
37077     },
37078     
37079     getMinWidth: function(){
37080         return this.minWidth;
37081     },
37082     
37083     getMinHeight: function(){
37084         return this.minHeight;
37085     }
37086 });
37087
37088
37089
37090
37091  
37092
37093
37094
37095
37096
37097
37098 Roo.bootstrap.layout.North = function(config)
37099 {
37100     config.region = 'north';
37101     config.cursor = 'n-resize';
37102     
37103     Roo.bootstrap.layout.Split.call(this, config);
37104     
37105     
37106     if(this.split){
37107         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37108         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37109         this.split.el.addClass("roo-layout-split-v");
37110     }
37111     var size = config.initialSize || config.height;
37112     if(typeof size != "undefined"){
37113         this.el.setHeight(size);
37114     }
37115 };
37116 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37117 {
37118     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37119     
37120     
37121     
37122     getBox : function(){
37123         if(this.collapsed){
37124             return this.collapsedEl.getBox();
37125         }
37126         var box = this.el.getBox();
37127         if(this.split){
37128             box.height += this.split.el.getHeight();
37129         }
37130         return box;
37131     },
37132     
37133     updateBox : function(box){
37134         if(this.split && !this.collapsed){
37135             box.height -= this.split.el.getHeight();
37136             this.split.el.setLeft(box.x);
37137             this.split.el.setTop(box.y+box.height);
37138             this.split.el.setWidth(box.width);
37139         }
37140         if(this.collapsed){
37141             this.updateBody(box.width, null);
37142         }
37143         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37144     }
37145 });
37146
37147
37148
37149
37150
37151 Roo.bootstrap.layout.South = function(config){
37152     config.region = 'south';
37153     config.cursor = 's-resize';
37154     Roo.bootstrap.layout.Split.call(this, config);
37155     if(this.split){
37156         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37157         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37158         this.split.el.addClass("roo-layout-split-v");
37159     }
37160     var size = config.initialSize || config.height;
37161     if(typeof size != "undefined"){
37162         this.el.setHeight(size);
37163     }
37164 };
37165
37166 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37167     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37168     getBox : function(){
37169         if(this.collapsed){
37170             return this.collapsedEl.getBox();
37171         }
37172         var box = this.el.getBox();
37173         if(this.split){
37174             var sh = this.split.el.getHeight();
37175             box.height += sh;
37176             box.y -= sh;
37177         }
37178         return box;
37179     },
37180     
37181     updateBox : function(box){
37182         if(this.split && !this.collapsed){
37183             var sh = this.split.el.getHeight();
37184             box.height -= sh;
37185             box.y += sh;
37186             this.split.el.setLeft(box.x);
37187             this.split.el.setTop(box.y-sh);
37188             this.split.el.setWidth(box.width);
37189         }
37190         if(this.collapsed){
37191             this.updateBody(box.width, null);
37192         }
37193         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37194     }
37195 });
37196
37197 Roo.bootstrap.layout.East = function(config){
37198     config.region = "east";
37199     config.cursor = "e-resize";
37200     Roo.bootstrap.layout.Split.call(this, config);
37201     if(this.split){
37202         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37203         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37204         this.split.el.addClass("roo-layout-split-h");
37205     }
37206     var size = config.initialSize || config.width;
37207     if(typeof size != "undefined"){
37208         this.el.setWidth(size);
37209     }
37210 };
37211 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37212     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37213     getBox : function(){
37214         if(this.collapsed){
37215             return this.collapsedEl.getBox();
37216         }
37217         var box = this.el.getBox();
37218         if(this.split){
37219             var sw = this.split.el.getWidth();
37220             box.width += sw;
37221             box.x -= sw;
37222         }
37223         return box;
37224     },
37225
37226     updateBox : function(box){
37227         if(this.split && !this.collapsed){
37228             var sw = this.split.el.getWidth();
37229             box.width -= sw;
37230             this.split.el.setLeft(box.x);
37231             this.split.el.setTop(box.y);
37232             this.split.el.setHeight(box.height);
37233             box.x += sw;
37234         }
37235         if(this.collapsed){
37236             this.updateBody(null, box.height);
37237         }
37238         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37239     }
37240 });
37241
37242 Roo.bootstrap.layout.West = function(config){
37243     config.region = "west";
37244     config.cursor = "w-resize";
37245     
37246     Roo.bootstrap.layout.Split.call(this, config);
37247     if(this.split){
37248         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37249         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37250         this.split.el.addClass("roo-layout-split-h");
37251     }
37252     
37253 };
37254 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37255     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37256     
37257     onRender: function(ctr, pos)
37258     {
37259         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37260         var size = this.config.initialSize || this.config.width;
37261         if(typeof size != "undefined"){
37262             this.el.setWidth(size);
37263         }
37264     },
37265     
37266     getBox : function(){
37267         if(this.collapsed){
37268             return this.collapsedEl.getBox();
37269         }
37270         var box = this.el.getBox();
37271         if(this.split){
37272             box.width += this.split.el.getWidth();
37273         }
37274         return box;
37275     },
37276     
37277     updateBox : function(box){
37278         if(this.split && !this.collapsed){
37279             var sw = this.split.el.getWidth();
37280             box.width -= sw;
37281             this.split.el.setLeft(box.x+box.width);
37282             this.split.el.setTop(box.y);
37283             this.split.el.setHeight(box.height);
37284         }
37285         if(this.collapsed){
37286             this.updateBody(null, box.height);
37287         }
37288         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37289     }
37290 });Roo.namespace("Roo.bootstrap.panel");/*
37291  * Based on:
37292  * Ext JS Library 1.1.1
37293  * Copyright(c) 2006-2007, Ext JS, LLC.
37294  *
37295  * Originally Released Under LGPL - original licence link has changed is not relivant.
37296  *
37297  * Fork - LGPL
37298  * <script type="text/javascript">
37299  */
37300 /**
37301  * @class Roo.ContentPanel
37302  * @extends Roo.util.Observable
37303  * A basic ContentPanel element.
37304  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37305  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37306  * @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
37307  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37308  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37309  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37310  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37311  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37312  * @cfg {String} title          The title for this panel
37313  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37314  * @cfg {String} url            Calls {@link #setUrl} with this value
37315  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37316  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37317  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37318  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37319  * @cfg {Boolean} badges render the badges
37320
37321  * @constructor
37322  * Create a new ContentPanel.
37323  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37324  * @param {String/Object} config A string to set only the title or a config object
37325  * @param {String} content (optional) Set the HTML content for this panel
37326  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37327  */
37328 Roo.bootstrap.panel.Content = function( config){
37329     
37330     this.tpl = config.tpl || false;
37331     
37332     var el = config.el;
37333     var content = config.content;
37334
37335     if(config.autoCreate){ // xtype is available if this is called from factory
37336         el = Roo.id();
37337     }
37338     this.el = Roo.get(el);
37339     if(!this.el && config && config.autoCreate){
37340         if(typeof config.autoCreate == "object"){
37341             if(!config.autoCreate.id){
37342                 config.autoCreate.id = config.id||el;
37343             }
37344             this.el = Roo.DomHelper.append(document.body,
37345                         config.autoCreate, true);
37346         }else{
37347             var elcfg =  {   tag: "div",
37348                             cls: "roo-layout-inactive-content",
37349                             id: config.id||el
37350                             };
37351             if (config.html) {
37352                 elcfg.html = config.html;
37353                 
37354             }
37355                         
37356             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37357         }
37358     } 
37359     this.closable = false;
37360     this.loaded = false;
37361     this.active = false;
37362    
37363       
37364     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37365         
37366         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37367         
37368         this.wrapEl = this.el; //this.el.wrap();
37369         var ti = [];
37370         if (config.toolbar.items) {
37371             ti = config.toolbar.items ;
37372             delete config.toolbar.items ;
37373         }
37374         
37375         var nitems = [];
37376         this.toolbar.render(this.wrapEl, 'before');
37377         for(var i =0;i < ti.length;i++) {
37378           //  Roo.log(['add child', items[i]]);
37379             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37380         }
37381         this.toolbar.items = nitems;
37382         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37383         delete config.toolbar;
37384         
37385     }
37386     /*
37387     // xtype created footer. - not sure if will work as we normally have to render first..
37388     if (this.footer && !this.footer.el && this.footer.xtype) {
37389         if (!this.wrapEl) {
37390             this.wrapEl = this.el.wrap();
37391         }
37392     
37393         this.footer.container = this.wrapEl.createChild();
37394          
37395         this.footer = Roo.factory(this.footer, Roo);
37396         
37397     }
37398     */
37399     
37400      if(typeof config == "string"){
37401         this.title = config;
37402     }else{
37403         Roo.apply(this, config);
37404     }
37405     
37406     if(this.resizeEl){
37407         this.resizeEl = Roo.get(this.resizeEl, true);
37408     }else{
37409         this.resizeEl = this.el;
37410     }
37411     // handle view.xtype
37412     
37413  
37414     
37415     
37416     this.addEvents({
37417         /**
37418          * @event activate
37419          * Fires when this panel is activated. 
37420          * @param {Roo.ContentPanel} this
37421          */
37422         "activate" : true,
37423         /**
37424          * @event deactivate
37425          * Fires when this panel is activated. 
37426          * @param {Roo.ContentPanel} this
37427          */
37428         "deactivate" : true,
37429
37430         /**
37431          * @event resize
37432          * Fires when this panel is resized if fitToFrame is true.
37433          * @param {Roo.ContentPanel} this
37434          * @param {Number} width The width after any component adjustments
37435          * @param {Number} height The height after any component adjustments
37436          */
37437         "resize" : true,
37438         
37439          /**
37440          * @event render
37441          * Fires when this tab is created
37442          * @param {Roo.ContentPanel} this
37443          */
37444         "render" : true
37445         
37446         
37447         
37448     });
37449     
37450
37451     
37452     
37453     if(this.autoScroll){
37454         this.resizeEl.setStyle("overflow", "auto");
37455     } else {
37456         // fix randome scrolling
37457         //this.el.on('scroll', function() {
37458         //    Roo.log('fix random scolling');
37459         //    this.scrollTo('top',0); 
37460         //});
37461     }
37462     content = content || this.content;
37463     if(content){
37464         this.setContent(content);
37465     }
37466     if(config && config.url){
37467         this.setUrl(this.url, this.params, this.loadOnce);
37468     }
37469     
37470     
37471     
37472     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37473     
37474     if (this.view && typeof(this.view.xtype) != 'undefined') {
37475         this.view.el = this.el.appendChild(document.createElement("div"));
37476         this.view = Roo.factory(this.view); 
37477         this.view.render  &&  this.view.render(false, '');  
37478     }
37479     
37480     
37481     this.fireEvent('render', this);
37482 };
37483
37484 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37485     
37486     tabTip : '',
37487     
37488     setRegion : function(region){
37489         this.region = region;
37490         this.setActiveClass(region && !this.background);
37491     },
37492     
37493     
37494     setActiveClass: function(state)
37495     {
37496         if(state){
37497            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37498            this.el.setStyle('position','relative');
37499         }else{
37500            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37501            this.el.setStyle('position', 'absolute');
37502         } 
37503     },
37504     
37505     /**
37506      * Returns the toolbar for this Panel if one was configured. 
37507      * @return {Roo.Toolbar} 
37508      */
37509     getToolbar : function(){
37510         return this.toolbar;
37511     },
37512     
37513     setActiveState : function(active)
37514     {
37515         this.active = active;
37516         this.setActiveClass(active);
37517         if(!active){
37518             if(this.fireEvent("deactivate", this) === false){
37519                 return false;
37520             }
37521             return true;
37522         }
37523         this.fireEvent("activate", this);
37524         return true;
37525     },
37526     /**
37527      * Updates this panel's element
37528      * @param {String} content The new content
37529      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37530     */
37531     setContent : function(content, loadScripts){
37532         this.el.update(content, loadScripts);
37533     },
37534
37535     ignoreResize : function(w, h){
37536         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37537             return true;
37538         }else{
37539             this.lastSize = {width: w, height: h};
37540             return false;
37541         }
37542     },
37543     /**
37544      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37545      * @return {Roo.UpdateManager} The UpdateManager
37546      */
37547     getUpdateManager : function(){
37548         return this.el.getUpdateManager();
37549     },
37550      /**
37551      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37552      * @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:
37553 <pre><code>
37554 panel.load({
37555     url: "your-url.php",
37556     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37557     callback: yourFunction,
37558     scope: yourObject, //(optional scope)
37559     discardUrl: false,
37560     nocache: false,
37561     text: "Loading...",
37562     timeout: 30,
37563     scripts: false
37564 });
37565 </code></pre>
37566      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37567      * 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.
37568      * @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}
37569      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37570      * @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.
37571      * @return {Roo.ContentPanel} this
37572      */
37573     load : function(){
37574         var um = this.el.getUpdateManager();
37575         um.update.apply(um, arguments);
37576         return this;
37577     },
37578
37579
37580     /**
37581      * 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.
37582      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37583      * @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)
37584      * @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)
37585      * @return {Roo.UpdateManager} The UpdateManager
37586      */
37587     setUrl : function(url, params, loadOnce){
37588         if(this.refreshDelegate){
37589             this.removeListener("activate", this.refreshDelegate);
37590         }
37591         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37592         this.on("activate", this.refreshDelegate);
37593         return this.el.getUpdateManager();
37594     },
37595     
37596     _handleRefresh : function(url, params, loadOnce){
37597         if(!loadOnce || !this.loaded){
37598             var updater = this.el.getUpdateManager();
37599             updater.update(url, params, this._setLoaded.createDelegate(this));
37600         }
37601     },
37602     
37603     _setLoaded : function(){
37604         this.loaded = true;
37605     }, 
37606     
37607     /**
37608      * Returns this panel's id
37609      * @return {String} 
37610      */
37611     getId : function(){
37612         return this.el.id;
37613     },
37614     
37615     /** 
37616      * Returns this panel's element - used by regiosn to add.
37617      * @return {Roo.Element} 
37618      */
37619     getEl : function(){
37620         return this.wrapEl || this.el;
37621     },
37622     
37623    
37624     
37625     adjustForComponents : function(width, height)
37626     {
37627         //Roo.log('adjustForComponents ');
37628         if(this.resizeEl != this.el){
37629             width -= this.el.getFrameWidth('lr');
37630             height -= this.el.getFrameWidth('tb');
37631         }
37632         if(this.toolbar){
37633             var te = this.toolbar.getEl();
37634             te.setWidth(width);
37635             height -= te.getHeight();
37636         }
37637         if(this.footer){
37638             var te = this.footer.getEl();
37639             te.setWidth(width);
37640             height -= te.getHeight();
37641         }
37642         
37643         
37644         if(this.adjustments){
37645             width += this.adjustments[0];
37646             height += this.adjustments[1];
37647         }
37648         return {"width": width, "height": height};
37649     },
37650     
37651     setSize : function(width, height){
37652         if(this.fitToFrame && !this.ignoreResize(width, height)){
37653             if(this.fitContainer && this.resizeEl != this.el){
37654                 this.el.setSize(width, height);
37655             }
37656             var size = this.adjustForComponents(width, height);
37657             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37658             this.fireEvent('resize', this, size.width, size.height);
37659         }
37660     },
37661     
37662     /**
37663      * Returns this panel's title
37664      * @return {String} 
37665      */
37666     getTitle : function(){
37667         
37668         if (typeof(this.title) != 'object') {
37669             return this.title;
37670         }
37671         
37672         var t = '';
37673         for (var k in this.title) {
37674             if (!this.title.hasOwnProperty(k)) {
37675                 continue;
37676             }
37677             
37678             if (k.indexOf('-') >= 0) {
37679                 var s = k.split('-');
37680                 for (var i = 0; i<s.length; i++) {
37681                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37682                 }
37683             } else {
37684                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37685             }
37686         }
37687         return t;
37688     },
37689     
37690     /**
37691      * Set this panel's title
37692      * @param {String} title
37693      */
37694     setTitle : function(title){
37695         this.title = title;
37696         if(this.region){
37697             this.region.updatePanelTitle(this, title);
37698         }
37699     },
37700     
37701     /**
37702      * Returns true is this panel was configured to be closable
37703      * @return {Boolean} 
37704      */
37705     isClosable : function(){
37706         return this.closable;
37707     },
37708     
37709     beforeSlide : function(){
37710         this.el.clip();
37711         this.resizeEl.clip();
37712     },
37713     
37714     afterSlide : function(){
37715         this.el.unclip();
37716         this.resizeEl.unclip();
37717     },
37718     
37719     /**
37720      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37721      *   Will fail silently if the {@link #setUrl} method has not been called.
37722      *   This does not activate the panel, just updates its content.
37723      */
37724     refresh : function(){
37725         if(this.refreshDelegate){
37726            this.loaded = false;
37727            this.refreshDelegate();
37728         }
37729     },
37730     
37731     /**
37732      * Destroys this panel
37733      */
37734     destroy : function(){
37735         this.el.removeAllListeners();
37736         var tempEl = document.createElement("span");
37737         tempEl.appendChild(this.el.dom);
37738         tempEl.innerHTML = "";
37739         this.el.remove();
37740         this.el = null;
37741     },
37742     
37743     /**
37744      * form - if the content panel contains a form - this is a reference to it.
37745      * @type {Roo.form.Form}
37746      */
37747     form : false,
37748     /**
37749      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37750      *    This contains a reference to it.
37751      * @type {Roo.View}
37752      */
37753     view : false,
37754     
37755       /**
37756      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37757      * <pre><code>
37758
37759 layout.addxtype({
37760        xtype : 'Form',
37761        items: [ .... ]
37762    }
37763 );
37764
37765 </code></pre>
37766      * @param {Object} cfg Xtype definition of item to add.
37767      */
37768     
37769     
37770     getChildContainer: function () {
37771         return this.getEl();
37772     }
37773     
37774     
37775     /*
37776         var  ret = new Roo.factory(cfg);
37777         return ret;
37778         
37779         
37780         // add form..
37781         if (cfg.xtype.match(/^Form$/)) {
37782             
37783             var el;
37784             //if (this.footer) {
37785             //    el = this.footer.container.insertSibling(false, 'before');
37786             //} else {
37787                 el = this.el.createChild();
37788             //}
37789
37790             this.form = new  Roo.form.Form(cfg);
37791             
37792             
37793             if ( this.form.allItems.length) {
37794                 this.form.render(el.dom);
37795             }
37796             return this.form;
37797         }
37798         // should only have one of theses..
37799         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37800             // views.. should not be just added - used named prop 'view''
37801             
37802             cfg.el = this.el.appendChild(document.createElement("div"));
37803             // factory?
37804             
37805             var ret = new Roo.factory(cfg);
37806              
37807              ret.render && ret.render(false, ''); // render blank..
37808             this.view = ret;
37809             return ret;
37810         }
37811         return false;
37812     }
37813     \*/
37814 });
37815  
37816 /**
37817  * @class Roo.bootstrap.panel.Grid
37818  * @extends Roo.bootstrap.panel.Content
37819  * @constructor
37820  * Create a new GridPanel.
37821  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37822  * @param {Object} config A the config object
37823   
37824  */
37825
37826
37827
37828 Roo.bootstrap.panel.Grid = function(config)
37829 {
37830     
37831       
37832     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37833         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37834
37835     config.el = this.wrapper;
37836     //this.el = this.wrapper;
37837     
37838       if (config.container) {
37839         // ctor'ed from a Border/panel.grid
37840         
37841         
37842         this.wrapper.setStyle("overflow", "hidden");
37843         this.wrapper.addClass('roo-grid-container');
37844
37845     }
37846     
37847     
37848     if(config.toolbar){
37849         var tool_el = this.wrapper.createChild();    
37850         this.toolbar = Roo.factory(config.toolbar);
37851         var ti = [];
37852         if (config.toolbar.items) {
37853             ti = config.toolbar.items ;
37854             delete config.toolbar.items ;
37855         }
37856         
37857         var nitems = [];
37858         this.toolbar.render(tool_el);
37859         for(var i =0;i < ti.length;i++) {
37860           //  Roo.log(['add child', items[i]]);
37861             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37862         }
37863         this.toolbar.items = nitems;
37864         
37865         delete config.toolbar;
37866     }
37867     
37868     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37869     config.grid.scrollBody = true;;
37870     config.grid.monitorWindowResize = false; // turn off autosizing
37871     config.grid.autoHeight = false;
37872     config.grid.autoWidth = false;
37873     
37874     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37875     
37876     if (config.background) {
37877         // render grid on panel activation (if panel background)
37878         this.on('activate', function(gp) {
37879             if (!gp.grid.rendered) {
37880                 gp.grid.render(this.wrapper);
37881                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37882             }
37883         });
37884             
37885     } else {
37886         this.grid.render(this.wrapper);
37887         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37888
37889     }
37890     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37891     // ??? needed ??? config.el = this.wrapper;
37892     
37893     
37894     
37895   
37896     // xtype created footer. - not sure if will work as we normally have to render first..
37897     if (this.footer && !this.footer.el && this.footer.xtype) {
37898         
37899         var ctr = this.grid.getView().getFooterPanel(true);
37900         this.footer.dataSource = this.grid.dataSource;
37901         this.footer = Roo.factory(this.footer, Roo);
37902         this.footer.render(ctr);
37903         
37904     }
37905     
37906     
37907     
37908     
37909      
37910 };
37911
37912 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37913     getId : function(){
37914         return this.grid.id;
37915     },
37916     
37917     /**
37918      * Returns the grid for this panel
37919      * @return {Roo.bootstrap.Table} 
37920      */
37921     getGrid : function(){
37922         return this.grid;    
37923     },
37924     
37925     setSize : function(width, height){
37926         if(!this.ignoreResize(width, height)){
37927             var grid = this.grid;
37928             var size = this.adjustForComponents(width, height);
37929             var gridel = grid.getGridEl();
37930             gridel.setSize(size.width, size.height);
37931             /*
37932             var thd = grid.getGridEl().select('thead',true).first();
37933             var tbd = grid.getGridEl().select('tbody', true).first();
37934             if (tbd) {
37935                 tbd.setSize(width, height - thd.getHeight());
37936             }
37937             */
37938             grid.autoSize();
37939         }
37940     },
37941      
37942     
37943     
37944     beforeSlide : function(){
37945         this.grid.getView().scroller.clip();
37946     },
37947     
37948     afterSlide : function(){
37949         this.grid.getView().scroller.unclip();
37950     },
37951     
37952     destroy : function(){
37953         this.grid.destroy();
37954         delete this.grid;
37955         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37956     }
37957 });
37958
37959 /**
37960  * @class Roo.bootstrap.panel.Nest
37961  * @extends Roo.bootstrap.panel.Content
37962  * @constructor
37963  * Create a new Panel, that can contain a layout.Border.
37964  * 
37965  * 
37966  * @param {Roo.BorderLayout} layout The layout for this panel
37967  * @param {String/Object} config A string to set only the title or a config object
37968  */
37969 Roo.bootstrap.panel.Nest = function(config)
37970 {
37971     // construct with only one argument..
37972     /* FIXME - implement nicer consturctors
37973     if (layout.layout) {
37974         config = layout;
37975         layout = config.layout;
37976         delete config.layout;
37977     }
37978     if (layout.xtype && !layout.getEl) {
37979         // then layout needs constructing..
37980         layout = Roo.factory(layout, Roo);
37981     }
37982     */
37983     
37984     config.el =  config.layout.getEl();
37985     
37986     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37987     
37988     config.layout.monitorWindowResize = false; // turn off autosizing
37989     this.layout = config.layout;
37990     this.layout.getEl().addClass("roo-layout-nested-layout");
37991     this.layout.parent = this;
37992     
37993     
37994     
37995     
37996 };
37997
37998 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37999
38000     setSize : function(width, height){
38001         if(!this.ignoreResize(width, height)){
38002             var size = this.adjustForComponents(width, height);
38003             var el = this.layout.getEl();
38004             if (size.height < 1) {
38005                 el.setWidth(size.width);   
38006             } else {
38007                 el.setSize(size.width, size.height);
38008             }
38009             var touch = el.dom.offsetWidth;
38010             this.layout.layout();
38011             // ie requires a double layout on the first pass
38012             if(Roo.isIE && !this.initialized){
38013                 this.initialized = true;
38014                 this.layout.layout();
38015             }
38016         }
38017     },
38018     
38019     // activate all subpanels if not currently active..
38020     
38021     setActiveState : function(active){
38022         this.active = active;
38023         this.setActiveClass(active);
38024         
38025         if(!active){
38026             this.fireEvent("deactivate", this);
38027             return;
38028         }
38029         
38030         this.fireEvent("activate", this);
38031         // not sure if this should happen before or after..
38032         if (!this.layout) {
38033             return; // should not happen..
38034         }
38035         var reg = false;
38036         for (var r in this.layout.regions) {
38037             reg = this.layout.getRegion(r);
38038             if (reg.getActivePanel()) {
38039                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38040                 reg.setActivePanel(reg.getActivePanel());
38041                 continue;
38042             }
38043             if (!reg.panels.length) {
38044                 continue;
38045             }
38046             reg.showPanel(reg.getPanel(0));
38047         }
38048         
38049         
38050         
38051         
38052     },
38053     
38054     /**
38055      * Returns the nested BorderLayout for this panel
38056      * @return {Roo.BorderLayout} 
38057      */
38058     getLayout : function(){
38059         return this.layout;
38060     },
38061     
38062      /**
38063      * Adds a xtype elements to the layout of the nested panel
38064      * <pre><code>
38065
38066 panel.addxtype({
38067        xtype : 'ContentPanel',
38068        region: 'west',
38069        items: [ .... ]
38070    }
38071 );
38072
38073 panel.addxtype({
38074         xtype : 'NestedLayoutPanel',
38075         region: 'west',
38076         layout: {
38077            center: { },
38078            west: { }   
38079         },
38080         items : [ ... list of content panels or nested layout panels.. ]
38081    }
38082 );
38083 </code></pre>
38084      * @param {Object} cfg Xtype definition of item to add.
38085      */
38086     addxtype : function(cfg) {
38087         return this.layout.addxtype(cfg);
38088     
38089     }
38090 });/*
38091  * Based on:
38092  * Ext JS Library 1.1.1
38093  * Copyright(c) 2006-2007, Ext JS, LLC.
38094  *
38095  * Originally Released Under LGPL - original licence link has changed is not relivant.
38096  *
38097  * Fork - LGPL
38098  * <script type="text/javascript">
38099  */
38100 /**
38101  * @class Roo.TabPanel
38102  * @extends Roo.util.Observable
38103  * A lightweight tab container.
38104  * <br><br>
38105  * Usage:
38106  * <pre><code>
38107 // basic tabs 1, built from existing content
38108 var tabs = new Roo.TabPanel("tabs1");
38109 tabs.addTab("script", "View Script");
38110 tabs.addTab("markup", "View Markup");
38111 tabs.activate("script");
38112
38113 // more advanced tabs, built from javascript
38114 var jtabs = new Roo.TabPanel("jtabs");
38115 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38116
38117 // set up the UpdateManager
38118 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38119 var updater = tab2.getUpdateManager();
38120 updater.setDefaultUrl("ajax1.htm");
38121 tab2.on('activate', updater.refresh, updater, true);
38122
38123 // Use setUrl for Ajax loading
38124 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38125 tab3.setUrl("ajax2.htm", null, true);
38126
38127 // Disabled tab
38128 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38129 tab4.disable();
38130
38131 jtabs.activate("jtabs-1");
38132  * </code></pre>
38133  * @constructor
38134  * Create a new TabPanel.
38135  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38136  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38137  */
38138 Roo.bootstrap.panel.Tabs = function(config){
38139     /**
38140     * The container element for this TabPanel.
38141     * @type Roo.Element
38142     */
38143     this.el = Roo.get(config.el);
38144     delete config.el;
38145     if(config){
38146         if(typeof config == "boolean"){
38147             this.tabPosition = config ? "bottom" : "top";
38148         }else{
38149             Roo.apply(this, config);
38150         }
38151     }
38152     
38153     if(this.tabPosition == "bottom"){
38154         // if tabs are at the bottom = create the body first.
38155         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38156         this.el.addClass("roo-tabs-bottom");
38157     }
38158     // next create the tabs holders
38159     
38160     if (this.tabPosition == "west"){
38161         
38162         var reg = this.region; // fake it..
38163         while (reg) {
38164             if (!reg.mgr.parent) {
38165                 break;
38166             }
38167             reg = reg.mgr.parent.region;
38168         }
38169         Roo.log("got nest?");
38170         Roo.log(reg);
38171         if (reg.mgr.getRegion('west')) {
38172             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38173             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38174             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38175             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38176             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38177         
38178             
38179         }
38180         
38181         
38182     } else {
38183      
38184         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38185         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38186         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38187         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38188     }
38189     
38190     
38191     if(Roo.isIE){
38192         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38193     }
38194     
38195     // finally - if tabs are at the top, then create the body last..
38196     if(this.tabPosition != "bottom"){
38197         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38198          * @type Roo.Element
38199          */
38200         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38201         this.el.addClass("roo-tabs-top");
38202     }
38203     this.items = [];
38204
38205     this.bodyEl.setStyle("position", "relative");
38206
38207     this.active = null;
38208     this.activateDelegate = this.activate.createDelegate(this);
38209
38210     this.addEvents({
38211         /**
38212          * @event tabchange
38213          * Fires when the active tab changes
38214          * @param {Roo.TabPanel} this
38215          * @param {Roo.TabPanelItem} activePanel The new active tab
38216          */
38217         "tabchange": true,
38218         /**
38219          * @event beforetabchange
38220          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38221          * @param {Roo.TabPanel} this
38222          * @param {Object} e Set cancel to true on this object to cancel the tab change
38223          * @param {Roo.TabPanelItem} tab The tab being changed to
38224          */
38225         "beforetabchange" : true
38226     });
38227
38228     Roo.EventManager.onWindowResize(this.onResize, this);
38229     this.cpad = this.el.getPadding("lr");
38230     this.hiddenCount = 0;
38231
38232
38233     // toolbar on the tabbar support...
38234     if (this.toolbar) {
38235         alert("no toolbar support yet");
38236         this.toolbar  = false;
38237         /*
38238         var tcfg = this.toolbar;
38239         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38240         this.toolbar = new Roo.Toolbar(tcfg);
38241         if (Roo.isSafari) {
38242             var tbl = tcfg.container.child('table', true);
38243             tbl.setAttribute('width', '100%');
38244         }
38245         */
38246         
38247     }
38248    
38249
38250
38251     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38252 };
38253
38254 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38255     /*
38256      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38257      */
38258     tabPosition : "top",
38259     /*
38260      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38261      */
38262     currentTabWidth : 0,
38263     /*
38264      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38265      */
38266     minTabWidth : 40,
38267     /*
38268      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38269      */
38270     maxTabWidth : 250,
38271     /*
38272      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38273      */
38274     preferredTabWidth : 175,
38275     /*
38276      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38277      */
38278     resizeTabs : false,
38279     /*
38280      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38281      */
38282     monitorResize : true,
38283     /*
38284      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38285      */
38286     toolbar : false,  // set by caller..
38287     
38288     region : false, /// set by caller
38289     
38290     disableTooltips : true, // not used yet...
38291
38292     /**
38293      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38294      * @param {String} id The id of the div to use <b>or create</b>
38295      * @param {String} text The text for the tab
38296      * @param {String} content (optional) Content to put in the TabPanelItem body
38297      * @param {Boolean} closable (optional) True to create a close icon on the tab
38298      * @return {Roo.TabPanelItem} The created TabPanelItem
38299      */
38300     addTab : function(id, text, content, closable, tpl)
38301     {
38302         var item = new Roo.bootstrap.panel.TabItem({
38303             panel: this,
38304             id : id,
38305             text : text,
38306             closable : closable,
38307             tpl : tpl
38308         });
38309         this.addTabItem(item);
38310         if(content){
38311             item.setContent(content);
38312         }
38313         return item;
38314     },
38315
38316     /**
38317      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38318      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38319      * @return {Roo.TabPanelItem}
38320      */
38321     getTab : function(id){
38322         return this.items[id];
38323     },
38324
38325     /**
38326      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38327      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38328      */
38329     hideTab : function(id){
38330         var t = this.items[id];
38331         if(!t.isHidden()){
38332            t.setHidden(true);
38333            this.hiddenCount++;
38334            this.autoSizeTabs();
38335         }
38336     },
38337
38338     /**
38339      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38340      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38341      */
38342     unhideTab : function(id){
38343         var t = this.items[id];
38344         if(t.isHidden()){
38345            t.setHidden(false);
38346            this.hiddenCount--;
38347            this.autoSizeTabs();
38348         }
38349     },
38350
38351     /**
38352      * Adds an existing {@link Roo.TabPanelItem}.
38353      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38354      */
38355     addTabItem : function(item)
38356     {
38357         this.items[item.id] = item;
38358         this.items.push(item);
38359         this.autoSizeTabs();
38360       //  if(this.resizeTabs){
38361     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38362   //         this.autoSizeTabs();
38363 //        }else{
38364 //            item.autoSize();
38365        // }
38366     },
38367
38368     /**
38369      * Removes a {@link Roo.TabPanelItem}.
38370      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38371      */
38372     removeTab : function(id){
38373         var items = this.items;
38374         var tab = items[id];
38375         if(!tab) { return; }
38376         var index = items.indexOf(tab);
38377         if(this.active == tab && items.length > 1){
38378             var newTab = this.getNextAvailable(index);
38379             if(newTab) {
38380                 newTab.activate();
38381             }
38382         }
38383         this.stripEl.dom.removeChild(tab.pnode.dom);
38384         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38385             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38386         }
38387         items.splice(index, 1);
38388         delete this.items[tab.id];
38389         tab.fireEvent("close", tab);
38390         tab.purgeListeners();
38391         this.autoSizeTabs();
38392     },
38393
38394     getNextAvailable : function(start){
38395         var items = this.items;
38396         var index = start;
38397         // look for a next tab that will slide over to
38398         // replace the one being removed
38399         while(index < items.length){
38400             var item = items[++index];
38401             if(item && !item.isHidden()){
38402                 return item;
38403             }
38404         }
38405         // if one isn't found select the previous tab (on the left)
38406         index = start;
38407         while(index >= 0){
38408             var item = items[--index];
38409             if(item && !item.isHidden()){
38410                 return item;
38411             }
38412         }
38413         return null;
38414     },
38415
38416     /**
38417      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38418      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38419      */
38420     disableTab : function(id){
38421         var tab = this.items[id];
38422         if(tab && this.active != tab){
38423             tab.disable();
38424         }
38425     },
38426
38427     /**
38428      * Enables a {@link Roo.TabPanelItem} that is disabled.
38429      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38430      */
38431     enableTab : function(id){
38432         var tab = this.items[id];
38433         tab.enable();
38434     },
38435
38436     /**
38437      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38438      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38439      * @return {Roo.TabPanelItem} The TabPanelItem.
38440      */
38441     activate : function(id)
38442     {
38443         //Roo.log('activite:'  + id);
38444         
38445         var tab = this.items[id];
38446         if(!tab){
38447             return null;
38448         }
38449         if(tab == this.active || tab.disabled){
38450             return tab;
38451         }
38452         var e = {};
38453         this.fireEvent("beforetabchange", this, e, tab);
38454         if(e.cancel !== true && !tab.disabled){
38455             if(this.active){
38456                 this.active.hide();
38457             }
38458             this.active = this.items[id];
38459             this.active.show();
38460             this.fireEvent("tabchange", this, this.active);
38461         }
38462         return tab;
38463     },
38464
38465     /**
38466      * Gets the active {@link Roo.TabPanelItem}.
38467      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38468      */
38469     getActiveTab : function(){
38470         return this.active;
38471     },
38472
38473     /**
38474      * Updates the tab body element to fit the height of the container element
38475      * for overflow scrolling
38476      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38477      */
38478     syncHeight : function(targetHeight){
38479         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38480         var bm = this.bodyEl.getMargins();
38481         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38482         this.bodyEl.setHeight(newHeight);
38483         return newHeight;
38484     },
38485
38486     onResize : function(){
38487         if(this.monitorResize){
38488             this.autoSizeTabs();
38489         }
38490     },
38491
38492     /**
38493      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38494      */
38495     beginUpdate : function(){
38496         this.updating = true;
38497     },
38498
38499     /**
38500      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38501      */
38502     endUpdate : function(){
38503         this.updating = false;
38504         this.autoSizeTabs();
38505     },
38506
38507     /**
38508      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38509      */
38510     autoSizeTabs : function()
38511     {
38512         var count = this.items.length;
38513         var vcount = count - this.hiddenCount;
38514         
38515         if (vcount < 2) {
38516             this.stripEl.hide();
38517         } else {
38518             this.stripEl.show();
38519         }
38520         
38521         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38522             return;
38523         }
38524         
38525         
38526         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38527         var availWidth = Math.floor(w / vcount);
38528         var b = this.stripBody;
38529         if(b.getWidth() > w){
38530             var tabs = this.items;
38531             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38532             if(availWidth < this.minTabWidth){
38533                 /*if(!this.sleft){    // incomplete scrolling code
38534                     this.createScrollButtons();
38535                 }
38536                 this.showScroll();
38537                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38538             }
38539         }else{
38540             if(this.currentTabWidth < this.preferredTabWidth){
38541                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38542             }
38543         }
38544     },
38545
38546     /**
38547      * Returns the number of tabs in this TabPanel.
38548      * @return {Number}
38549      */
38550      getCount : function(){
38551          return this.items.length;
38552      },
38553
38554     /**
38555      * Resizes all the tabs to the passed width
38556      * @param {Number} The new width
38557      */
38558     setTabWidth : function(width){
38559         this.currentTabWidth = width;
38560         for(var i = 0, len = this.items.length; i < len; i++) {
38561                 if(!this.items[i].isHidden()) {
38562                 this.items[i].setWidth(width);
38563             }
38564         }
38565     },
38566
38567     /**
38568      * Destroys this TabPanel
38569      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38570      */
38571     destroy : function(removeEl){
38572         Roo.EventManager.removeResizeListener(this.onResize, this);
38573         for(var i = 0, len = this.items.length; i < len; i++){
38574             this.items[i].purgeListeners();
38575         }
38576         if(removeEl === true){
38577             this.el.update("");
38578             this.el.remove();
38579         }
38580     },
38581     
38582     createStrip : function(container)
38583     {
38584         var strip = document.createElement("nav");
38585         strip.className = Roo.bootstrap.version == 4 ?
38586             "navbar-light bg-light" : 
38587             "navbar navbar-default"; //"x-tabs-wrap";
38588         container.appendChild(strip);
38589         return strip;
38590     },
38591     
38592     createStripList : function(strip)
38593     {
38594         // div wrapper for retard IE
38595         // returns the "tr" element.
38596         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38597         //'<div class="x-tabs-strip-wrap">'+
38598           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38599           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38600         return strip.firstChild; //.firstChild.firstChild.firstChild;
38601     },
38602     createBody : function(container)
38603     {
38604         var body = document.createElement("div");
38605         Roo.id(body, "tab-body");
38606         //Roo.fly(body).addClass("x-tabs-body");
38607         Roo.fly(body).addClass("tab-content");
38608         container.appendChild(body);
38609         return body;
38610     },
38611     createItemBody :function(bodyEl, id){
38612         var body = Roo.getDom(id);
38613         if(!body){
38614             body = document.createElement("div");
38615             body.id = id;
38616         }
38617         //Roo.fly(body).addClass("x-tabs-item-body");
38618         Roo.fly(body).addClass("tab-pane");
38619          bodyEl.insertBefore(body, bodyEl.firstChild);
38620         return body;
38621     },
38622     /** @private */
38623     createStripElements :  function(stripEl, text, closable, tpl)
38624     {
38625         var td = document.createElement("li"); // was td..
38626         td.className = 'nav-item';
38627         
38628         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38629         
38630         
38631         stripEl.appendChild(td);
38632         /*if(closable){
38633             td.className = "x-tabs-closable";
38634             if(!this.closeTpl){
38635                 this.closeTpl = new Roo.Template(
38636                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38637                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38638                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38639                 );
38640             }
38641             var el = this.closeTpl.overwrite(td, {"text": text});
38642             var close = el.getElementsByTagName("div")[0];
38643             var inner = el.getElementsByTagName("em")[0];
38644             return {"el": el, "close": close, "inner": inner};
38645         } else {
38646         */
38647         // not sure what this is..
38648 //            if(!this.tabTpl){
38649                 //this.tabTpl = new Roo.Template(
38650                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38651                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38652                 //);
38653 //                this.tabTpl = new Roo.Template(
38654 //                   '<a href="#">' +
38655 //                   '<span unselectable="on"' +
38656 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38657 //                            ' >{text}</span></a>'
38658 //                );
38659 //                
38660 //            }
38661
38662
38663             var template = tpl || this.tabTpl || false;
38664             
38665             if(!template){
38666                 template =  new Roo.Template(
38667                         Roo.bootstrap.version == 4 ? 
38668                             (
38669                                 '<a class="nav-link" href="#" unselectable="on"' +
38670                                      (this.disableTooltips ? '' : ' title="{text}"') +
38671                                      ' >{text}</a>'
38672                             ) : (
38673                                 '<a class="nav-link" href="#">' +
38674                                 '<span unselectable="on"' +
38675                                          (this.disableTooltips ? '' : ' title="{text}"') +
38676                                     ' >{text}</span></a>'
38677                             )
38678                 );
38679             }
38680             
38681             switch (typeof(template)) {
38682                 case 'object' :
38683                     break;
38684                 case 'string' :
38685                     template = new Roo.Template(template);
38686                     break;
38687                 default :
38688                     break;
38689             }
38690             
38691             var el = template.overwrite(td, {"text": text});
38692             
38693             var inner = el.getElementsByTagName("span")[0];
38694             
38695             return {"el": el, "inner": inner};
38696             
38697     }
38698         
38699     
38700 });
38701
38702 /**
38703  * @class Roo.TabPanelItem
38704  * @extends Roo.util.Observable
38705  * Represents an individual item (tab plus body) in a TabPanel.
38706  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38707  * @param {String} id The id of this TabPanelItem
38708  * @param {String} text The text for the tab of this TabPanelItem
38709  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38710  */
38711 Roo.bootstrap.panel.TabItem = function(config){
38712     /**
38713      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38714      * @type Roo.TabPanel
38715      */
38716     this.tabPanel = config.panel;
38717     /**
38718      * The id for this TabPanelItem
38719      * @type String
38720      */
38721     this.id = config.id;
38722     /** @private */
38723     this.disabled = false;
38724     /** @private */
38725     this.text = config.text;
38726     /** @private */
38727     this.loaded = false;
38728     this.closable = config.closable;
38729
38730     /**
38731      * The body element for this TabPanelItem.
38732      * @type Roo.Element
38733      */
38734     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38735     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38736     this.bodyEl.setStyle("display", "block");
38737     this.bodyEl.setStyle("zoom", "1");
38738     //this.hideAction();
38739
38740     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38741     /** @private */
38742     this.el = Roo.get(els.el);
38743     this.inner = Roo.get(els.inner, true);
38744      this.textEl = Roo.bootstrap.version == 4 ?
38745         this.el : Roo.get(this.el.dom.firstChild, true);
38746
38747     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38748     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38749
38750     
38751 //    this.el.on("mousedown", this.onTabMouseDown, this);
38752     this.el.on("click", this.onTabClick, this);
38753     /** @private */
38754     if(config.closable){
38755         var c = Roo.get(els.close, true);
38756         c.dom.title = this.closeText;
38757         c.addClassOnOver("close-over");
38758         c.on("click", this.closeClick, this);
38759      }
38760
38761     this.addEvents({
38762          /**
38763          * @event activate
38764          * Fires when this tab becomes the active tab.
38765          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38766          * @param {Roo.TabPanelItem} this
38767          */
38768         "activate": true,
38769         /**
38770          * @event beforeclose
38771          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38772          * @param {Roo.TabPanelItem} this
38773          * @param {Object} e Set cancel to true on this object to cancel the close.
38774          */
38775         "beforeclose": true,
38776         /**
38777          * @event close
38778          * Fires when this tab is closed.
38779          * @param {Roo.TabPanelItem} this
38780          */
38781          "close": true,
38782         /**
38783          * @event deactivate
38784          * Fires when this tab is no longer the active tab.
38785          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38786          * @param {Roo.TabPanelItem} this
38787          */
38788          "deactivate" : true
38789     });
38790     this.hidden = false;
38791
38792     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38793 };
38794
38795 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38796            {
38797     purgeListeners : function(){
38798        Roo.util.Observable.prototype.purgeListeners.call(this);
38799        this.el.removeAllListeners();
38800     },
38801     /**
38802      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38803      */
38804     show : function(){
38805         this.status_node.addClass("active");
38806         this.showAction();
38807         if(Roo.isOpera){
38808             this.tabPanel.stripWrap.repaint();
38809         }
38810         this.fireEvent("activate", this.tabPanel, this);
38811     },
38812
38813     /**
38814      * Returns true if this tab is the active tab.
38815      * @return {Boolean}
38816      */
38817     isActive : function(){
38818         return this.tabPanel.getActiveTab() == this;
38819     },
38820
38821     /**
38822      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38823      */
38824     hide : function(){
38825         this.status_node.removeClass("active");
38826         this.hideAction();
38827         this.fireEvent("deactivate", this.tabPanel, this);
38828     },
38829
38830     hideAction : function(){
38831         this.bodyEl.hide();
38832         this.bodyEl.setStyle("position", "absolute");
38833         this.bodyEl.setLeft("-20000px");
38834         this.bodyEl.setTop("-20000px");
38835     },
38836
38837     showAction : function(){
38838         this.bodyEl.setStyle("position", "relative");
38839         this.bodyEl.setTop("");
38840         this.bodyEl.setLeft("");
38841         this.bodyEl.show();
38842     },
38843
38844     /**
38845      * Set the tooltip for the tab.
38846      * @param {String} tooltip The tab's tooltip
38847      */
38848     setTooltip : function(text){
38849         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38850             this.textEl.dom.qtip = text;
38851             this.textEl.dom.removeAttribute('title');
38852         }else{
38853             this.textEl.dom.title = text;
38854         }
38855     },
38856
38857     onTabClick : function(e){
38858         e.preventDefault();
38859         this.tabPanel.activate(this.id);
38860     },
38861
38862     onTabMouseDown : function(e){
38863         e.preventDefault();
38864         this.tabPanel.activate(this.id);
38865     },
38866 /*
38867     getWidth : function(){
38868         return this.inner.getWidth();
38869     },
38870
38871     setWidth : function(width){
38872         var iwidth = width - this.linode.getPadding("lr");
38873         this.inner.setWidth(iwidth);
38874         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38875         this.linode.setWidth(width);
38876     },
38877 */
38878     /**
38879      * Show or hide the tab
38880      * @param {Boolean} hidden True to hide or false to show.
38881      */
38882     setHidden : function(hidden){
38883         this.hidden = hidden;
38884         this.linode.setStyle("display", hidden ? "none" : "");
38885     },
38886
38887     /**
38888      * Returns true if this tab is "hidden"
38889      * @return {Boolean}
38890      */
38891     isHidden : function(){
38892         return this.hidden;
38893     },
38894
38895     /**
38896      * Returns the text for this tab
38897      * @return {String}
38898      */
38899     getText : function(){
38900         return this.text;
38901     },
38902     /*
38903     autoSize : function(){
38904         //this.el.beginMeasure();
38905         this.textEl.setWidth(1);
38906         /*
38907          *  #2804 [new] Tabs in Roojs
38908          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38909          */
38910         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38911         //this.el.endMeasure();
38912     //},
38913
38914     /**
38915      * Sets the text for the tab (Note: this also sets the tooltip text)
38916      * @param {String} text The tab's text and tooltip
38917      */
38918     setText : function(text){
38919         this.text = text;
38920         this.textEl.update(text);
38921         this.setTooltip(text);
38922         //if(!this.tabPanel.resizeTabs){
38923         //    this.autoSize();
38924         //}
38925     },
38926     /**
38927      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38928      */
38929     activate : function(){
38930         this.tabPanel.activate(this.id);
38931     },
38932
38933     /**
38934      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38935      */
38936     disable : function(){
38937         if(this.tabPanel.active != this){
38938             this.disabled = true;
38939             this.status_node.addClass("disabled");
38940         }
38941     },
38942
38943     /**
38944      * Enables this TabPanelItem if it was previously disabled.
38945      */
38946     enable : function(){
38947         this.disabled = false;
38948         this.status_node.removeClass("disabled");
38949     },
38950
38951     /**
38952      * Sets the content for this TabPanelItem.
38953      * @param {String} content The content
38954      * @param {Boolean} loadScripts true to look for and load scripts
38955      */
38956     setContent : function(content, loadScripts){
38957         this.bodyEl.update(content, loadScripts);
38958     },
38959
38960     /**
38961      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38962      * @return {Roo.UpdateManager} The UpdateManager
38963      */
38964     getUpdateManager : function(){
38965         return this.bodyEl.getUpdateManager();
38966     },
38967
38968     /**
38969      * Set a URL to be used to load the content for this TabPanelItem.
38970      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38971      * @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)
38972      * @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)
38973      * @return {Roo.UpdateManager} The UpdateManager
38974      */
38975     setUrl : function(url, params, loadOnce){
38976         if(this.refreshDelegate){
38977             this.un('activate', this.refreshDelegate);
38978         }
38979         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38980         this.on("activate", this.refreshDelegate);
38981         return this.bodyEl.getUpdateManager();
38982     },
38983
38984     /** @private */
38985     _handleRefresh : function(url, params, loadOnce){
38986         if(!loadOnce || !this.loaded){
38987             var updater = this.bodyEl.getUpdateManager();
38988             updater.update(url, params, this._setLoaded.createDelegate(this));
38989         }
38990     },
38991
38992     /**
38993      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38994      *   Will fail silently if the setUrl method has not been called.
38995      *   This does not activate the panel, just updates its content.
38996      */
38997     refresh : function(){
38998         if(this.refreshDelegate){
38999            this.loaded = false;
39000            this.refreshDelegate();
39001         }
39002     },
39003
39004     /** @private */
39005     _setLoaded : function(){
39006         this.loaded = true;
39007     },
39008
39009     /** @private */
39010     closeClick : function(e){
39011         var o = {};
39012         e.stopEvent();
39013         this.fireEvent("beforeclose", this, o);
39014         if(o.cancel !== true){
39015             this.tabPanel.removeTab(this.id);
39016         }
39017     },
39018     /**
39019      * The text displayed in the tooltip for the close icon.
39020      * @type String
39021      */
39022     closeText : "Close this tab"
39023 });
39024 /**
39025 *    This script refer to:
39026 *    Title: International Telephone Input
39027 *    Author: Jack O'Connor
39028 *    Code version:  v12.1.12
39029 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39030 **/
39031
39032 Roo.bootstrap.PhoneInputData = function() {
39033     var d = [
39034       [
39035         "Afghanistan (‫افغانستان‬‎)",
39036         "af",
39037         "93"
39038       ],
39039       [
39040         "Albania (Shqipëri)",
39041         "al",
39042         "355"
39043       ],
39044       [
39045         "Algeria (‫الجزائر‬‎)",
39046         "dz",
39047         "213"
39048       ],
39049       [
39050         "American Samoa",
39051         "as",
39052         "1684"
39053       ],
39054       [
39055         "Andorra",
39056         "ad",
39057         "376"
39058       ],
39059       [
39060         "Angola",
39061         "ao",
39062         "244"
39063       ],
39064       [
39065         "Anguilla",
39066         "ai",
39067         "1264"
39068       ],
39069       [
39070         "Antigua and Barbuda",
39071         "ag",
39072         "1268"
39073       ],
39074       [
39075         "Argentina",
39076         "ar",
39077         "54"
39078       ],
39079       [
39080         "Armenia (Հայաստան)",
39081         "am",
39082         "374"
39083       ],
39084       [
39085         "Aruba",
39086         "aw",
39087         "297"
39088       ],
39089       [
39090         "Australia",
39091         "au",
39092         "61",
39093         0
39094       ],
39095       [
39096         "Austria (Österreich)",
39097         "at",
39098         "43"
39099       ],
39100       [
39101         "Azerbaijan (Azərbaycan)",
39102         "az",
39103         "994"
39104       ],
39105       [
39106         "Bahamas",
39107         "bs",
39108         "1242"
39109       ],
39110       [
39111         "Bahrain (‫البحرين‬‎)",
39112         "bh",
39113         "973"
39114       ],
39115       [
39116         "Bangladesh (বাংলাদেশ)",
39117         "bd",
39118         "880"
39119       ],
39120       [
39121         "Barbados",
39122         "bb",
39123         "1246"
39124       ],
39125       [
39126         "Belarus (Беларусь)",
39127         "by",
39128         "375"
39129       ],
39130       [
39131         "Belgium (België)",
39132         "be",
39133         "32"
39134       ],
39135       [
39136         "Belize",
39137         "bz",
39138         "501"
39139       ],
39140       [
39141         "Benin (Bénin)",
39142         "bj",
39143         "229"
39144       ],
39145       [
39146         "Bermuda",
39147         "bm",
39148         "1441"
39149       ],
39150       [
39151         "Bhutan (འབྲུག)",
39152         "bt",
39153         "975"
39154       ],
39155       [
39156         "Bolivia",
39157         "bo",
39158         "591"
39159       ],
39160       [
39161         "Bosnia and Herzegovina (Босна и Херцеговина)",
39162         "ba",
39163         "387"
39164       ],
39165       [
39166         "Botswana",
39167         "bw",
39168         "267"
39169       ],
39170       [
39171         "Brazil (Brasil)",
39172         "br",
39173         "55"
39174       ],
39175       [
39176         "British Indian Ocean Territory",
39177         "io",
39178         "246"
39179       ],
39180       [
39181         "British Virgin Islands",
39182         "vg",
39183         "1284"
39184       ],
39185       [
39186         "Brunei",
39187         "bn",
39188         "673"
39189       ],
39190       [
39191         "Bulgaria (България)",
39192         "bg",
39193         "359"
39194       ],
39195       [
39196         "Burkina Faso",
39197         "bf",
39198         "226"
39199       ],
39200       [
39201         "Burundi (Uburundi)",
39202         "bi",
39203         "257"
39204       ],
39205       [
39206         "Cambodia (កម្ពុជា)",
39207         "kh",
39208         "855"
39209       ],
39210       [
39211         "Cameroon (Cameroun)",
39212         "cm",
39213         "237"
39214       ],
39215       [
39216         "Canada",
39217         "ca",
39218         "1",
39219         1,
39220         ["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"]
39221       ],
39222       [
39223         "Cape Verde (Kabu Verdi)",
39224         "cv",
39225         "238"
39226       ],
39227       [
39228         "Caribbean Netherlands",
39229         "bq",
39230         "599",
39231         1
39232       ],
39233       [
39234         "Cayman Islands",
39235         "ky",
39236         "1345"
39237       ],
39238       [
39239         "Central African Republic (République centrafricaine)",
39240         "cf",
39241         "236"
39242       ],
39243       [
39244         "Chad (Tchad)",
39245         "td",
39246         "235"
39247       ],
39248       [
39249         "Chile",
39250         "cl",
39251         "56"
39252       ],
39253       [
39254         "China (中国)",
39255         "cn",
39256         "86"
39257       ],
39258       [
39259         "Christmas Island",
39260         "cx",
39261         "61",
39262         2
39263       ],
39264       [
39265         "Cocos (Keeling) Islands",
39266         "cc",
39267         "61",
39268         1
39269       ],
39270       [
39271         "Colombia",
39272         "co",
39273         "57"
39274       ],
39275       [
39276         "Comoros (‫جزر القمر‬‎)",
39277         "km",
39278         "269"
39279       ],
39280       [
39281         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39282         "cd",
39283         "243"
39284       ],
39285       [
39286         "Congo (Republic) (Congo-Brazzaville)",
39287         "cg",
39288         "242"
39289       ],
39290       [
39291         "Cook Islands",
39292         "ck",
39293         "682"
39294       ],
39295       [
39296         "Costa Rica",
39297         "cr",
39298         "506"
39299       ],
39300       [
39301         "Côte d’Ivoire",
39302         "ci",
39303         "225"
39304       ],
39305       [
39306         "Croatia (Hrvatska)",
39307         "hr",
39308         "385"
39309       ],
39310       [
39311         "Cuba",
39312         "cu",
39313         "53"
39314       ],
39315       [
39316         "Curaçao",
39317         "cw",
39318         "599",
39319         0
39320       ],
39321       [
39322         "Cyprus (Κύπρος)",
39323         "cy",
39324         "357"
39325       ],
39326       [
39327         "Czech Republic (Česká republika)",
39328         "cz",
39329         "420"
39330       ],
39331       [
39332         "Denmark (Danmark)",
39333         "dk",
39334         "45"
39335       ],
39336       [
39337         "Djibouti",
39338         "dj",
39339         "253"
39340       ],
39341       [
39342         "Dominica",
39343         "dm",
39344         "1767"
39345       ],
39346       [
39347         "Dominican Republic (República Dominicana)",
39348         "do",
39349         "1",
39350         2,
39351         ["809", "829", "849"]
39352       ],
39353       [
39354         "Ecuador",
39355         "ec",
39356         "593"
39357       ],
39358       [
39359         "Egypt (‫مصر‬‎)",
39360         "eg",
39361         "20"
39362       ],
39363       [
39364         "El Salvador",
39365         "sv",
39366         "503"
39367       ],
39368       [
39369         "Equatorial Guinea (Guinea Ecuatorial)",
39370         "gq",
39371         "240"
39372       ],
39373       [
39374         "Eritrea",
39375         "er",
39376         "291"
39377       ],
39378       [
39379         "Estonia (Eesti)",
39380         "ee",
39381         "372"
39382       ],
39383       [
39384         "Ethiopia",
39385         "et",
39386         "251"
39387       ],
39388       [
39389         "Falkland Islands (Islas Malvinas)",
39390         "fk",
39391         "500"
39392       ],
39393       [
39394         "Faroe Islands (Føroyar)",
39395         "fo",
39396         "298"
39397       ],
39398       [
39399         "Fiji",
39400         "fj",
39401         "679"
39402       ],
39403       [
39404         "Finland (Suomi)",
39405         "fi",
39406         "358",
39407         0
39408       ],
39409       [
39410         "France",
39411         "fr",
39412         "33"
39413       ],
39414       [
39415         "French Guiana (Guyane française)",
39416         "gf",
39417         "594"
39418       ],
39419       [
39420         "French Polynesia (Polynésie française)",
39421         "pf",
39422         "689"
39423       ],
39424       [
39425         "Gabon",
39426         "ga",
39427         "241"
39428       ],
39429       [
39430         "Gambia",
39431         "gm",
39432         "220"
39433       ],
39434       [
39435         "Georgia (საქართველო)",
39436         "ge",
39437         "995"
39438       ],
39439       [
39440         "Germany (Deutschland)",
39441         "de",
39442         "49"
39443       ],
39444       [
39445         "Ghana (Gaana)",
39446         "gh",
39447         "233"
39448       ],
39449       [
39450         "Gibraltar",
39451         "gi",
39452         "350"
39453       ],
39454       [
39455         "Greece (Ελλάδα)",
39456         "gr",
39457         "30"
39458       ],
39459       [
39460         "Greenland (Kalaallit Nunaat)",
39461         "gl",
39462         "299"
39463       ],
39464       [
39465         "Grenada",
39466         "gd",
39467         "1473"
39468       ],
39469       [
39470         "Guadeloupe",
39471         "gp",
39472         "590",
39473         0
39474       ],
39475       [
39476         "Guam",
39477         "gu",
39478         "1671"
39479       ],
39480       [
39481         "Guatemala",
39482         "gt",
39483         "502"
39484       ],
39485       [
39486         "Guernsey",
39487         "gg",
39488         "44",
39489         1
39490       ],
39491       [
39492         "Guinea (Guinée)",
39493         "gn",
39494         "224"
39495       ],
39496       [
39497         "Guinea-Bissau (Guiné Bissau)",
39498         "gw",
39499         "245"
39500       ],
39501       [
39502         "Guyana",
39503         "gy",
39504         "592"
39505       ],
39506       [
39507         "Haiti",
39508         "ht",
39509         "509"
39510       ],
39511       [
39512         "Honduras",
39513         "hn",
39514         "504"
39515       ],
39516       [
39517         "Hong Kong (香港)",
39518         "hk",
39519         "852"
39520       ],
39521       [
39522         "Hungary (Magyarország)",
39523         "hu",
39524         "36"
39525       ],
39526       [
39527         "Iceland (Ísland)",
39528         "is",
39529         "354"
39530       ],
39531       [
39532         "India (भारत)",
39533         "in",
39534         "91"
39535       ],
39536       [
39537         "Indonesia",
39538         "id",
39539         "62"
39540       ],
39541       [
39542         "Iran (‫ایران‬‎)",
39543         "ir",
39544         "98"
39545       ],
39546       [
39547         "Iraq (‫العراق‬‎)",
39548         "iq",
39549         "964"
39550       ],
39551       [
39552         "Ireland",
39553         "ie",
39554         "353"
39555       ],
39556       [
39557         "Isle of Man",
39558         "im",
39559         "44",
39560         2
39561       ],
39562       [
39563         "Israel (‫ישראל‬‎)",
39564         "il",
39565         "972"
39566       ],
39567       [
39568         "Italy (Italia)",
39569         "it",
39570         "39",
39571         0
39572       ],
39573       [
39574         "Jamaica",
39575         "jm",
39576         "1876"
39577       ],
39578       [
39579         "Japan (日本)",
39580         "jp",
39581         "81"
39582       ],
39583       [
39584         "Jersey",
39585         "je",
39586         "44",
39587         3
39588       ],
39589       [
39590         "Jordan (‫الأردن‬‎)",
39591         "jo",
39592         "962"
39593       ],
39594       [
39595         "Kazakhstan (Казахстан)",
39596         "kz",
39597         "7",
39598         1
39599       ],
39600       [
39601         "Kenya",
39602         "ke",
39603         "254"
39604       ],
39605       [
39606         "Kiribati",
39607         "ki",
39608         "686"
39609       ],
39610       [
39611         "Kosovo",
39612         "xk",
39613         "383"
39614       ],
39615       [
39616         "Kuwait (‫الكويت‬‎)",
39617         "kw",
39618         "965"
39619       ],
39620       [
39621         "Kyrgyzstan (Кыргызстан)",
39622         "kg",
39623         "996"
39624       ],
39625       [
39626         "Laos (ລາວ)",
39627         "la",
39628         "856"
39629       ],
39630       [
39631         "Latvia (Latvija)",
39632         "lv",
39633         "371"
39634       ],
39635       [
39636         "Lebanon (‫لبنان‬‎)",
39637         "lb",
39638         "961"
39639       ],
39640       [
39641         "Lesotho",
39642         "ls",
39643         "266"
39644       ],
39645       [
39646         "Liberia",
39647         "lr",
39648         "231"
39649       ],
39650       [
39651         "Libya (‫ليبيا‬‎)",
39652         "ly",
39653         "218"
39654       ],
39655       [
39656         "Liechtenstein",
39657         "li",
39658         "423"
39659       ],
39660       [
39661         "Lithuania (Lietuva)",
39662         "lt",
39663         "370"
39664       ],
39665       [
39666         "Luxembourg",
39667         "lu",
39668         "352"
39669       ],
39670       [
39671         "Macau (澳門)",
39672         "mo",
39673         "853"
39674       ],
39675       [
39676         "Macedonia (FYROM) (Македонија)",
39677         "mk",
39678         "389"
39679       ],
39680       [
39681         "Madagascar (Madagasikara)",
39682         "mg",
39683         "261"
39684       ],
39685       [
39686         "Malawi",
39687         "mw",
39688         "265"
39689       ],
39690       [
39691         "Malaysia",
39692         "my",
39693         "60"
39694       ],
39695       [
39696         "Maldives",
39697         "mv",
39698         "960"
39699       ],
39700       [
39701         "Mali",
39702         "ml",
39703         "223"
39704       ],
39705       [
39706         "Malta",
39707         "mt",
39708         "356"
39709       ],
39710       [
39711         "Marshall Islands",
39712         "mh",
39713         "692"
39714       ],
39715       [
39716         "Martinique",
39717         "mq",
39718         "596"
39719       ],
39720       [
39721         "Mauritania (‫موريتانيا‬‎)",
39722         "mr",
39723         "222"
39724       ],
39725       [
39726         "Mauritius (Moris)",
39727         "mu",
39728         "230"
39729       ],
39730       [
39731         "Mayotte",
39732         "yt",
39733         "262",
39734         1
39735       ],
39736       [
39737         "Mexico (México)",
39738         "mx",
39739         "52"
39740       ],
39741       [
39742         "Micronesia",
39743         "fm",
39744         "691"
39745       ],
39746       [
39747         "Moldova (Republica Moldova)",
39748         "md",
39749         "373"
39750       ],
39751       [
39752         "Monaco",
39753         "mc",
39754         "377"
39755       ],
39756       [
39757         "Mongolia (Монгол)",
39758         "mn",
39759         "976"
39760       ],
39761       [
39762         "Montenegro (Crna Gora)",
39763         "me",
39764         "382"
39765       ],
39766       [
39767         "Montserrat",
39768         "ms",
39769         "1664"
39770       ],
39771       [
39772         "Morocco (‫المغرب‬‎)",
39773         "ma",
39774         "212",
39775         0
39776       ],
39777       [
39778         "Mozambique (Moçambique)",
39779         "mz",
39780         "258"
39781       ],
39782       [
39783         "Myanmar (Burma) (မြန်မာ)",
39784         "mm",
39785         "95"
39786       ],
39787       [
39788         "Namibia (Namibië)",
39789         "na",
39790         "264"
39791       ],
39792       [
39793         "Nauru",
39794         "nr",
39795         "674"
39796       ],
39797       [
39798         "Nepal (नेपाल)",
39799         "np",
39800         "977"
39801       ],
39802       [
39803         "Netherlands (Nederland)",
39804         "nl",
39805         "31"
39806       ],
39807       [
39808         "New Caledonia (Nouvelle-Calédonie)",
39809         "nc",
39810         "687"
39811       ],
39812       [
39813         "New Zealand",
39814         "nz",
39815         "64"
39816       ],
39817       [
39818         "Nicaragua",
39819         "ni",
39820         "505"
39821       ],
39822       [
39823         "Niger (Nijar)",
39824         "ne",
39825         "227"
39826       ],
39827       [
39828         "Nigeria",
39829         "ng",
39830         "234"
39831       ],
39832       [
39833         "Niue",
39834         "nu",
39835         "683"
39836       ],
39837       [
39838         "Norfolk Island",
39839         "nf",
39840         "672"
39841       ],
39842       [
39843         "North Korea (조선 민주주의 인민 공화국)",
39844         "kp",
39845         "850"
39846       ],
39847       [
39848         "Northern Mariana Islands",
39849         "mp",
39850         "1670"
39851       ],
39852       [
39853         "Norway (Norge)",
39854         "no",
39855         "47",
39856         0
39857       ],
39858       [
39859         "Oman (‫عُمان‬‎)",
39860         "om",
39861         "968"
39862       ],
39863       [
39864         "Pakistan (‫پاکستان‬‎)",
39865         "pk",
39866         "92"
39867       ],
39868       [
39869         "Palau",
39870         "pw",
39871         "680"
39872       ],
39873       [
39874         "Palestine (‫فلسطين‬‎)",
39875         "ps",
39876         "970"
39877       ],
39878       [
39879         "Panama (Panamá)",
39880         "pa",
39881         "507"
39882       ],
39883       [
39884         "Papua New Guinea",
39885         "pg",
39886         "675"
39887       ],
39888       [
39889         "Paraguay",
39890         "py",
39891         "595"
39892       ],
39893       [
39894         "Peru (Perú)",
39895         "pe",
39896         "51"
39897       ],
39898       [
39899         "Philippines",
39900         "ph",
39901         "63"
39902       ],
39903       [
39904         "Poland (Polska)",
39905         "pl",
39906         "48"
39907       ],
39908       [
39909         "Portugal",
39910         "pt",
39911         "351"
39912       ],
39913       [
39914         "Puerto Rico",
39915         "pr",
39916         "1",
39917         3,
39918         ["787", "939"]
39919       ],
39920       [
39921         "Qatar (‫قطر‬‎)",
39922         "qa",
39923         "974"
39924       ],
39925       [
39926         "Réunion (La Réunion)",
39927         "re",
39928         "262",
39929         0
39930       ],
39931       [
39932         "Romania (România)",
39933         "ro",
39934         "40"
39935       ],
39936       [
39937         "Russia (Россия)",
39938         "ru",
39939         "7",
39940         0
39941       ],
39942       [
39943         "Rwanda",
39944         "rw",
39945         "250"
39946       ],
39947       [
39948         "Saint Barthélemy",
39949         "bl",
39950         "590",
39951         1
39952       ],
39953       [
39954         "Saint Helena",
39955         "sh",
39956         "290"
39957       ],
39958       [
39959         "Saint Kitts and Nevis",
39960         "kn",
39961         "1869"
39962       ],
39963       [
39964         "Saint Lucia",
39965         "lc",
39966         "1758"
39967       ],
39968       [
39969         "Saint Martin (Saint-Martin (partie française))",
39970         "mf",
39971         "590",
39972         2
39973       ],
39974       [
39975         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39976         "pm",
39977         "508"
39978       ],
39979       [
39980         "Saint Vincent and the Grenadines",
39981         "vc",
39982         "1784"
39983       ],
39984       [
39985         "Samoa",
39986         "ws",
39987         "685"
39988       ],
39989       [
39990         "San Marino",
39991         "sm",
39992         "378"
39993       ],
39994       [
39995         "São Tomé and Príncipe (São Tomé e Príncipe)",
39996         "st",
39997         "239"
39998       ],
39999       [
40000         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
40001         "sa",
40002         "966"
40003       ],
40004       [
40005         "Senegal (Sénégal)",
40006         "sn",
40007         "221"
40008       ],
40009       [
40010         "Serbia (Србија)",
40011         "rs",
40012         "381"
40013       ],
40014       [
40015         "Seychelles",
40016         "sc",
40017         "248"
40018       ],
40019       [
40020         "Sierra Leone",
40021         "sl",
40022         "232"
40023       ],
40024       [
40025         "Singapore",
40026         "sg",
40027         "65"
40028       ],
40029       [
40030         "Sint Maarten",
40031         "sx",
40032         "1721"
40033       ],
40034       [
40035         "Slovakia (Slovensko)",
40036         "sk",
40037         "421"
40038       ],
40039       [
40040         "Slovenia (Slovenija)",
40041         "si",
40042         "386"
40043       ],
40044       [
40045         "Solomon Islands",
40046         "sb",
40047         "677"
40048       ],
40049       [
40050         "Somalia (Soomaaliya)",
40051         "so",
40052         "252"
40053       ],
40054       [
40055         "South Africa",
40056         "za",
40057         "27"
40058       ],
40059       [
40060         "South Korea (대한민국)",
40061         "kr",
40062         "82"
40063       ],
40064       [
40065         "South Sudan (‫جنوب السودان‬‎)",
40066         "ss",
40067         "211"
40068       ],
40069       [
40070         "Spain (España)",
40071         "es",
40072         "34"
40073       ],
40074       [
40075         "Sri Lanka (ශ්‍රී ලංකාව)",
40076         "lk",
40077         "94"
40078       ],
40079       [
40080         "Sudan (‫السودان‬‎)",
40081         "sd",
40082         "249"
40083       ],
40084       [
40085         "Suriname",
40086         "sr",
40087         "597"
40088       ],
40089       [
40090         "Svalbard and Jan Mayen",
40091         "sj",
40092         "47",
40093         1
40094       ],
40095       [
40096         "Swaziland",
40097         "sz",
40098         "268"
40099       ],
40100       [
40101         "Sweden (Sverige)",
40102         "se",
40103         "46"
40104       ],
40105       [
40106         "Switzerland (Schweiz)",
40107         "ch",
40108         "41"
40109       ],
40110       [
40111         "Syria (‫سوريا‬‎)",
40112         "sy",
40113         "963"
40114       ],
40115       [
40116         "Taiwan (台灣)",
40117         "tw",
40118         "886"
40119       ],
40120       [
40121         "Tajikistan",
40122         "tj",
40123         "992"
40124       ],
40125       [
40126         "Tanzania",
40127         "tz",
40128         "255"
40129       ],
40130       [
40131         "Thailand (ไทย)",
40132         "th",
40133         "66"
40134       ],
40135       [
40136         "Timor-Leste",
40137         "tl",
40138         "670"
40139       ],
40140       [
40141         "Togo",
40142         "tg",
40143         "228"
40144       ],
40145       [
40146         "Tokelau",
40147         "tk",
40148         "690"
40149       ],
40150       [
40151         "Tonga",
40152         "to",
40153         "676"
40154       ],
40155       [
40156         "Trinidad and Tobago",
40157         "tt",
40158         "1868"
40159       ],
40160       [
40161         "Tunisia (‫تونس‬‎)",
40162         "tn",
40163         "216"
40164       ],
40165       [
40166         "Turkey (Türkiye)",
40167         "tr",
40168         "90"
40169       ],
40170       [
40171         "Turkmenistan",
40172         "tm",
40173         "993"
40174       ],
40175       [
40176         "Turks and Caicos Islands",
40177         "tc",
40178         "1649"
40179       ],
40180       [
40181         "Tuvalu",
40182         "tv",
40183         "688"
40184       ],
40185       [
40186         "U.S. Virgin Islands",
40187         "vi",
40188         "1340"
40189       ],
40190       [
40191         "Uganda",
40192         "ug",
40193         "256"
40194       ],
40195       [
40196         "Ukraine (Україна)",
40197         "ua",
40198         "380"
40199       ],
40200       [
40201         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40202         "ae",
40203         "971"
40204       ],
40205       [
40206         "United Kingdom",
40207         "gb",
40208         "44",
40209         0
40210       ],
40211       [
40212         "United States",
40213         "us",
40214         "1",
40215         0
40216       ],
40217       [
40218         "Uruguay",
40219         "uy",
40220         "598"
40221       ],
40222       [
40223         "Uzbekistan (Oʻzbekiston)",
40224         "uz",
40225         "998"
40226       ],
40227       [
40228         "Vanuatu",
40229         "vu",
40230         "678"
40231       ],
40232       [
40233         "Vatican City (Città del Vaticano)",
40234         "va",
40235         "39",
40236         1
40237       ],
40238       [
40239         "Venezuela",
40240         "ve",
40241         "58"
40242       ],
40243       [
40244         "Vietnam (Việt Nam)",
40245         "vn",
40246         "84"
40247       ],
40248       [
40249         "Wallis and Futuna (Wallis-et-Futuna)",
40250         "wf",
40251         "681"
40252       ],
40253       [
40254         "Western Sahara (‫الصحراء الغربية‬‎)",
40255         "eh",
40256         "212",
40257         1
40258       ],
40259       [
40260         "Yemen (‫اليمن‬‎)",
40261         "ye",
40262         "967"
40263       ],
40264       [
40265         "Zambia",
40266         "zm",
40267         "260"
40268       ],
40269       [
40270         "Zimbabwe",
40271         "zw",
40272         "263"
40273       ],
40274       [
40275         "Åland Islands",
40276         "ax",
40277         "358",
40278         1
40279       ]
40280   ];
40281   
40282   return d;
40283 }/**
40284 *    This script refer to:
40285 *    Title: International Telephone Input
40286 *    Author: Jack O'Connor
40287 *    Code version:  v12.1.12
40288 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40289 **/
40290
40291 /**
40292  * @class Roo.bootstrap.PhoneInput
40293  * @extends Roo.bootstrap.TriggerField
40294  * An input with International dial-code selection
40295  
40296  * @cfg {String} defaultDialCode default '+852'
40297  * @cfg {Array} preferedCountries default []
40298   
40299  * @constructor
40300  * Create a new PhoneInput.
40301  * @param {Object} config Configuration options
40302  */
40303
40304 Roo.bootstrap.PhoneInput = function(config) {
40305     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40306 };
40307
40308 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40309         
40310         listWidth: undefined,
40311         
40312         selectedClass: 'active',
40313         
40314         invalidClass : "has-warning",
40315         
40316         validClass: 'has-success',
40317         
40318         allowed: '0123456789',
40319         
40320         max_length: 15,
40321         
40322         /**
40323          * @cfg {String} defaultDialCode The default dial code when initializing the input
40324          */
40325         defaultDialCode: '+852',
40326         
40327         /**
40328          * @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
40329          */
40330         preferedCountries: false,
40331         
40332         getAutoCreate : function()
40333         {
40334             var data = Roo.bootstrap.PhoneInputData();
40335             var align = this.labelAlign || this.parentLabelAlign();
40336             var id = Roo.id();
40337             
40338             this.allCountries = [];
40339             this.dialCodeMapping = [];
40340             
40341             for (var i = 0; i < data.length; i++) {
40342               var c = data[i];
40343               this.allCountries[i] = {
40344                 name: c[0],
40345                 iso2: c[1],
40346                 dialCode: c[2],
40347                 priority: c[3] || 0,
40348                 areaCodes: c[4] || null
40349               };
40350               this.dialCodeMapping[c[2]] = {
40351                   name: c[0],
40352                   iso2: c[1],
40353                   priority: c[3] || 0,
40354                   areaCodes: c[4] || null
40355               };
40356             }
40357             
40358             var cfg = {
40359                 cls: 'form-group',
40360                 cn: []
40361             };
40362             
40363             var input =  {
40364                 tag: 'input',
40365                 id : id,
40366                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40367                 maxlength: this.max_length,
40368                 cls : 'form-control tel-input',
40369                 autocomplete: 'new-password'
40370             };
40371             
40372             var hiddenInput = {
40373                 tag: 'input',
40374                 type: 'hidden',
40375                 cls: 'hidden-tel-input'
40376             };
40377             
40378             if (this.name) {
40379                 hiddenInput.name = this.name;
40380             }
40381             
40382             if (this.disabled) {
40383                 input.disabled = true;
40384             }
40385             
40386             var flag_container = {
40387                 tag: 'div',
40388                 cls: 'flag-box',
40389                 cn: [
40390                     {
40391                         tag: 'div',
40392                         cls: 'flag'
40393                     },
40394                     {
40395                         tag: 'div',
40396                         cls: 'caret'
40397                     }
40398                 ]
40399             };
40400             
40401             var box = {
40402                 tag: 'div',
40403                 cls: this.hasFeedback ? 'has-feedback' : '',
40404                 cn: [
40405                     hiddenInput,
40406                     input,
40407                     {
40408                         tag: 'input',
40409                         cls: 'dial-code-holder',
40410                         disabled: true
40411                     }
40412                 ]
40413             };
40414             
40415             var container = {
40416                 cls: 'roo-select2-container input-group',
40417                 cn: [
40418                     flag_container,
40419                     box
40420                 ]
40421             };
40422             
40423             if (this.fieldLabel.length) {
40424                 var indicator = {
40425                     tag: 'i',
40426                     tooltip: 'This field is required'
40427                 };
40428                 
40429                 var label = {
40430                     tag: 'label',
40431                     'for':  id,
40432                     cls: 'control-label',
40433                     cn: []
40434                 };
40435                 
40436                 var label_text = {
40437                     tag: 'span',
40438                     html: this.fieldLabel
40439                 };
40440                 
40441                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40442                 label.cn = [
40443                     indicator,
40444                     label_text
40445                 ];
40446                 
40447                 if(this.indicatorpos == 'right') {
40448                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40449                     label.cn = [
40450                         label_text,
40451                         indicator
40452                     ];
40453                 }
40454                 
40455                 if(align == 'left') {
40456                     container = {
40457                         tag: 'div',
40458                         cn: [
40459                             container
40460                         ]
40461                     };
40462                     
40463                     if(this.labelWidth > 12){
40464                         label.style = "width: " + this.labelWidth + 'px';
40465                     }
40466                     if(this.labelWidth < 13 && this.labelmd == 0){
40467                         this.labelmd = this.labelWidth;
40468                     }
40469                     if(this.labellg > 0){
40470                         label.cls += ' col-lg-' + this.labellg;
40471                         input.cls += ' col-lg-' + (12 - this.labellg);
40472                     }
40473                     if(this.labelmd > 0){
40474                         label.cls += ' col-md-' + this.labelmd;
40475                         container.cls += ' col-md-' + (12 - this.labelmd);
40476                     }
40477                     if(this.labelsm > 0){
40478                         label.cls += ' col-sm-' + this.labelsm;
40479                         container.cls += ' col-sm-' + (12 - this.labelsm);
40480                     }
40481                     if(this.labelxs > 0){
40482                         label.cls += ' col-xs-' + this.labelxs;
40483                         container.cls += ' col-xs-' + (12 - this.labelxs);
40484                     }
40485                 }
40486             }
40487             
40488             cfg.cn = [
40489                 label,
40490                 container
40491             ];
40492             
40493             var settings = this;
40494             
40495             ['xs','sm','md','lg'].map(function(size){
40496                 if (settings[size]) {
40497                     cfg.cls += ' col-' + size + '-' + settings[size];
40498                 }
40499             });
40500             
40501             this.store = new Roo.data.Store({
40502                 proxy : new Roo.data.MemoryProxy({}),
40503                 reader : new Roo.data.JsonReader({
40504                     fields : [
40505                         {
40506                             'name' : 'name',
40507                             'type' : 'string'
40508                         },
40509                         {
40510                             'name' : 'iso2',
40511                             'type' : 'string'
40512                         },
40513                         {
40514                             'name' : 'dialCode',
40515                             'type' : 'string'
40516                         },
40517                         {
40518                             'name' : 'priority',
40519                             'type' : 'string'
40520                         },
40521                         {
40522                             'name' : 'areaCodes',
40523                             'type' : 'string'
40524                         }
40525                     ]
40526                 })
40527             });
40528             
40529             if(!this.preferedCountries) {
40530                 this.preferedCountries = [
40531                     'hk',
40532                     'gb',
40533                     'us'
40534                 ];
40535             }
40536             
40537             var p = this.preferedCountries.reverse();
40538             
40539             if(p) {
40540                 for (var i = 0; i < p.length; i++) {
40541                     for (var j = 0; j < this.allCountries.length; j++) {
40542                         if(this.allCountries[j].iso2 == p[i]) {
40543                             var t = this.allCountries[j];
40544                             this.allCountries.splice(j,1);
40545                             this.allCountries.unshift(t);
40546                         }
40547                     } 
40548                 }
40549             }
40550             
40551             this.store.proxy.data = {
40552                 success: true,
40553                 data: this.allCountries
40554             };
40555             
40556             return cfg;
40557         },
40558         
40559         initEvents : function()
40560         {
40561             this.createList();
40562             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40563             
40564             this.indicator = this.indicatorEl();
40565             this.flag = this.flagEl();
40566             this.dialCodeHolder = this.dialCodeHolderEl();
40567             
40568             this.trigger = this.el.select('div.flag-box',true).first();
40569             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40570             
40571             var _this = this;
40572             
40573             (function(){
40574                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40575                 _this.list.setWidth(lw);
40576             }).defer(100);
40577             
40578             this.list.on('mouseover', this.onViewOver, this);
40579             this.list.on('mousemove', this.onViewMove, this);
40580             this.inputEl().on("keyup", this.onKeyUp, this);
40581             this.inputEl().on("keypress", this.onKeyPress, this);
40582             
40583             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40584
40585             this.view = new Roo.View(this.list, this.tpl, {
40586                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40587             });
40588             
40589             this.view.on('click', this.onViewClick, this);
40590             this.setValue(this.defaultDialCode);
40591         },
40592         
40593         onTriggerClick : function(e)
40594         {
40595             Roo.log('trigger click');
40596             if(this.disabled){
40597                 return;
40598             }
40599             
40600             if(this.isExpanded()){
40601                 this.collapse();
40602                 this.hasFocus = false;
40603             }else {
40604                 this.store.load({});
40605                 this.hasFocus = true;
40606                 this.expand();
40607             }
40608         },
40609         
40610         isExpanded : function()
40611         {
40612             return this.list.isVisible();
40613         },
40614         
40615         collapse : function()
40616         {
40617             if(!this.isExpanded()){
40618                 return;
40619             }
40620             this.list.hide();
40621             Roo.get(document).un('mousedown', this.collapseIf, this);
40622             Roo.get(document).un('mousewheel', this.collapseIf, this);
40623             this.fireEvent('collapse', this);
40624             this.validate();
40625         },
40626         
40627         expand : function()
40628         {
40629             Roo.log('expand');
40630
40631             if(this.isExpanded() || !this.hasFocus){
40632                 return;
40633             }
40634             
40635             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40636             this.list.setWidth(lw);
40637             
40638             this.list.show();
40639             this.restrictHeight();
40640             
40641             Roo.get(document).on('mousedown', this.collapseIf, this);
40642             Roo.get(document).on('mousewheel', this.collapseIf, this);
40643             
40644             this.fireEvent('expand', this);
40645         },
40646         
40647         restrictHeight : function()
40648         {
40649             this.list.alignTo(this.inputEl(), this.listAlign);
40650             this.list.alignTo(this.inputEl(), this.listAlign);
40651         },
40652         
40653         onViewOver : function(e, t)
40654         {
40655             if(this.inKeyMode){
40656                 return;
40657             }
40658             var item = this.view.findItemFromChild(t);
40659             
40660             if(item){
40661                 var index = this.view.indexOf(item);
40662                 this.select(index, false);
40663             }
40664         },
40665
40666         // private
40667         onViewClick : function(view, doFocus, el, e)
40668         {
40669             var index = this.view.getSelectedIndexes()[0];
40670             
40671             var r = this.store.getAt(index);
40672             
40673             if(r){
40674                 this.onSelect(r, index);
40675             }
40676             if(doFocus !== false && !this.blockFocus){
40677                 this.inputEl().focus();
40678             }
40679         },
40680         
40681         onViewMove : function(e, t)
40682         {
40683             this.inKeyMode = false;
40684         },
40685         
40686         select : function(index, scrollIntoView)
40687         {
40688             this.selectedIndex = index;
40689             this.view.select(index);
40690             if(scrollIntoView !== false){
40691                 var el = this.view.getNode(index);
40692                 if(el){
40693                     this.list.scrollChildIntoView(el, false);
40694                 }
40695             }
40696         },
40697         
40698         createList : function()
40699         {
40700             this.list = Roo.get(document.body).createChild({
40701                 tag: 'ul',
40702                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40703                 style: 'display:none'
40704             });
40705             
40706             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40707         },
40708         
40709         collapseIf : function(e)
40710         {
40711             var in_combo  = e.within(this.el);
40712             var in_list =  e.within(this.list);
40713             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40714             
40715             if (in_combo || in_list || is_list) {
40716                 return;
40717             }
40718             this.collapse();
40719         },
40720         
40721         onSelect : function(record, index)
40722         {
40723             if(this.fireEvent('beforeselect', this, record, index) !== false){
40724                 
40725                 this.setFlagClass(record.data.iso2);
40726                 this.setDialCode(record.data.dialCode);
40727                 this.hasFocus = false;
40728                 this.collapse();
40729                 this.fireEvent('select', this, record, index);
40730             }
40731         },
40732         
40733         flagEl : function()
40734         {
40735             var flag = this.el.select('div.flag',true).first();
40736             if(!flag){
40737                 return false;
40738             }
40739             return flag;
40740         },
40741         
40742         dialCodeHolderEl : function()
40743         {
40744             var d = this.el.select('input.dial-code-holder',true).first();
40745             if(!d){
40746                 return false;
40747             }
40748             return d;
40749         },
40750         
40751         setDialCode : function(v)
40752         {
40753             this.dialCodeHolder.dom.value = '+'+v;
40754         },
40755         
40756         setFlagClass : function(n)
40757         {
40758             this.flag.dom.className = 'flag '+n;
40759         },
40760         
40761         getValue : function()
40762         {
40763             var v = this.inputEl().getValue();
40764             if(this.dialCodeHolder) {
40765                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40766             }
40767             return v;
40768         },
40769         
40770         setValue : function(v)
40771         {
40772             var d = this.getDialCode(v);
40773             
40774             //invalid dial code
40775             if(v.length == 0 || !d || d.length == 0) {
40776                 if(this.rendered){
40777                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40778                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40779                 }
40780                 return;
40781             }
40782             
40783             //valid dial code
40784             this.setFlagClass(this.dialCodeMapping[d].iso2);
40785             this.setDialCode(d);
40786             this.inputEl().dom.value = v.replace('+'+d,'');
40787             this.hiddenEl().dom.value = this.getValue();
40788             
40789             this.validate();
40790         },
40791         
40792         getDialCode : function(v)
40793         {
40794             v = v ||  '';
40795             
40796             if (v.length == 0) {
40797                 return this.dialCodeHolder.dom.value;
40798             }
40799             
40800             var dialCode = "";
40801             if (v.charAt(0) != "+") {
40802                 return false;
40803             }
40804             var numericChars = "";
40805             for (var i = 1; i < v.length; i++) {
40806               var c = v.charAt(i);
40807               if (!isNaN(c)) {
40808                 numericChars += c;
40809                 if (this.dialCodeMapping[numericChars]) {
40810                   dialCode = v.substr(1, i);
40811                 }
40812                 if (numericChars.length == 4) {
40813                   break;
40814                 }
40815               }
40816             }
40817             return dialCode;
40818         },
40819         
40820         reset : function()
40821         {
40822             this.setValue(this.defaultDialCode);
40823             this.validate();
40824         },
40825         
40826         hiddenEl : function()
40827         {
40828             return this.el.select('input.hidden-tel-input',true).first();
40829         },
40830         
40831         // after setting val
40832         onKeyUp : function(e){
40833             this.setValue(this.getValue());
40834         },
40835         
40836         onKeyPress : function(e){
40837             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40838                 e.stopEvent();
40839             }
40840         }
40841         
40842 });
40843 /**
40844  * @class Roo.bootstrap.MoneyField
40845  * @extends Roo.bootstrap.ComboBox
40846  * Bootstrap MoneyField class
40847  * 
40848  * @constructor
40849  * Create a new MoneyField.
40850  * @param {Object} config Configuration options
40851  */
40852
40853 Roo.bootstrap.MoneyField = function(config) {
40854     
40855     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40856     
40857 };
40858
40859 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40860     
40861     /**
40862      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40863      */
40864     allowDecimals : true,
40865     /**
40866      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40867      */
40868     decimalSeparator : ".",
40869     /**
40870      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40871      */
40872     decimalPrecision : 0,
40873     /**
40874      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40875      */
40876     allowNegative : true,
40877     /**
40878      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40879      */
40880     allowZero: true,
40881     /**
40882      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40883      */
40884     minValue : Number.NEGATIVE_INFINITY,
40885     /**
40886      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40887      */
40888     maxValue : Number.MAX_VALUE,
40889     /**
40890      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40891      */
40892     minText : "The minimum value for this field is {0}",
40893     /**
40894      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40895      */
40896     maxText : "The maximum value for this field is {0}",
40897     /**
40898      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40899      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40900      */
40901     nanText : "{0} is not a valid number",
40902     /**
40903      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40904      */
40905     castInt : true,
40906     /**
40907      * @cfg {String} defaults currency of the MoneyField
40908      * value should be in lkey
40909      */
40910     defaultCurrency : false,
40911     /**
40912      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40913      */
40914     thousandsDelimiter : false,
40915     /**
40916      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40917      */
40918     max_length: false,
40919     
40920     inputlg : 9,
40921     inputmd : 9,
40922     inputsm : 9,
40923     inputxs : 6,
40924     
40925     store : false,
40926     
40927     getAutoCreate : function()
40928     {
40929         var align = this.labelAlign || this.parentLabelAlign();
40930         
40931         var id = Roo.id();
40932
40933         var cfg = {
40934             cls: 'form-group',
40935             cn: []
40936         };
40937
40938         var input =  {
40939             tag: 'input',
40940             id : id,
40941             cls : 'form-control roo-money-amount-input',
40942             autocomplete: 'new-password'
40943         };
40944         
40945         var hiddenInput = {
40946             tag: 'input',
40947             type: 'hidden',
40948             id: Roo.id(),
40949             cls: 'hidden-number-input'
40950         };
40951         
40952         if(this.max_length) {
40953             input.maxlength = this.max_length; 
40954         }
40955         
40956         if (this.name) {
40957             hiddenInput.name = this.name;
40958         }
40959
40960         if (this.disabled) {
40961             input.disabled = true;
40962         }
40963
40964         var clg = 12 - this.inputlg;
40965         var cmd = 12 - this.inputmd;
40966         var csm = 12 - this.inputsm;
40967         var cxs = 12 - this.inputxs;
40968         
40969         var container = {
40970             tag : 'div',
40971             cls : 'row roo-money-field',
40972             cn : [
40973                 {
40974                     tag : 'div',
40975                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40976                     cn : [
40977                         {
40978                             tag : 'div',
40979                             cls: 'roo-select2-container input-group',
40980                             cn: [
40981                                 {
40982                                     tag : 'input',
40983                                     cls : 'form-control roo-money-currency-input',
40984                                     autocomplete: 'new-password',
40985                                     readOnly : 1,
40986                                     name : this.currencyName
40987                                 },
40988                                 {
40989                                     tag :'span',
40990                                     cls : 'input-group-addon',
40991                                     cn : [
40992                                         {
40993                                             tag: 'span',
40994                                             cls: 'caret'
40995                                         }
40996                                     ]
40997                                 }
40998                             ]
40999                         }
41000                     ]
41001                 },
41002                 {
41003                     tag : 'div',
41004                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41005                     cn : [
41006                         {
41007                             tag: 'div',
41008                             cls: this.hasFeedback ? 'has-feedback' : '',
41009                             cn: [
41010                                 input
41011                             ]
41012                         }
41013                     ]
41014                 }
41015             ]
41016             
41017         };
41018         
41019         if (this.fieldLabel.length) {
41020             var indicator = {
41021                 tag: 'i',
41022                 tooltip: 'This field is required'
41023             };
41024
41025             var label = {
41026                 tag: 'label',
41027                 'for':  id,
41028                 cls: 'control-label',
41029                 cn: []
41030             };
41031
41032             var label_text = {
41033                 tag: 'span',
41034                 html: this.fieldLabel
41035             };
41036
41037             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41038             label.cn = [
41039                 indicator,
41040                 label_text
41041             ];
41042
41043             if(this.indicatorpos == 'right') {
41044                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41045                 label.cn = [
41046                     label_text,
41047                     indicator
41048                 ];
41049             }
41050
41051             if(align == 'left') {
41052                 container = {
41053                     tag: 'div',
41054                     cn: [
41055                         container
41056                     ]
41057                 };
41058
41059                 if(this.labelWidth > 12){
41060                     label.style = "width: " + this.labelWidth + 'px';
41061                 }
41062                 if(this.labelWidth < 13 && this.labelmd == 0){
41063                     this.labelmd = this.labelWidth;
41064                 }
41065                 if(this.labellg > 0){
41066                     label.cls += ' col-lg-' + this.labellg;
41067                     input.cls += ' col-lg-' + (12 - this.labellg);
41068                 }
41069                 if(this.labelmd > 0){
41070                     label.cls += ' col-md-' + this.labelmd;
41071                     container.cls += ' col-md-' + (12 - this.labelmd);
41072                 }
41073                 if(this.labelsm > 0){
41074                     label.cls += ' col-sm-' + this.labelsm;
41075                     container.cls += ' col-sm-' + (12 - this.labelsm);
41076                 }
41077                 if(this.labelxs > 0){
41078                     label.cls += ' col-xs-' + this.labelxs;
41079                     container.cls += ' col-xs-' + (12 - this.labelxs);
41080                 }
41081             }
41082         }
41083
41084         cfg.cn = [
41085             label,
41086             container,
41087             hiddenInput
41088         ];
41089         
41090         var settings = this;
41091
41092         ['xs','sm','md','lg'].map(function(size){
41093             if (settings[size]) {
41094                 cfg.cls += ' col-' + size + '-' + settings[size];
41095             }
41096         });
41097         
41098         return cfg;
41099     },
41100     
41101     initEvents : function()
41102     {
41103         this.indicator = this.indicatorEl();
41104         
41105         this.initCurrencyEvent();
41106         
41107         this.initNumberEvent();
41108     },
41109     
41110     initCurrencyEvent : function()
41111     {
41112         if (!this.store) {
41113             throw "can not find store for combo";
41114         }
41115         
41116         this.store = Roo.factory(this.store, Roo.data);
41117         this.store.parent = this;
41118         
41119         this.createList();
41120         
41121         this.triggerEl = this.el.select('.input-group-addon', true).first();
41122         
41123         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41124         
41125         var _this = this;
41126         
41127         (function(){
41128             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41129             _this.list.setWidth(lw);
41130         }).defer(100);
41131         
41132         this.list.on('mouseover', this.onViewOver, this);
41133         this.list.on('mousemove', this.onViewMove, this);
41134         this.list.on('scroll', this.onViewScroll, this);
41135         
41136         if(!this.tpl){
41137             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41138         }
41139         
41140         this.view = new Roo.View(this.list, this.tpl, {
41141             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41142         });
41143         
41144         this.view.on('click', this.onViewClick, this);
41145         
41146         this.store.on('beforeload', this.onBeforeLoad, this);
41147         this.store.on('load', this.onLoad, this);
41148         this.store.on('loadexception', this.onLoadException, this);
41149         
41150         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41151             "up" : function(e){
41152                 this.inKeyMode = true;
41153                 this.selectPrev();
41154             },
41155
41156             "down" : function(e){
41157                 if(!this.isExpanded()){
41158                     this.onTriggerClick();
41159                 }else{
41160                     this.inKeyMode = true;
41161                     this.selectNext();
41162                 }
41163             },
41164
41165             "enter" : function(e){
41166                 this.collapse();
41167                 
41168                 if(this.fireEvent("specialkey", this, e)){
41169                     this.onViewClick(false);
41170                 }
41171                 
41172                 return true;
41173             },
41174
41175             "esc" : function(e){
41176                 this.collapse();
41177             },
41178
41179             "tab" : function(e){
41180                 this.collapse();
41181                 
41182                 if(this.fireEvent("specialkey", this, e)){
41183                     this.onViewClick(false);
41184                 }
41185                 
41186                 return true;
41187             },
41188
41189             scope : this,
41190
41191             doRelay : function(foo, bar, hname){
41192                 if(hname == 'down' || this.scope.isExpanded()){
41193                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41194                 }
41195                 return true;
41196             },
41197
41198             forceKeyDown: true
41199         });
41200         
41201         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41202         
41203     },
41204     
41205     initNumberEvent : function(e)
41206     {
41207         this.inputEl().on("keydown" , this.fireKey,  this);
41208         this.inputEl().on("focus", this.onFocus,  this);
41209         this.inputEl().on("blur", this.onBlur,  this);
41210         
41211         this.inputEl().relayEvent('keyup', this);
41212         
41213         if(this.indicator){
41214             this.indicator.addClass('invisible');
41215         }
41216  
41217         this.originalValue = this.getValue();
41218         
41219         if(this.validationEvent == 'keyup'){
41220             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41221             this.inputEl().on('keyup', this.filterValidation, this);
41222         }
41223         else if(this.validationEvent !== false){
41224             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41225         }
41226         
41227         if(this.selectOnFocus){
41228             this.on("focus", this.preFocus, this);
41229             
41230         }
41231         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41232             this.inputEl().on("keypress", this.filterKeys, this);
41233         } else {
41234             this.inputEl().relayEvent('keypress', this);
41235         }
41236         
41237         var allowed = "0123456789";
41238         
41239         if(this.allowDecimals){
41240             allowed += this.decimalSeparator;
41241         }
41242         
41243         if(this.allowNegative){
41244             allowed += "-";
41245         }
41246         
41247         if(this.thousandsDelimiter) {
41248             allowed += ",";
41249         }
41250         
41251         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41252         
41253         var keyPress = function(e){
41254             
41255             var k = e.getKey();
41256             
41257             var c = e.getCharCode();
41258             
41259             if(
41260                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41261                     allowed.indexOf(String.fromCharCode(c)) === -1
41262             ){
41263                 e.stopEvent();
41264                 return;
41265             }
41266             
41267             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41268                 return;
41269             }
41270             
41271             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41272                 e.stopEvent();
41273             }
41274         };
41275         
41276         this.inputEl().on("keypress", keyPress, this);
41277         
41278     },
41279     
41280     onTriggerClick : function(e)
41281     {   
41282         if(this.disabled){
41283             return;
41284         }
41285         
41286         this.page = 0;
41287         this.loadNext = false;
41288         
41289         if(this.isExpanded()){
41290             this.collapse();
41291             return;
41292         }
41293         
41294         this.hasFocus = true;
41295         
41296         if(this.triggerAction == 'all') {
41297             this.doQuery(this.allQuery, true);
41298             return;
41299         }
41300         
41301         this.doQuery(this.getRawValue());
41302     },
41303     
41304     getCurrency : function()
41305     {   
41306         var v = this.currencyEl().getValue();
41307         
41308         return v;
41309     },
41310     
41311     restrictHeight : function()
41312     {
41313         this.list.alignTo(this.currencyEl(), this.listAlign);
41314         this.list.alignTo(this.currencyEl(), this.listAlign);
41315     },
41316     
41317     onViewClick : function(view, doFocus, el, e)
41318     {
41319         var index = this.view.getSelectedIndexes()[0];
41320         
41321         var r = this.store.getAt(index);
41322         
41323         if(r){
41324             this.onSelect(r, index);
41325         }
41326     },
41327     
41328     onSelect : function(record, index){
41329         
41330         if(this.fireEvent('beforeselect', this, record, index) !== false){
41331         
41332             this.setFromCurrencyData(index > -1 ? record.data : false);
41333             
41334             this.collapse();
41335             
41336             this.fireEvent('select', this, record, index);
41337         }
41338     },
41339     
41340     setFromCurrencyData : function(o)
41341     {
41342         var currency = '';
41343         
41344         this.lastCurrency = o;
41345         
41346         if (this.currencyField) {
41347             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41348         } else {
41349             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41350         }
41351         
41352         this.lastSelectionText = currency;
41353         
41354         //setting default currency
41355         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41356             this.setCurrency(this.defaultCurrency);
41357             return;
41358         }
41359         
41360         this.setCurrency(currency);
41361     },
41362     
41363     setFromData : function(o)
41364     {
41365         var c = {};
41366         
41367         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41368         
41369         this.setFromCurrencyData(c);
41370         
41371         var value = '';
41372         
41373         if (this.name) {
41374             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41375         } else {
41376             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41377         }
41378         
41379         this.setValue(value);
41380         
41381     },
41382     
41383     setCurrency : function(v)
41384     {   
41385         this.currencyValue = v;
41386         
41387         if(this.rendered){
41388             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41389             this.validate();
41390         }
41391     },
41392     
41393     setValue : function(v)
41394     {
41395         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41396         
41397         this.value = v;
41398         
41399         if(this.rendered){
41400             
41401             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41402             
41403             this.inputEl().dom.value = (v == '') ? '' :
41404                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41405             
41406             if(!this.allowZero && v === '0') {
41407                 this.hiddenEl().dom.value = '';
41408                 this.inputEl().dom.value = '';
41409             }
41410             
41411             this.validate();
41412         }
41413     },
41414     
41415     getRawValue : function()
41416     {
41417         var v = this.inputEl().getValue();
41418         
41419         return v;
41420     },
41421     
41422     getValue : function()
41423     {
41424         return this.fixPrecision(this.parseValue(this.getRawValue()));
41425     },
41426     
41427     parseValue : function(value)
41428     {
41429         if(this.thousandsDelimiter) {
41430             value += "";
41431             r = new RegExp(",", "g");
41432             value = value.replace(r, "");
41433         }
41434         
41435         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41436         return isNaN(value) ? '' : value;
41437         
41438     },
41439     
41440     fixPrecision : function(value)
41441     {
41442         if(this.thousandsDelimiter) {
41443             value += "";
41444             r = new RegExp(",", "g");
41445             value = value.replace(r, "");
41446         }
41447         
41448         var nan = isNaN(value);
41449         
41450         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41451             return nan ? '' : value;
41452         }
41453         return parseFloat(value).toFixed(this.decimalPrecision);
41454     },
41455     
41456     decimalPrecisionFcn : function(v)
41457     {
41458         return Math.floor(v);
41459     },
41460     
41461     validateValue : function(value)
41462     {
41463         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41464             return false;
41465         }
41466         
41467         var num = this.parseValue(value);
41468         
41469         if(isNaN(num)){
41470             this.markInvalid(String.format(this.nanText, value));
41471             return false;
41472         }
41473         
41474         if(num < this.minValue){
41475             this.markInvalid(String.format(this.minText, this.minValue));
41476             return false;
41477         }
41478         
41479         if(num > this.maxValue){
41480             this.markInvalid(String.format(this.maxText, this.maxValue));
41481             return false;
41482         }
41483         
41484         return true;
41485     },
41486     
41487     validate : function()
41488     {
41489         if(this.disabled || this.allowBlank){
41490             this.markValid();
41491             return true;
41492         }
41493         
41494         var currency = this.getCurrency();
41495         
41496         if(this.validateValue(this.getRawValue()) && currency.length){
41497             this.markValid();
41498             return true;
41499         }
41500         
41501         this.markInvalid();
41502         return false;
41503     },
41504     
41505     getName: function()
41506     {
41507         return this.name;
41508     },
41509     
41510     beforeBlur : function()
41511     {
41512         if(!this.castInt){
41513             return;
41514         }
41515         
41516         var v = this.parseValue(this.getRawValue());
41517         
41518         if(v || v == 0){
41519             this.setValue(v);
41520         }
41521     },
41522     
41523     onBlur : function()
41524     {
41525         this.beforeBlur();
41526         
41527         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41528             //this.el.removeClass(this.focusClass);
41529         }
41530         
41531         this.hasFocus = false;
41532         
41533         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41534             this.validate();
41535         }
41536         
41537         var v = this.getValue();
41538         
41539         if(String(v) !== String(this.startValue)){
41540             this.fireEvent('change', this, v, this.startValue);
41541         }
41542         
41543         this.fireEvent("blur", this);
41544     },
41545     
41546     inputEl : function()
41547     {
41548         return this.el.select('.roo-money-amount-input', true).first();
41549     },
41550     
41551     currencyEl : function()
41552     {
41553         return this.el.select('.roo-money-currency-input', true).first();
41554     },
41555     
41556     hiddenEl : function()
41557     {
41558         return this.el.select('input.hidden-number-input',true).first();
41559     }
41560     
41561 });/**
41562  * @class Roo.bootstrap.BezierSignature
41563  * @extends Roo.bootstrap.Component
41564  * Bootstrap BezierSignature class
41565  * This script refer to:
41566  *    Title: Signature Pad
41567  *    Author: szimek
41568  *    Availability: https://github.com/szimek/signature_pad
41569  *
41570  * @constructor
41571  * Create a new BezierSignature
41572  * @param {Object} config The config object
41573  */
41574
41575 Roo.bootstrap.BezierSignature = function(config){
41576     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41577     this.addEvents({
41578         "resize" : true
41579     });
41580 };
41581
41582 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41583 {
41584      
41585     curve_data: [],
41586     
41587     is_empty: true,
41588     
41589     mouse_btn_down: true,
41590     
41591     /**
41592      * @cfg {int} canvas height
41593      */
41594     canvas_height: '200px',
41595     
41596     /**
41597      * @cfg {float|function} Radius of a single dot.
41598      */ 
41599     dot_size: false,
41600     
41601     /**
41602      * @cfg {float} Minimum width of a line. Defaults to 0.5.
41603      */
41604     min_width: 0.5,
41605     
41606     /**
41607      * @cfg {float} Maximum width of a line. Defaults to 2.5.
41608      */
41609     max_width: 2.5,
41610     
41611     /**
41612      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41613      */
41614     throttle: 16,
41615     
41616     /**
41617      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41618      */
41619     min_distance: 5,
41620     
41621     /**
41622      * @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.
41623      */
41624     bg_color: 'rgba(0, 0, 0, 0)',
41625     
41626     /**
41627      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41628      */
41629     dot_color: 'black',
41630     
41631     /**
41632      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41633      */ 
41634     velocity_filter_weight: 0.7,
41635     
41636     /**
41637      * @cfg {function} Callback when stroke begin. 
41638      */
41639     onBegin: false,
41640     
41641     /**
41642      * @cfg {function} Callback when stroke end.
41643      */
41644     onEnd: false,
41645     
41646     getAutoCreate : function()
41647     {
41648         var cls = 'roo-signature column';
41649         
41650         if(this.cls){
41651             cls += ' ' + this.cls;
41652         }
41653         
41654         var col_sizes = [
41655             'lg',
41656             'md',
41657             'sm',
41658             'xs'
41659         ];
41660         
41661         for(var i = 0; i < col_sizes.length; i++) {
41662             if(this[col_sizes[i]]) {
41663                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41664             }
41665         }
41666         
41667         var cfg = {
41668             tag: 'div',
41669             cls: cls,
41670             cn: [
41671                 {
41672                     tag: 'div',
41673                     cls: 'roo-signature-body',
41674                     cn: [
41675                         {
41676                             tag: 'canvas',
41677                             cls: 'roo-signature-body-canvas',
41678                             height: this.canvas_height,
41679                             width: this.canvas_width
41680                         }
41681                     ]
41682                 },
41683                 {
41684                     tag: 'input',
41685                     type: 'file',
41686                     style: 'display: none'
41687                 }
41688             ]
41689         };
41690         
41691         return cfg;
41692     },
41693     
41694     initEvents: function() 
41695     {
41696         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41697         
41698         var canvas = this.canvasEl();
41699         
41700         // mouse && touch event swapping...
41701         canvas.dom.style.touchAction = 'none';
41702         canvas.dom.style.msTouchAction = 'none';
41703         
41704         this.mouse_btn_down = false;
41705         canvas.on('mousedown', this._handleMouseDown, this);
41706         canvas.on('mousemove', this._handleMouseMove, this);
41707         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41708         
41709         if (window.PointerEvent) {
41710             canvas.on('pointerdown', this._handleMouseDown, this);
41711             canvas.on('pointermove', this._handleMouseMove, this);
41712             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41713         }
41714         
41715         if ('ontouchstart' in window) {
41716             canvas.on('touchstart', this._handleTouchStart, this);
41717             canvas.on('touchmove', this._handleTouchMove, this);
41718             canvas.on('touchend', this._handleTouchEnd, this);
41719         }
41720         
41721         Roo.EventManager.onWindowResize(this.resize, this, true);
41722         
41723         // file input event
41724         this.fileEl().on('change', this.uploadImage, this);
41725         
41726         this.clear();
41727         
41728         this.resize();
41729     },
41730     
41731     resize: function(){
41732         
41733         var canvas = this.canvasEl().dom;
41734         var ctx = this.canvasElCtx();
41735         var img_data = false;
41736         
41737         if(canvas.width > 0) {
41738             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41739         }
41740         // setting canvas width will clean img data
41741         canvas.width = 0;
41742         
41743         var style = window.getComputedStyle ? 
41744             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41745             
41746         var padding_left = parseInt(style.paddingLeft) || 0;
41747         var padding_right = parseInt(style.paddingRight) || 0;
41748         
41749         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41750         
41751         if(img_data) {
41752             ctx.putImageData(img_data, 0, 0);
41753         }
41754     },
41755     
41756     _handleMouseDown: function(e)
41757     {
41758         if (e.browserEvent.which === 1) {
41759             this.mouse_btn_down = true;
41760             this.strokeBegin(e);
41761         }
41762     },
41763     
41764     _handleMouseMove: function (e)
41765     {
41766         if (this.mouse_btn_down) {
41767             this.strokeMoveUpdate(e);
41768         }
41769     },
41770     
41771     _handleMouseUp: function (e)
41772     {
41773         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41774             this.mouse_btn_down = false;
41775             this.strokeEnd(e);
41776         }
41777     },
41778     
41779     _handleTouchStart: function (e) {
41780         
41781         e.preventDefault();
41782         if (e.browserEvent.targetTouches.length === 1) {
41783             // var touch = e.browserEvent.changedTouches[0];
41784             // this.strokeBegin(touch);
41785             
41786              this.strokeBegin(e); // assume e catching the correct xy...
41787         }
41788     },
41789     
41790     _handleTouchMove: function (e) {
41791         e.preventDefault();
41792         // var touch = event.targetTouches[0];
41793         // _this._strokeMoveUpdate(touch);
41794         this.strokeMoveUpdate(e);
41795     },
41796     
41797     _handleTouchEnd: function (e) {
41798         var wasCanvasTouched = e.target === this.canvasEl().dom;
41799         if (wasCanvasTouched) {
41800             e.preventDefault();
41801             // var touch = event.changedTouches[0];
41802             // _this._strokeEnd(touch);
41803             this.strokeEnd(e);
41804         }
41805     },
41806     
41807     reset: function () {
41808         this._lastPoints = [];
41809         this._lastVelocity = 0;
41810         this._lastWidth = (this.min_width + this.max_width) / 2;
41811         this.canvasElCtx().fillStyle = this.dot_color;
41812     },
41813     
41814     strokeMoveUpdate: function(e)
41815     {
41816         this.strokeUpdate(e);
41817         
41818         if (this.throttle) {
41819             this.throttleStroke(this.strokeUpdate, this.throttle);
41820         }
41821         else {
41822             this.strokeUpdate(e);
41823         }
41824     },
41825     
41826     strokeBegin: function(e)
41827     {
41828         var newPointGroup = {
41829             color: this.dot_color,
41830             points: []
41831         };
41832         
41833         if (typeof this.onBegin === 'function') {
41834             this.onBegin(e);
41835         }
41836         
41837         this.curve_data.push(newPointGroup);
41838         this.reset();
41839         this.strokeUpdate(e);
41840     },
41841     
41842     strokeUpdate: function(e)
41843     {
41844         var rect = this.canvasEl().dom.getBoundingClientRect();
41845         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41846         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41847         var lastPoints = lastPointGroup.points;
41848         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41849         var isLastPointTooClose = lastPoint
41850             ? point.distanceTo(lastPoint) <= this.min_distance
41851             : false;
41852         var color = lastPointGroup.color;
41853         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41854             var curve = this.addPoint(point);
41855             if (!lastPoint) {
41856                 this.drawDot({color: color, point: point});
41857             }
41858             else if (curve) {
41859                 this.drawCurve({color: color, curve: curve});
41860             }
41861             lastPoints.push({
41862                 time: point.time,
41863                 x: point.x,
41864                 y: point.y
41865             });
41866         }
41867     },
41868     
41869     strokeEnd: function(e)
41870     {
41871         this.strokeUpdate(e);
41872         if (typeof this.onEnd === 'function') {
41873             this.onEnd(e);
41874         }
41875     },
41876     
41877     addPoint:  function (point) {
41878         var _lastPoints = this._lastPoints;
41879         _lastPoints.push(point);
41880         if (_lastPoints.length > 2) {
41881             if (_lastPoints.length === 3) {
41882                 _lastPoints.unshift(_lastPoints[0]);
41883             }
41884             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41885             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41886             _lastPoints.shift();
41887             return curve;
41888         }
41889         return null;
41890     },
41891     
41892     calculateCurveWidths: function (startPoint, endPoint) {
41893         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
41894             (1 - this.velocity_filter_weight) * this._lastVelocity;
41895
41896         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
41897         var widths = {
41898             end: newWidth,
41899             start: this._lastWidth
41900         };
41901         
41902         this._lastVelocity = velocity;
41903         this._lastWidth = newWidth;
41904         return widths;
41905     },
41906     
41907     drawDot: function (_a) {
41908         var color = _a.color, point = _a.point;
41909         var ctx = this.canvasElCtx();
41910         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
41911         ctx.beginPath();
41912         this.drawCurveSegment(point.x, point.y, width);
41913         ctx.closePath();
41914         ctx.fillStyle = color;
41915         ctx.fill();
41916     },
41917     
41918     drawCurve: function (_a) {
41919         var color = _a.color, curve = _a.curve;
41920         var ctx = this.canvasElCtx();
41921         var widthDelta = curve.endWidth - curve.startWidth;
41922         var drawSteps = Math.floor(curve.length()) * 2;
41923         ctx.beginPath();
41924         ctx.fillStyle = color;
41925         for (var i = 0; i < drawSteps; i += 1) {
41926         var t = i / drawSteps;
41927         var tt = t * t;
41928         var ttt = tt * t;
41929         var u = 1 - t;
41930         var uu = u * u;
41931         var uuu = uu * u;
41932         var x = uuu * curve.startPoint.x;
41933         x += 3 * uu * t * curve.control1.x;
41934         x += 3 * u * tt * curve.control2.x;
41935         x += ttt * curve.endPoint.x;
41936         var y = uuu * curve.startPoint.y;
41937         y += 3 * uu * t * curve.control1.y;
41938         y += 3 * u * tt * curve.control2.y;
41939         y += ttt * curve.endPoint.y;
41940         var width = curve.startWidth + ttt * widthDelta;
41941         this.drawCurveSegment(x, y, width);
41942         }
41943         ctx.closePath();
41944         ctx.fill();
41945     },
41946     
41947     drawCurveSegment: function (x, y, width) {
41948         var ctx = this.canvasElCtx();
41949         ctx.moveTo(x, y);
41950         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
41951         this.is_empty = false;
41952     },
41953     
41954     clear: function()
41955     {
41956         var ctx = this.canvasElCtx();
41957         var canvas = this.canvasEl().dom;
41958         ctx.fillStyle = this.bg_color;
41959         ctx.clearRect(0, 0, canvas.width, canvas.height);
41960         ctx.fillRect(0, 0, canvas.width, canvas.height);
41961         this.curve_data = [];
41962         this.reset();
41963         this.is_empty = true;
41964     },
41965     
41966     fileEl: function()
41967     {
41968         return  this.el.select('input',true).first();
41969     },
41970     
41971     canvasEl: function()
41972     {
41973         return this.el.select('canvas',true).first();
41974     },
41975     
41976     canvasElCtx: function()
41977     {
41978         return this.el.select('canvas',true).first().dom.getContext('2d');
41979     },
41980     
41981     getImage: function(type)
41982     {
41983         if(this.is_empty) {
41984             return false;
41985         }
41986         
41987         // encryption ?
41988         return this.canvasEl().dom.toDataURL('image/'+type, 1);
41989     },
41990     
41991     drawFromImage: function(img_src)
41992     {
41993         var img = new Image();
41994         
41995         img.onload = function(){
41996             this.canvasElCtx().drawImage(img, 0, 0);
41997         }.bind(this);
41998         
41999         img.src = img_src;
42000         
42001         this.is_empty = false;
42002     },
42003     
42004     selectImage: function()
42005     {
42006         this.fileEl().dom.click();
42007     },
42008     
42009     uploadImage: function(e)
42010     {
42011         var reader = new FileReader();
42012         
42013         reader.onload = function(e){
42014             var img = new Image();
42015             img.onload = function(){
42016                 this.reset();
42017                 this.canvasElCtx().drawImage(img, 0, 0);
42018             }.bind(this);
42019             img.src = e.target.result;
42020         }.bind(this);
42021         
42022         reader.readAsDataURL(e.target.files[0]);
42023     },
42024     
42025     // Bezier Point Constructor
42026     Point: (function () {
42027         function Point(x, y, time) {
42028             this.x = x;
42029             this.y = y;
42030             this.time = time || Date.now();
42031         }
42032         Point.prototype.distanceTo = function (start) {
42033             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42034         };
42035         Point.prototype.equals = function (other) {
42036             return this.x === other.x && this.y === other.y && this.time === other.time;
42037         };
42038         Point.prototype.velocityFrom = function (start) {
42039             return this.time !== start.time
42040             ? this.distanceTo(start) / (this.time - start.time)
42041             : 0;
42042         };
42043         return Point;
42044     }()),
42045     
42046     
42047     // Bezier Constructor
42048     Bezier: (function () {
42049         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42050             this.startPoint = startPoint;
42051             this.control2 = control2;
42052             this.control1 = control1;
42053             this.endPoint = endPoint;
42054             this.startWidth = startWidth;
42055             this.endWidth = endWidth;
42056         }
42057         Bezier.fromPoints = function (points, widths, scope) {
42058             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42059             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42060             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42061         };
42062         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42063             var dx1 = s1.x - s2.x;
42064             var dy1 = s1.y - s2.y;
42065             var dx2 = s2.x - s3.x;
42066             var dy2 = s2.y - s3.y;
42067             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42068             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42069             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42070             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42071             var dxm = m1.x - m2.x;
42072             var dym = m1.y - m2.y;
42073             var k = l2 / (l1 + l2);
42074             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42075             var tx = s2.x - cm.x;
42076             var ty = s2.y - cm.y;
42077             return {
42078                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42079                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42080             };
42081         };
42082         Bezier.prototype.length = function () {
42083             var steps = 10;
42084             var length = 0;
42085             var px;
42086             var py;
42087             for (var i = 0; i <= steps; i += 1) {
42088                 var t = i / steps;
42089                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42090                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42091                 if (i > 0) {
42092                     var xdiff = cx - px;
42093                     var ydiff = cy - py;
42094                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42095                 }
42096                 px = cx;
42097                 py = cy;
42098             }
42099             return length;
42100         };
42101         Bezier.prototype.point = function (t, start, c1, c2, end) {
42102             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42103             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42104             + (3.0 * c2 * (1.0 - t) * t * t)
42105             + (end * t * t * t);
42106         };
42107         return Bezier;
42108     }()),
42109     
42110     throttleStroke: function(fn, wait) {
42111       if (wait === void 0) { wait = 250; }
42112       var previous = 0;
42113       var timeout = null;
42114       var result;
42115       var storedContext;
42116       var storedArgs;
42117       var later = function () {
42118           previous = Date.now();
42119           timeout = null;
42120           result = fn.apply(storedContext, storedArgs);
42121           if (!timeout) {
42122               storedContext = null;
42123               storedArgs = [];
42124           }
42125       };
42126       return function wrapper() {
42127           var args = [];
42128           for (var _i = 0; _i < arguments.length; _i++) {
42129               args[_i] = arguments[_i];
42130           }
42131           var now = Date.now();
42132           var remaining = wait - (now - previous);
42133           storedContext = this;
42134           storedArgs = args;
42135           if (remaining <= 0 || remaining > wait) {
42136               if (timeout) {
42137                   clearTimeout(timeout);
42138                   timeout = null;
42139               }
42140               previous = now;
42141               result = fn.apply(storedContext, storedArgs);
42142               if (!timeout) {
42143                   storedContext = null;
42144                   storedArgs = [];
42145               }
42146           }
42147           else if (!timeout) {
42148               timeout = window.setTimeout(later, remaining);
42149           }
42150           return result;
42151       };
42152   }
42153   
42154 });
42155
42156  
42157
42158