49927bdd09f33431174e5c03397e3d33ff784d7a
[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) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
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                     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                         tag: 'span',
15380                         cls: 'caret'
15381             };
15382             
15383             if (this.caret != false) {
15384                 caret = {
15385                      tag: 'i',
15386                      cls: 'fa fa-' + this.caret
15387                 };
15388                 
15389             }
15390             
15391             combobox.cn.push({
15392                 tag :'span',
15393                 cls : 'input-group-addon input-group-append input-group-text btn' +
15394                     (Roo.bootstrap.version == 3 ? ' dropdown-toggle' : ''),
15395                 cn : [
15396                     caret,
15397                     {
15398                         tag: 'span',
15399                         cls: 'combobox-clear',
15400                         cn  : [
15401                             {
15402                                 tag : 'i',
15403                                 cls: 'icon-remove'
15404                             }
15405                         ]
15406                     }
15407                 ]
15408
15409             })
15410         }
15411         
15412         if(this.multiple){
15413             combobox.cls += ' roo-select2-container-multi';
15414         }
15415         
15416         var align = this.labelAlign || this.parentLabelAlign();
15417         
15418         if (align ==='left' && this.fieldLabel.length) {
15419
15420             cfg.cn = [
15421                 {
15422                    tag : 'i',
15423                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15424                    tooltip : 'This field is required'
15425                 },
15426                 {
15427                     tag: 'label',
15428                     cls : 'control-label col-form-label',
15429                     html : this.fieldLabel
15430
15431                 },
15432                 {
15433                     cls : '', 
15434                     cn: [
15435                         combobox
15436                     ]
15437                 }
15438             ];
15439             
15440             var labelCfg = cfg.cn[1];
15441             var contentCfg = cfg.cn[2];
15442             
15443
15444             if(this.indicatorpos == 'right'){
15445                 cfg.cn = [
15446                     {
15447                         tag: 'label',
15448                         'for' :  id,
15449                         cls : 'control-label col-form-label',
15450                         cn : [
15451                             {
15452                                 tag : 'span',
15453                                 html : this.fieldLabel
15454                             },
15455                             {
15456                                 tag : 'i',
15457                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15458                                 tooltip : 'This field is required'
15459                             }
15460                         ]
15461                     },
15462                     {
15463                         cls : "",
15464                         cn: [
15465                             combobox
15466                         ]
15467                     }
15468
15469                 ];
15470                 
15471                 labelCfg = cfg.cn[0];
15472                 contentCfg = cfg.cn[1];
15473             }
15474             
15475            
15476             
15477             if(this.labelWidth > 12){
15478                 labelCfg.style = "width: " + this.labelWidth + 'px';
15479             }
15480             
15481             if(this.labelWidth < 13 && this.labelmd == 0){
15482                 this.labelmd = this.labelWidth;
15483             }
15484             
15485             if(this.labellg > 0){
15486                 labelCfg.cls += ' col-lg-' + this.labellg;
15487                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15488             }
15489             
15490             if(this.labelmd > 0){
15491                 labelCfg.cls += ' col-md-' + this.labelmd;
15492                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15493             }
15494             
15495             if(this.labelsm > 0){
15496                 labelCfg.cls += ' col-sm-' + this.labelsm;
15497                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15498             }
15499             
15500             if(this.labelxs > 0){
15501                 labelCfg.cls += ' col-xs-' + this.labelxs;
15502                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15503             }
15504                 
15505                 
15506         } else if ( this.fieldLabel.length) {
15507             cfg.cn = [
15508                 {
15509                    tag : 'i',
15510                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15511                    tooltip : 'This field is required'
15512                 },
15513                 {
15514                     tag: 'label',
15515                     cls : 'control-label',
15516                     html : this.fieldLabel
15517
15518                 },
15519                 {
15520                     cls : '', 
15521                     cn: [
15522                         combobox
15523                     ]
15524                 }
15525             ];
15526             
15527             if(this.indicatorpos == 'right'){
15528                 cfg.cn = [
15529                     {
15530                         tag: 'label',
15531                         cls : 'control-label',
15532                         html : this.fieldLabel,
15533                         cn : [
15534                             {
15535                                tag : 'i',
15536                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15537                                tooltip : 'This field is required'
15538                             }
15539                         ]
15540                     },
15541                     {
15542                         cls : '', 
15543                         cn: [
15544                             combobox
15545                         ]
15546                     }
15547                 ];
15548             }
15549         } else {
15550             cfg.cn = combobox;    
15551         }
15552         
15553         
15554         var settings = this;
15555         
15556         ['xs','sm','md','lg'].map(function(size){
15557             if (settings[size]) {
15558                 cfg.cls += ' col-' + size + '-' + settings[size];
15559             }
15560         });
15561         
15562         return cfg;
15563     },
15564     
15565     initTouchView : function()
15566     {
15567         this.renderTouchView();
15568         
15569         this.touchViewEl.on('scroll', function(){
15570             this.el.dom.scrollTop = 0;
15571         }, this);
15572         
15573         this.originalValue = this.getValue();
15574         
15575         this.triggerEl = this.el.select('span.input-group-append',true).first();
15576         
15577         this.inputEl().on("click", this.showTouchView, this);
15578         if (this.triggerEl) {
15579             this.triggerEl.on("click", this.showTouchView, this);
15580         }
15581         
15582         
15583         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15584         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15585         
15586         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15587         
15588         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15589         this.store.on('load', this.onTouchViewLoad, this);
15590         this.store.on('loadexception', this.onTouchViewLoadException, this);
15591         
15592         if(this.hiddenName){
15593             
15594             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15595             
15596             this.hiddenField.dom.value =
15597                 this.hiddenValue !== undefined ? this.hiddenValue :
15598                 this.value !== undefined ? this.value : '';
15599         
15600             this.el.dom.removeAttribute('name');
15601             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15602         }
15603         
15604         if(this.multiple){
15605             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15606             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15607         }
15608         
15609         if(this.removable && !this.multiple){
15610             var close = this.closeTriggerEl();
15611             if(close){
15612                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15613                 close.on('click', this.removeBtnClick, this, close);
15614             }
15615         }
15616         /*
15617          * fix the bug in Safari iOS8
15618          */
15619         this.inputEl().on("focus", function(e){
15620             document.activeElement.blur();
15621         }, this);
15622         
15623         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15624         
15625         return;
15626         
15627         
15628     },
15629     
15630     renderTouchView : function()
15631     {
15632         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15633         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15634         
15635         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15636         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15637         
15638         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15639         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15640         this.touchViewBodyEl.setStyle('overflow', 'auto');
15641         
15642         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15643         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15644         
15645         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15646         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15647         
15648     },
15649     
15650     showTouchView : function()
15651     {
15652         if(this.disabled){
15653             return;
15654         }
15655         
15656         this.touchViewHeaderEl.hide();
15657
15658         if(this.modalTitle.length){
15659             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15660             this.touchViewHeaderEl.show();
15661         }
15662
15663         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15664         this.touchViewEl.show();
15665
15666         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15667         
15668         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15669         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15670
15671         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15672
15673         if(this.modalTitle.length){
15674             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15675         }
15676         
15677         this.touchViewBodyEl.setHeight(bodyHeight);
15678
15679         if(this.animate){
15680             var _this = this;
15681             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15682         }else{
15683             this.touchViewEl.addClass('in');
15684         }
15685         
15686         if(this._touchViewMask){
15687             Roo.get(document.body).addClass("x-body-masked");
15688             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15689             this._touchViewMask.setStyle('z-index', 10000);
15690             this._touchViewMask.addClass('show');
15691         }
15692         
15693         this.doTouchViewQuery();
15694         
15695     },
15696     
15697     hideTouchView : function()
15698     {
15699         this.touchViewEl.removeClass('in');
15700
15701         if(this.animate){
15702             var _this = this;
15703             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15704         }else{
15705             this.touchViewEl.setStyle('display', 'none');
15706         }
15707         
15708         if(this._touchViewMask){
15709             this._touchViewMask.removeClass('show');
15710             Roo.get(document.body).removeClass("x-body-masked");
15711         }
15712     },
15713     
15714     setTouchViewValue : function()
15715     {
15716         if(this.multiple){
15717             this.clearItem();
15718         
15719             var _this = this;
15720
15721             Roo.each(this.tickItems, function(o){
15722                 this.addItem(o);
15723             }, this);
15724         }
15725         
15726         this.hideTouchView();
15727     },
15728     
15729     doTouchViewQuery : function()
15730     {
15731         var qe = {
15732             query: '',
15733             forceAll: true,
15734             combo: this,
15735             cancel:false
15736         };
15737         
15738         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15739             return false;
15740         }
15741         
15742         if(!this.alwaysQuery || this.mode == 'local'){
15743             this.onTouchViewLoad();
15744             return;
15745         }
15746         
15747         this.store.load();
15748     },
15749     
15750     onTouchViewBeforeLoad : function(combo,opts)
15751     {
15752         return;
15753     },
15754
15755     // private
15756     onTouchViewLoad : function()
15757     {
15758         if(this.store.getCount() < 1){
15759             this.onTouchViewEmptyResults();
15760             return;
15761         }
15762         
15763         this.clearTouchView();
15764         
15765         var rawValue = this.getRawValue();
15766         
15767         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15768         
15769         this.tickItems = [];
15770         
15771         this.store.data.each(function(d, rowIndex){
15772             var row = this.touchViewListGroup.createChild(template);
15773             
15774             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15775                 row.addClass(d.data.cls);
15776             }
15777             
15778             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15779                 var cfg = {
15780                     data : d.data,
15781                     html : d.data[this.displayField]
15782                 };
15783                 
15784                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15785                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15786                 }
15787             }
15788             row.removeClass('selected');
15789             if(!this.multiple && this.valueField &&
15790                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15791             {
15792                 // radio buttons..
15793                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15794                 row.addClass('selected');
15795             }
15796             
15797             if(this.multiple && this.valueField &&
15798                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15799             {
15800                 
15801                 // checkboxes...
15802                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15803                 this.tickItems.push(d.data);
15804             }
15805             
15806             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15807             
15808         }, this);
15809         
15810         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15811         
15812         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15813
15814         if(this.modalTitle.length){
15815             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15816         }
15817
15818         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15819         
15820         if(this.mobile_restrict_height && listHeight < bodyHeight){
15821             this.touchViewBodyEl.setHeight(listHeight);
15822         }
15823         
15824         var _this = this;
15825         
15826         if(firstChecked && listHeight > bodyHeight){
15827             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15828         }
15829         
15830     },
15831     
15832     onTouchViewLoadException : function()
15833     {
15834         this.hideTouchView();
15835     },
15836     
15837     onTouchViewEmptyResults : function()
15838     {
15839         this.clearTouchView();
15840         
15841         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15842         
15843         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15844         
15845     },
15846     
15847     clearTouchView : function()
15848     {
15849         this.touchViewListGroup.dom.innerHTML = '';
15850     },
15851     
15852     onTouchViewClick : function(e, el, o)
15853     {
15854         e.preventDefault();
15855         
15856         var row = o.row;
15857         var rowIndex = o.rowIndex;
15858         
15859         var r = this.store.getAt(rowIndex);
15860         
15861         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15862             
15863             if(!this.multiple){
15864                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15865                     c.dom.removeAttribute('checked');
15866                 }, this);
15867
15868                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15869
15870                 this.setFromData(r.data);
15871
15872                 var close = this.closeTriggerEl();
15873
15874                 if(close){
15875                     close.show();
15876                 }
15877
15878                 this.hideTouchView();
15879
15880                 this.fireEvent('select', this, r, rowIndex);
15881
15882                 return;
15883             }
15884
15885             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15886                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15887                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15888                 return;
15889             }
15890
15891             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15892             this.addItem(r.data);
15893             this.tickItems.push(r.data);
15894         }
15895     },
15896     
15897     getAutoCreateNativeIOS : function()
15898     {
15899         var cfg = {
15900             cls: 'form-group' //input-group,
15901         };
15902         
15903         var combobox =  {
15904             tag: 'select',
15905             cls : 'roo-ios-select'
15906         };
15907         
15908         if (this.name) {
15909             combobox.name = this.name;
15910         }
15911         
15912         if (this.disabled) {
15913             combobox.disabled = true;
15914         }
15915         
15916         var settings = this;
15917         
15918         ['xs','sm','md','lg'].map(function(size){
15919             if (settings[size]) {
15920                 cfg.cls += ' col-' + size + '-' + settings[size];
15921             }
15922         });
15923         
15924         cfg.cn = combobox;
15925         
15926         return cfg;
15927         
15928     },
15929     
15930     initIOSView : function()
15931     {
15932         this.store.on('load', this.onIOSViewLoad, this);
15933         
15934         return;
15935     },
15936     
15937     onIOSViewLoad : function()
15938     {
15939         if(this.store.getCount() < 1){
15940             return;
15941         }
15942         
15943         this.clearIOSView();
15944         
15945         if(this.allowBlank) {
15946             
15947             var default_text = '-- SELECT --';
15948             
15949             if(this.placeholder.length){
15950                 default_text = this.placeholder;
15951             }
15952             
15953             if(this.emptyTitle.length){
15954                 default_text += ' - ' + this.emptyTitle + ' -';
15955             }
15956             
15957             var opt = this.inputEl().createChild({
15958                 tag: 'option',
15959                 value : 0,
15960                 html : default_text
15961             });
15962             
15963             var o = {};
15964             o[this.valueField] = 0;
15965             o[this.displayField] = default_text;
15966             
15967             this.ios_options.push({
15968                 data : o,
15969                 el : opt
15970             });
15971             
15972         }
15973         
15974         this.store.data.each(function(d, rowIndex){
15975             
15976             var html = '';
15977             
15978             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15979                 html = d.data[this.displayField];
15980             }
15981             
15982             var value = '';
15983             
15984             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15985                 value = d.data[this.valueField];
15986             }
15987             
15988             var option = {
15989                 tag: 'option',
15990                 value : value,
15991                 html : html
15992             };
15993             
15994             if(this.value == d.data[this.valueField]){
15995                 option['selected'] = true;
15996             }
15997             
15998             var opt = this.inputEl().createChild(option);
15999             
16000             this.ios_options.push({
16001                 data : d.data,
16002                 el : opt
16003             });
16004             
16005         }, this);
16006         
16007         this.inputEl().on('change', function(){
16008            this.fireEvent('select', this);
16009         }, this);
16010         
16011     },
16012     
16013     clearIOSView: function()
16014     {
16015         this.inputEl().dom.innerHTML = '';
16016         
16017         this.ios_options = [];
16018     },
16019     
16020     setIOSValue: function(v)
16021     {
16022         this.value = v;
16023         
16024         if(!this.ios_options){
16025             return;
16026         }
16027         
16028         Roo.each(this.ios_options, function(opts){
16029            
16030            opts.el.dom.removeAttribute('selected');
16031            
16032            if(opts.data[this.valueField] != v){
16033                return;
16034            }
16035            
16036            opts.el.dom.setAttribute('selected', true);
16037            
16038         }, this);
16039     }
16040
16041     /** 
16042     * @cfg {Boolean} grow 
16043     * @hide 
16044     */
16045     /** 
16046     * @cfg {Number} growMin 
16047     * @hide 
16048     */
16049     /** 
16050     * @cfg {Number} growMax 
16051     * @hide 
16052     */
16053     /**
16054      * @hide
16055      * @method autoSize
16056      */
16057 });
16058
16059 Roo.apply(Roo.bootstrap.ComboBox,  {
16060     
16061     header : {
16062         tag: 'div',
16063         cls: 'modal-header',
16064         cn: [
16065             {
16066                 tag: 'h4',
16067                 cls: 'modal-title'
16068             }
16069         ]
16070     },
16071     
16072     body : {
16073         tag: 'div',
16074         cls: 'modal-body',
16075         cn: [
16076             {
16077                 tag: 'ul',
16078                 cls: 'list-group'
16079             }
16080         ]
16081     },
16082     
16083     listItemRadio : {
16084         tag: 'li',
16085         cls: 'list-group-item',
16086         cn: [
16087             {
16088                 tag: 'span',
16089                 cls: 'roo-combobox-list-group-item-value'
16090             },
16091             {
16092                 tag: 'div',
16093                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16094                 cn: [
16095                     {
16096                         tag: 'input',
16097                         type: 'radio'
16098                     },
16099                     {
16100                         tag: 'label'
16101                     }
16102                 ]
16103             }
16104         ]
16105     },
16106     
16107     listItemCheckbox : {
16108         tag: 'li',
16109         cls: 'list-group-item',
16110         cn: [
16111             {
16112                 tag: 'span',
16113                 cls: 'roo-combobox-list-group-item-value'
16114             },
16115             {
16116                 tag: 'div',
16117                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16118                 cn: [
16119                     {
16120                         tag: 'input',
16121                         type: 'checkbox'
16122                     },
16123                     {
16124                         tag: 'label'
16125                     }
16126                 ]
16127             }
16128         ]
16129     },
16130     
16131     emptyResult : {
16132         tag: 'div',
16133         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16134     },
16135     
16136     footer : {
16137         tag: 'div',
16138         cls: 'modal-footer',
16139         cn: [
16140             {
16141                 tag: 'div',
16142                 cls: 'row',
16143                 cn: [
16144                     {
16145                         tag: 'div',
16146                         cls: 'col-xs-6 text-left',
16147                         cn: {
16148                             tag: 'button',
16149                             cls: 'btn btn-danger roo-touch-view-cancel',
16150                             html: 'Cancel'
16151                         }
16152                     },
16153                     {
16154                         tag: 'div',
16155                         cls: 'col-xs-6 text-right',
16156                         cn: {
16157                             tag: 'button',
16158                             cls: 'btn btn-success roo-touch-view-ok',
16159                             html: 'OK'
16160                         }
16161                     }
16162                 ]
16163             }
16164         ]
16165         
16166     }
16167 });
16168
16169 Roo.apply(Roo.bootstrap.ComboBox,  {
16170     
16171     touchViewTemplate : {
16172         tag: 'div',
16173         cls: 'modal fade roo-combobox-touch-view',
16174         cn: [
16175             {
16176                 tag: 'div',
16177                 cls: 'modal-dialog',
16178                 style : 'position:fixed', // we have to fix position....
16179                 cn: [
16180                     {
16181                         tag: 'div',
16182                         cls: 'modal-content',
16183                         cn: [
16184                             Roo.bootstrap.ComboBox.header,
16185                             Roo.bootstrap.ComboBox.body,
16186                             Roo.bootstrap.ComboBox.footer
16187                         ]
16188                     }
16189                 ]
16190             }
16191         ]
16192     }
16193 });/*
16194  * Based on:
16195  * Ext JS Library 1.1.1
16196  * Copyright(c) 2006-2007, Ext JS, LLC.
16197  *
16198  * Originally Released Under LGPL - original licence link has changed is not relivant.
16199  *
16200  * Fork - LGPL
16201  * <script type="text/javascript">
16202  */
16203
16204 /**
16205  * @class Roo.View
16206  * @extends Roo.util.Observable
16207  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16208  * This class also supports single and multi selection modes. <br>
16209  * Create a data model bound view:
16210  <pre><code>
16211  var store = new Roo.data.Store(...);
16212
16213  var view = new Roo.View({
16214     el : "my-element",
16215     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16216  
16217     singleSelect: true,
16218     selectedClass: "ydataview-selected",
16219     store: store
16220  });
16221
16222  // listen for node click?
16223  view.on("click", function(vw, index, node, e){
16224  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16225  });
16226
16227  // load XML data
16228  dataModel.load("foobar.xml");
16229  </code></pre>
16230  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16231  * <br><br>
16232  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16233  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16234  * 
16235  * Note: old style constructor is still suported (container, template, config)
16236  * 
16237  * @constructor
16238  * Create a new View
16239  * @param {Object} config The config object
16240  * 
16241  */
16242 Roo.View = function(config, depreciated_tpl, depreciated_config){
16243     
16244     this.parent = false;
16245     
16246     if (typeof(depreciated_tpl) == 'undefined') {
16247         // new way.. - universal constructor.
16248         Roo.apply(this, config);
16249         this.el  = Roo.get(this.el);
16250     } else {
16251         // old format..
16252         this.el  = Roo.get(config);
16253         this.tpl = depreciated_tpl;
16254         Roo.apply(this, depreciated_config);
16255     }
16256     this.wrapEl  = this.el.wrap().wrap();
16257     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16258     
16259     
16260     if(typeof(this.tpl) == "string"){
16261         this.tpl = new Roo.Template(this.tpl);
16262     } else {
16263         // support xtype ctors..
16264         this.tpl = new Roo.factory(this.tpl, Roo);
16265     }
16266     
16267     
16268     this.tpl.compile();
16269     
16270     /** @private */
16271     this.addEvents({
16272         /**
16273          * @event beforeclick
16274          * Fires before a click is processed. Returns false to cancel the default action.
16275          * @param {Roo.View} this
16276          * @param {Number} index The index of the target node
16277          * @param {HTMLElement} node The target node
16278          * @param {Roo.EventObject} e The raw event object
16279          */
16280             "beforeclick" : true,
16281         /**
16282          * @event click
16283          * Fires when a template node is clicked.
16284          * @param {Roo.View} this
16285          * @param {Number} index The index of the target node
16286          * @param {HTMLElement} node The target node
16287          * @param {Roo.EventObject} e The raw event object
16288          */
16289             "click" : true,
16290         /**
16291          * @event dblclick
16292          * Fires when a template node is double clicked.
16293          * @param {Roo.View} this
16294          * @param {Number} index The index of the target node
16295          * @param {HTMLElement} node The target node
16296          * @param {Roo.EventObject} e The raw event object
16297          */
16298             "dblclick" : true,
16299         /**
16300          * @event contextmenu
16301          * Fires when a template node is right clicked.
16302          * @param {Roo.View} this
16303          * @param {Number} index The index of the target node
16304          * @param {HTMLElement} node The target node
16305          * @param {Roo.EventObject} e The raw event object
16306          */
16307             "contextmenu" : true,
16308         /**
16309          * @event selectionchange
16310          * Fires when the selected nodes change.
16311          * @param {Roo.View} this
16312          * @param {Array} selections Array of the selected nodes
16313          */
16314             "selectionchange" : true,
16315     
16316         /**
16317          * @event beforeselect
16318          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16319          * @param {Roo.View} this
16320          * @param {HTMLElement} node The node to be selected
16321          * @param {Array} selections Array of currently selected nodes
16322          */
16323             "beforeselect" : true,
16324         /**
16325          * @event preparedata
16326          * Fires on every row to render, to allow you to change the data.
16327          * @param {Roo.View} this
16328          * @param {Object} data to be rendered (change this)
16329          */
16330           "preparedata" : true
16331           
16332           
16333         });
16334
16335
16336
16337     this.el.on({
16338         "click": this.onClick,
16339         "dblclick": this.onDblClick,
16340         "contextmenu": this.onContextMenu,
16341         scope:this
16342     });
16343
16344     this.selections = [];
16345     this.nodes = [];
16346     this.cmp = new Roo.CompositeElementLite([]);
16347     if(this.store){
16348         this.store = Roo.factory(this.store, Roo.data);
16349         this.setStore(this.store, true);
16350     }
16351     
16352     if ( this.footer && this.footer.xtype) {
16353            
16354          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16355         
16356         this.footer.dataSource = this.store;
16357         this.footer.container = fctr;
16358         this.footer = Roo.factory(this.footer, Roo);
16359         fctr.insertFirst(this.el);
16360         
16361         // this is a bit insane - as the paging toolbar seems to detach the el..
16362 //        dom.parentNode.parentNode.parentNode
16363          // they get detached?
16364     }
16365     
16366     
16367     Roo.View.superclass.constructor.call(this);
16368     
16369     
16370 };
16371
16372 Roo.extend(Roo.View, Roo.util.Observable, {
16373     
16374      /**
16375      * @cfg {Roo.data.Store} store Data store to load data from.
16376      */
16377     store : false,
16378     
16379     /**
16380      * @cfg {String|Roo.Element} el The container element.
16381      */
16382     el : '',
16383     
16384     /**
16385      * @cfg {String|Roo.Template} tpl The template used by this View 
16386      */
16387     tpl : false,
16388     /**
16389      * @cfg {String} dataName the named area of the template to use as the data area
16390      *                          Works with domtemplates roo-name="name"
16391      */
16392     dataName: false,
16393     /**
16394      * @cfg {String} selectedClass The css class to add to selected nodes
16395      */
16396     selectedClass : "x-view-selected",
16397      /**
16398      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16399      */
16400     emptyText : "",
16401     
16402     /**
16403      * @cfg {String} text to display on mask (default Loading)
16404      */
16405     mask : false,
16406     /**
16407      * @cfg {Boolean} multiSelect Allow multiple selection
16408      */
16409     multiSelect : false,
16410     /**
16411      * @cfg {Boolean} singleSelect Allow single selection
16412      */
16413     singleSelect:  false,
16414     
16415     /**
16416      * @cfg {Boolean} toggleSelect - selecting 
16417      */
16418     toggleSelect : false,
16419     
16420     /**
16421      * @cfg {Boolean} tickable - selecting 
16422      */
16423     tickable : false,
16424     
16425     /**
16426      * Returns the element this view is bound to.
16427      * @return {Roo.Element}
16428      */
16429     getEl : function(){
16430         return this.wrapEl;
16431     },
16432     
16433     
16434
16435     /**
16436      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16437      */
16438     refresh : function(){
16439         //Roo.log('refresh');
16440         var t = this.tpl;
16441         
16442         // if we are using something like 'domtemplate', then
16443         // the what gets used is:
16444         // t.applySubtemplate(NAME, data, wrapping data..)
16445         // the outer template then get' applied with
16446         //     the store 'extra data'
16447         // and the body get's added to the
16448         //      roo-name="data" node?
16449         //      <span class='roo-tpl-{name}'></span> ?????
16450         
16451         
16452         
16453         this.clearSelections();
16454         this.el.update("");
16455         var html = [];
16456         var records = this.store.getRange();
16457         if(records.length < 1) {
16458             
16459             // is this valid??  = should it render a template??
16460             
16461             this.el.update(this.emptyText);
16462             return;
16463         }
16464         var el = this.el;
16465         if (this.dataName) {
16466             this.el.update(t.apply(this.store.meta)); //????
16467             el = this.el.child('.roo-tpl-' + this.dataName);
16468         }
16469         
16470         for(var i = 0, len = records.length; i < len; i++){
16471             var data = this.prepareData(records[i].data, i, records[i]);
16472             this.fireEvent("preparedata", this, data, i, records[i]);
16473             
16474             var d = Roo.apply({}, data);
16475             
16476             if(this.tickable){
16477                 Roo.apply(d, {'roo-id' : Roo.id()});
16478                 
16479                 var _this = this;
16480             
16481                 Roo.each(this.parent.item, function(item){
16482                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16483                         return;
16484                     }
16485                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16486                 });
16487             }
16488             
16489             html[html.length] = Roo.util.Format.trim(
16490                 this.dataName ?
16491                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16492                     t.apply(d)
16493             );
16494         }
16495         
16496         
16497         
16498         el.update(html.join(""));
16499         this.nodes = el.dom.childNodes;
16500         this.updateIndexes(0);
16501     },
16502     
16503
16504     /**
16505      * Function to override to reformat the data that is sent to
16506      * the template for each node.
16507      * DEPRICATED - use the preparedata event handler.
16508      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16509      * a JSON object for an UpdateManager bound view).
16510      */
16511     prepareData : function(data, index, record)
16512     {
16513         this.fireEvent("preparedata", this, data, index, record);
16514         return data;
16515     },
16516
16517     onUpdate : function(ds, record){
16518         // Roo.log('on update');   
16519         this.clearSelections();
16520         var index = this.store.indexOf(record);
16521         var n = this.nodes[index];
16522         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16523         n.parentNode.removeChild(n);
16524         this.updateIndexes(index, index);
16525     },
16526
16527     
16528     
16529 // --------- FIXME     
16530     onAdd : function(ds, records, index)
16531     {
16532         //Roo.log(['on Add', ds, records, index] );        
16533         this.clearSelections();
16534         if(this.nodes.length == 0){
16535             this.refresh();
16536             return;
16537         }
16538         var n = this.nodes[index];
16539         for(var i = 0, len = records.length; i < len; i++){
16540             var d = this.prepareData(records[i].data, i, records[i]);
16541             if(n){
16542                 this.tpl.insertBefore(n, d);
16543             }else{
16544                 
16545                 this.tpl.append(this.el, d);
16546             }
16547         }
16548         this.updateIndexes(index);
16549     },
16550
16551     onRemove : function(ds, record, index){
16552        // Roo.log('onRemove');
16553         this.clearSelections();
16554         var el = this.dataName  ?
16555             this.el.child('.roo-tpl-' + this.dataName) :
16556             this.el; 
16557         
16558         el.dom.removeChild(this.nodes[index]);
16559         this.updateIndexes(index);
16560     },
16561
16562     /**
16563      * Refresh an individual node.
16564      * @param {Number} index
16565      */
16566     refreshNode : function(index){
16567         this.onUpdate(this.store, this.store.getAt(index));
16568     },
16569
16570     updateIndexes : function(startIndex, endIndex){
16571         var ns = this.nodes;
16572         startIndex = startIndex || 0;
16573         endIndex = endIndex || ns.length - 1;
16574         for(var i = startIndex; i <= endIndex; i++){
16575             ns[i].nodeIndex = i;
16576         }
16577     },
16578
16579     /**
16580      * Changes the data store this view uses and refresh the view.
16581      * @param {Store} store
16582      */
16583     setStore : function(store, initial){
16584         if(!initial && this.store){
16585             this.store.un("datachanged", this.refresh);
16586             this.store.un("add", this.onAdd);
16587             this.store.un("remove", this.onRemove);
16588             this.store.un("update", this.onUpdate);
16589             this.store.un("clear", this.refresh);
16590             this.store.un("beforeload", this.onBeforeLoad);
16591             this.store.un("load", this.onLoad);
16592             this.store.un("loadexception", this.onLoad);
16593         }
16594         if(store){
16595           
16596             store.on("datachanged", this.refresh, this);
16597             store.on("add", this.onAdd, this);
16598             store.on("remove", this.onRemove, this);
16599             store.on("update", this.onUpdate, this);
16600             store.on("clear", this.refresh, this);
16601             store.on("beforeload", this.onBeforeLoad, this);
16602             store.on("load", this.onLoad, this);
16603             store.on("loadexception", this.onLoad, this);
16604         }
16605         
16606         if(store){
16607             this.refresh();
16608         }
16609     },
16610     /**
16611      * onbeforeLoad - masks the loading area.
16612      *
16613      */
16614     onBeforeLoad : function(store,opts)
16615     {
16616          //Roo.log('onBeforeLoad');   
16617         if (!opts.add) {
16618             this.el.update("");
16619         }
16620         this.el.mask(this.mask ? this.mask : "Loading" ); 
16621     },
16622     onLoad : function ()
16623     {
16624         this.el.unmask();
16625     },
16626     
16627
16628     /**
16629      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16630      * @param {HTMLElement} node
16631      * @return {HTMLElement} The template node
16632      */
16633     findItemFromChild : function(node){
16634         var el = this.dataName  ?
16635             this.el.child('.roo-tpl-' + this.dataName,true) :
16636             this.el.dom; 
16637         
16638         if(!node || node.parentNode == el){
16639                     return node;
16640             }
16641             var p = node.parentNode;
16642             while(p && p != el){
16643             if(p.parentNode == el){
16644                 return p;
16645             }
16646             p = p.parentNode;
16647         }
16648             return null;
16649     },
16650
16651     /** @ignore */
16652     onClick : function(e){
16653         var item = this.findItemFromChild(e.getTarget());
16654         if(item){
16655             var index = this.indexOf(item);
16656             if(this.onItemClick(item, index, e) !== false){
16657                 this.fireEvent("click", this, index, item, e);
16658             }
16659         }else{
16660             this.clearSelections();
16661         }
16662     },
16663
16664     /** @ignore */
16665     onContextMenu : function(e){
16666         var item = this.findItemFromChild(e.getTarget());
16667         if(item){
16668             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16669         }
16670     },
16671
16672     /** @ignore */
16673     onDblClick : function(e){
16674         var item = this.findItemFromChild(e.getTarget());
16675         if(item){
16676             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16677         }
16678     },
16679
16680     onItemClick : function(item, index, e)
16681     {
16682         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16683             return false;
16684         }
16685         if (this.toggleSelect) {
16686             var m = this.isSelected(item) ? 'unselect' : 'select';
16687             //Roo.log(m);
16688             var _t = this;
16689             _t[m](item, true, false);
16690             return true;
16691         }
16692         if(this.multiSelect || this.singleSelect){
16693             if(this.multiSelect && e.shiftKey && this.lastSelection){
16694                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16695             }else{
16696                 this.select(item, this.multiSelect && e.ctrlKey);
16697                 this.lastSelection = item;
16698             }
16699             
16700             if(!this.tickable){
16701                 e.preventDefault();
16702             }
16703             
16704         }
16705         return true;
16706     },
16707
16708     /**
16709      * Get the number of selected nodes.
16710      * @return {Number}
16711      */
16712     getSelectionCount : function(){
16713         return this.selections.length;
16714     },
16715
16716     /**
16717      * Get the currently selected nodes.
16718      * @return {Array} An array of HTMLElements
16719      */
16720     getSelectedNodes : function(){
16721         return this.selections;
16722     },
16723
16724     /**
16725      * Get the indexes of the selected nodes.
16726      * @return {Array}
16727      */
16728     getSelectedIndexes : function(){
16729         var indexes = [], s = this.selections;
16730         for(var i = 0, len = s.length; i < len; i++){
16731             indexes.push(s[i].nodeIndex);
16732         }
16733         return indexes;
16734     },
16735
16736     /**
16737      * Clear all selections
16738      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16739      */
16740     clearSelections : function(suppressEvent){
16741         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16742             this.cmp.elements = this.selections;
16743             this.cmp.removeClass(this.selectedClass);
16744             this.selections = [];
16745             if(!suppressEvent){
16746                 this.fireEvent("selectionchange", this, this.selections);
16747             }
16748         }
16749     },
16750
16751     /**
16752      * Returns true if the passed node is selected
16753      * @param {HTMLElement/Number} node The node or node index
16754      * @return {Boolean}
16755      */
16756     isSelected : function(node){
16757         var s = this.selections;
16758         if(s.length < 1){
16759             return false;
16760         }
16761         node = this.getNode(node);
16762         return s.indexOf(node) !== -1;
16763     },
16764
16765     /**
16766      * Selects nodes.
16767      * @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
16768      * @param {Boolean} keepExisting (optional) true to keep existing selections
16769      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16770      */
16771     select : function(nodeInfo, keepExisting, suppressEvent){
16772         if(nodeInfo instanceof Array){
16773             if(!keepExisting){
16774                 this.clearSelections(true);
16775             }
16776             for(var i = 0, len = nodeInfo.length; i < len; i++){
16777                 this.select(nodeInfo[i], true, true);
16778             }
16779             return;
16780         } 
16781         var node = this.getNode(nodeInfo);
16782         if(!node || this.isSelected(node)){
16783             return; // already selected.
16784         }
16785         if(!keepExisting){
16786             this.clearSelections(true);
16787         }
16788         
16789         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16790             Roo.fly(node).addClass(this.selectedClass);
16791             this.selections.push(node);
16792             if(!suppressEvent){
16793                 this.fireEvent("selectionchange", this, this.selections);
16794             }
16795         }
16796         
16797         
16798     },
16799       /**
16800      * Unselects nodes.
16801      * @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
16802      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16803      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16804      */
16805     unselect : function(nodeInfo, keepExisting, suppressEvent)
16806     {
16807         if(nodeInfo instanceof Array){
16808             Roo.each(this.selections, function(s) {
16809                 this.unselect(s, nodeInfo);
16810             }, this);
16811             return;
16812         }
16813         var node = this.getNode(nodeInfo);
16814         if(!node || !this.isSelected(node)){
16815             //Roo.log("not selected");
16816             return; // not selected.
16817         }
16818         // fireevent???
16819         var ns = [];
16820         Roo.each(this.selections, function(s) {
16821             if (s == node ) {
16822                 Roo.fly(node).removeClass(this.selectedClass);
16823
16824                 return;
16825             }
16826             ns.push(s);
16827         },this);
16828         
16829         this.selections= ns;
16830         this.fireEvent("selectionchange", this, this.selections);
16831     },
16832
16833     /**
16834      * Gets a template node.
16835      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16836      * @return {HTMLElement} The node or null if it wasn't found
16837      */
16838     getNode : function(nodeInfo){
16839         if(typeof nodeInfo == "string"){
16840             return document.getElementById(nodeInfo);
16841         }else if(typeof nodeInfo == "number"){
16842             return this.nodes[nodeInfo];
16843         }
16844         return nodeInfo;
16845     },
16846
16847     /**
16848      * Gets a range template nodes.
16849      * @param {Number} startIndex
16850      * @param {Number} endIndex
16851      * @return {Array} An array of nodes
16852      */
16853     getNodes : function(start, end){
16854         var ns = this.nodes;
16855         start = start || 0;
16856         end = typeof end == "undefined" ? ns.length - 1 : end;
16857         var nodes = [];
16858         if(start <= end){
16859             for(var i = start; i <= end; i++){
16860                 nodes.push(ns[i]);
16861             }
16862         } else{
16863             for(var i = start; i >= end; i--){
16864                 nodes.push(ns[i]);
16865             }
16866         }
16867         return nodes;
16868     },
16869
16870     /**
16871      * Finds the index of the passed node
16872      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16873      * @return {Number} The index of the node or -1
16874      */
16875     indexOf : function(node){
16876         node = this.getNode(node);
16877         if(typeof node.nodeIndex == "number"){
16878             return node.nodeIndex;
16879         }
16880         var ns = this.nodes;
16881         for(var i = 0, len = ns.length; i < len; i++){
16882             if(ns[i] == node){
16883                 return i;
16884             }
16885         }
16886         return -1;
16887     }
16888 });
16889 /*
16890  * - LGPL
16891  *
16892  * based on jquery fullcalendar
16893  * 
16894  */
16895
16896 Roo.bootstrap = Roo.bootstrap || {};
16897 /**
16898  * @class Roo.bootstrap.Calendar
16899  * @extends Roo.bootstrap.Component
16900  * Bootstrap Calendar class
16901  * @cfg {Boolean} loadMask (true|false) default false
16902  * @cfg {Object} header generate the user specific header of the calendar, default false
16903
16904  * @constructor
16905  * Create a new Container
16906  * @param {Object} config The config object
16907  */
16908
16909
16910
16911 Roo.bootstrap.Calendar = function(config){
16912     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16913      this.addEvents({
16914         /**
16915              * @event select
16916              * Fires when a date is selected
16917              * @param {DatePicker} this
16918              * @param {Date} date The selected date
16919              */
16920         'select': true,
16921         /**
16922              * @event monthchange
16923              * Fires when the displayed month changes 
16924              * @param {DatePicker} this
16925              * @param {Date} date The selected month
16926              */
16927         'monthchange': true,
16928         /**
16929              * @event evententer
16930              * Fires when mouse over an event
16931              * @param {Calendar} this
16932              * @param {event} Event
16933              */
16934         'evententer': true,
16935         /**
16936              * @event eventleave
16937              * Fires when the mouse leaves an
16938              * @param {Calendar} this
16939              * @param {event}
16940              */
16941         'eventleave': true,
16942         /**
16943              * @event eventclick
16944              * Fires when the mouse click an
16945              * @param {Calendar} this
16946              * @param {event}
16947              */
16948         'eventclick': true
16949         
16950     });
16951
16952 };
16953
16954 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16955     
16956      /**
16957      * @cfg {Number} startDay
16958      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16959      */
16960     startDay : 0,
16961     
16962     loadMask : false,
16963     
16964     header : false,
16965       
16966     getAutoCreate : function(){
16967         
16968         
16969         var fc_button = function(name, corner, style, content ) {
16970             return Roo.apply({},{
16971                 tag : 'span',
16972                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16973                          (corner.length ?
16974                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16975                             ''
16976                         ),
16977                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16978                 unselectable: 'on'
16979             });
16980         };
16981         
16982         var header = {};
16983         
16984         if(!this.header){
16985             header = {
16986                 tag : 'table',
16987                 cls : 'fc-header',
16988                 style : 'width:100%',
16989                 cn : [
16990                     {
16991                         tag: 'tr',
16992                         cn : [
16993                             {
16994                                 tag : 'td',
16995                                 cls : 'fc-header-left',
16996                                 cn : [
16997                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16998                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16999                                     { tag: 'span', cls: 'fc-header-space' },
17000                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
17001
17002
17003                                 ]
17004                             },
17005
17006                             {
17007                                 tag : 'td',
17008                                 cls : 'fc-header-center',
17009                                 cn : [
17010                                     {
17011                                         tag: 'span',
17012                                         cls: 'fc-header-title',
17013                                         cn : {
17014                                             tag: 'H2',
17015                                             html : 'month / year'
17016                                         }
17017                                     }
17018
17019                                 ]
17020                             },
17021                             {
17022                                 tag : 'td',
17023                                 cls : 'fc-header-right',
17024                                 cn : [
17025                               /*      fc_button('month', 'left', '', 'month' ),
17026                                     fc_button('week', '', '', 'week' ),
17027                                     fc_button('day', 'right', '', 'day' )
17028                                 */    
17029
17030                                 ]
17031                             }
17032
17033                         ]
17034                     }
17035                 ]
17036             };
17037         }
17038         
17039         header = this.header;
17040         
17041        
17042         var cal_heads = function() {
17043             var ret = [];
17044             // fixme - handle this.
17045             
17046             for (var i =0; i < Date.dayNames.length; i++) {
17047                 var d = Date.dayNames[i];
17048                 ret.push({
17049                     tag: 'th',
17050                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17051                     html : d.substring(0,3)
17052                 });
17053                 
17054             }
17055             ret[0].cls += ' fc-first';
17056             ret[6].cls += ' fc-last';
17057             return ret;
17058         };
17059         var cal_cell = function(n) {
17060             return  {
17061                 tag: 'td',
17062                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17063                 cn : [
17064                     {
17065                         cn : [
17066                             {
17067                                 cls: 'fc-day-number',
17068                                 html: 'D'
17069                             },
17070                             {
17071                                 cls: 'fc-day-content',
17072                              
17073                                 cn : [
17074                                      {
17075                                         style: 'position: relative;' // height: 17px;
17076                                     }
17077                                 ]
17078                             }
17079                             
17080                             
17081                         ]
17082                     }
17083                 ]
17084                 
17085             }
17086         };
17087         var cal_rows = function() {
17088             
17089             var ret = [];
17090             for (var r = 0; r < 6; r++) {
17091                 var row= {
17092                     tag : 'tr',
17093                     cls : 'fc-week',
17094                     cn : []
17095                 };
17096                 
17097                 for (var i =0; i < Date.dayNames.length; i++) {
17098                     var d = Date.dayNames[i];
17099                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17100
17101                 }
17102                 row.cn[0].cls+=' fc-first';
17103                 row.cn[0].cn[0].style = 'min-height:90px';
17104                 row.cn[6].cls+=' fc-last';
17105                 ret.push(row);
17106                 
17107             }
17108             ret[0].cls += ' fc-first';
17109             ret[4].cls += ' fc-prev-last';
17110             ret[5].cls += ' fc-last';
17111             return ret;
17112             
17113         };
17114         
17115         var cal_table = {
17116             tag: 'table',
17117             cls: 'fc-border-separate',
17118             style : 'width:100%',
17119             cellspacing  : 0,
17120             cn : [
17121                 { 
17122                     tag: 'thead',
17123                     cn : [
17124                         { 
17125                             tag: 'tr',
17126                             cls : 'fc-first fc-last',
17127                             cn : cal_heads()
17128                         }
17129                     ]
17130                 },
17131                 { 
17132                     tag: 'tbody',
17133                     cn : cal_rows()
17134                 }
17135                   
17136             ]
17137         };
17138          
17139          var cfg = {
17140             cls : 'fc fc-ltr',
17141             cn : [
17142                 header,
17143                 {
17144                     cls : 'fc-content',
17145                     style : "position: relative;",
17146                     cn : [
17147                         {
17148                             cls : 'fc-view fc-view-month fc-grid',
17149                             style : 'position: relative',
17150                             unselectable : 'on',
17151                             cn : [
17152                                 {
17153                                     cls : 'fc-event-container',
17154                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17155                                 },
17156                                 cal_table
17157                             ]
17158                         }
17159                     ]
17160     
17161                 }
17162            ] 
17163             
17164         };
17165         
17166          
17167         
17168         return cfg;
17169     },
17170     
17171     
17172     initEvents : function()
17173     {
17174         if(!this.store){
17175             throw "can not find store for calendar";
17176         }
17177         
17178         var mark = {
17179             tag: "div",
17180             cls:"x-dlg-mask",
17181             style: "text-align:center",
17182             cn: [
17183                 {
17184                     tag: "div",
17185                     style: "background-color:white;width:50%;margin:250 auto",
17186                     cn: [
17187                         {
17188                             tag: "img",
17189                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17190                         },
17191                         {
17192                             tag: "span",
17193                             html: "Loading"
17194                         }
17195                         
17196                     ]
17197                 }
17198             ]
17199         };
17200         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17201         
17202         var size = this.el.select('.fc-content', true).first().getSize();
17203         this.maskEl.setSize(size.width, size.height);
17204         this.maskEl.enableDisplayMode("block");
17205         if(!this.loadMask){
17206             this.maskEl.hide();
17207         }
17208         
17209         this.store = Roo.factory(this.store, Roo.data);
17210         this.store.on('load', this.onLoad, this);
17211         this.store.on('beforeload', this.onBeforeLoad, this);
17212         
17213         this.resize();
17214         
17215         this.cells = this.el.select('.fc-day',true);
17216         //Roo.log(this.cells);
17217         this.textNodes = this.el.query('.fc-day-number');
17218         this.cells.addClassOnOver('fc-state-hover');
17219         
17220         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17221         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17222         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17223         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17224         
17225         this.on('monthchange', this.onMonthChange, this);
17226         
17227         this.update(new Date().clearTime());
17228     },
17229     
17230     resize : function() {
17231         var sz  = this.el.getSize();
17232         
17233         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17234         this.el.select('.fc-day-content div',true).setHeight(34);
17235     },
17236     
17237     
17238     // private
17239     showPrevMonth : function(e){
17240         this.update(this.activeDate.add("mo", -1));
17241     },
17242     showToday : function(e){
17243         this.update(new Date().clearTime());
17244     },
17245     // private
17246     showNextMonth : function(e){
17247         this.update(this.activeDate.add("mo", 1));
17248     },
17249
17250     // private
17251     showPrevYear : function(){
17252         this.update(this.activeDate.add("y", -1));
17253     },
17254
17255     // private
17256     showNextYear : function(){
17257         this.update(this.activeDate.add("y", 1));
17258     },
17259
17260     
17261    // private
17262     update : function(date)
17263     {
17264         var vd = this.activeDate;
17265         this.activeDate = date;
17266 //        if(vd && this.el){
17267 //            var t = date.getTime();
17268 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17269 //                Roo.log('using add remove');
17270 //                
17271 //                this.fireEvent('monthchange', this, date);
17272 //                
17273 //                this.cells.removeClass("fc-state-highlight");
17274 //                this.cells.each(function(c){
17275 //                   if(c.dateValue == t){
17276 //                       c.addClass("fc-state-highlight");
17277 //                       setTimeout(function(){
17278 //                            try{c.dom.firstChild.focus();}catch(e){}
17279 //                       }, 50);
17280 //                       return false;
17281 //                   }
17282 //                   return true;
17283 //                });
17284 //                return;
17285 //            }
17286 //        }
17287         
17288         var days = date.getDaysInMonth();
17289         
17290         var firstOfMonth = date.getFirstDateOfMonth();
17291         var startingPos = firstOfMonth.getDay()-this.startDay;
17292         
17293         if(startingPos < this.startDay){
17294             startingPos += 7;
17295         }
17296         
17297         var pm = date.add(Date.MONTH, -1);
17298         var prevStart = pm.getDaysInMonth()-startingPos;
17299 //        
17300         this.cells = this.el.select('.fc-day',true);
17301         this.textNodes = this.el.query('.fc-day-number');
17302         this.cells.addClassOnOver('fc-state-hover');
17303         
17304         var cells = this.cells.elements;
17305         var textEls = this.textNodes;
17306         
17307         Roo.each(cells, function(cell){
17308             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17309         });
17310         
17311         days += startingPos;
17312
17313         // convert everything to numbers so it's fast
17314         var day = 86400000;
17315         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17316         //Roo.log(d);
17317         //Roo.log(pm);
17318         //Roo.log(prevStart);
17319         
17320         var today = new Date().clearTime().getTime();
17321         var sel = date.clearTime().getTime();
17322         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17323         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17324         var ddMatch = this.disabledDatesRE;
17325         var ddText = this.disabledDatesText;
17326         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17327         var ddaysText = this.disabledDaysText;
17328         var format = this.format;
17329         
17330         var setCellClass = function(cal, cell){
17331             cell.row = 0;
17332             cell.events = [];
17333             cell.more = [];
17334             //Roo.log('set Cell Class');
17335             cell.title = "";
17336             var t = d.getTime();
17337             
17338             //Roo.log(d);
17339             
17340             cell.dateValue = t;
17341             if(t == today){
17342                 cell.className += " fc-today";
17343                 cell.className += " fc-state-highlight";
17344                 cell.title = cal.todayText;
17345             }
17346             if(t == sel){
17347                 // disable highlight in other month..
17348                 //cell.className += " fc-state-highlight";
17349                 
17350             }
17351             // disabling
17352             if(t < min) {
17353                 cell.className = " fc-state-disabled";
17354                 cell.title = cal.minText;
17355                 return;
17356             }
17357             if(t > max) {
17358                 cell.className = " fc-state-disabled";
17359                 cell.title = cal.maxText;
17360                 return;
17361             }
17362             if(ddays){
17363                 if(ddays.indexOf(d.getDay()) != -1){
17364                     cell.title = ddaysText;
17365                     cell.className = " fc-state-disabled";
17366                 }
17367             }
17368             if(ddMatch && format){
17369                 var fvalue = d.dateFormat(format);
17370                 if(ddMatch.test(fvalue)){
17371                     cell.title = ddText.replace("%0", fvalue);
17372                     cell.className = " fc-state-disabled";
17373                 }
17374             }
17375             
17376             if (!cell.initialClassName) {
17377                 cell.initialClassName = cell.dom.className;
17378             }
17379             
17380             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17381         };
17382
17383         var i = 0;
17384         
17385         for(; i < startingPos; i++) {
17386             textEls[i].innerHTML = (++prevStart);
17387             d.setDate(d.getDate()+1);
17388             
17389             cells[i].className = "fc-past fc-other-month";
17390             setCellClass(this, cells[i]);
17391         }
17392         
17393         var intDay = 0;
17394         
17395         for(; i < days; i++){
17396             intDay = i - startingPos + 1;
17397             textEls[i].innerHTML = (intDay);
17398             d.setDate(d.getDate()+1);
17399             
17400             cells[i].className = ''; // "x-date-active";
17401             setCellClass(this, cells[i]);
17402         }
17403         var extraDays = 0;
17404         
17405         for(; i < 42; i++) {
17406             textEls[i].innerHTML = (++extraDays);
17407             d.setDate(d.getDate()+1);
17408             
17409             cells[i].className = "fc-future fc-other-month";
17410             setCellClass(this, cells[i]);
17411         }
17412         
17413         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17414         
17415         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17416         
17417         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17418         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17419         
17420         if(totalRows != 6){
17421             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17422             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17423         }
17424         
17425         this.fireEvent('monthchange', this, date);
17426         
17427         
17428         /*
17429         if(!this.internalRender){
17430             var main = this.el.dom.firstChild;
17431             var w = main.offsetWidth;
17432             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17433             Roo.fly(main).setWidth(w);
17434             this.internalRender = true;
17435             // opera does not respect the auto grow header center column
17436             // then, after it gets a width opera refuses to recalculate
17437             // without a second pass
17438             if(Roo.isOpera && !this.secondPass){
17439                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17440                 this.secondPass = true;
17441                 this.update.defer(10, this, [date]);
17442             }
17443         }
17444         */
17445         
17446     },
17447     
17448     findCell : function(dt) {
17449         dt = dt.clearTime().getTime();
17450         var ret = false;
17451         this.cells.each(function(c){
17452             //Roo.log("check " +c.dateValue + '?=' + dt);
17453             if(c.dateValue == dt){
17454                 ret = c;
17455                 return false;
17456             }
17457             return true;
17458         });
17459         
17460         return ret;
17461     },
17462     
17463     findCells : function(ev) {
17464         var s = ev.start.clone().clearTime().getTime();
17465        // Roo.log(s);
17466         var e= ev.end.clone().clearTime().getTime();
17467        // Roo.log(e);
17468         var ret = [];
17469         this.cells.each(function(c){
17470              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17471             
17472             if(c.dateValue > e){
17473                 return ;
17474             }
17475             if(c.dateValue < s){
17476                 return ;
17477             }
17478             ret.push(c);
17479         });
17480         
17481         return ret;    
17482     },
17483     
17484 //    findBestRow: function(cells)
17485 //    {
17486 //        var ret = 0;
17487 //        
17488 //        for (var i =0 ; i < cells.length;i++) {
17489 //            ret  = Math.max(cells[i].rows || 0,ret);
17490 //        }
17491 //        return ret;
17492 //        
17493 //    },
17494     
17495     
17496     addItem : function(ev)
17497     {
17498         // look for vertical location slot in
17499         var cells = this.findCells(ev);
17500         
17501 //        ev.row = this.findBestRow(cells);
17502         
17503         // work out the location.
17504         
17505         var crow = false;
17506         var rows = [];
17507         for(var i =0; i < cells.length; i++) {
17508             
17509             cells[i].row = cells[0].row;
17510             
17511             if(i == 0){
17512                 cells[i].row = cells[i].row + 1;
17513             }
17514             
17515             if (!crow) {
17516                 crow = {
17517                     start : cells[i],
17518                     end :  cells[i]
17519                 };
17520                 continue;
17521             }
17522             if (crow.start.getY() == cells[i].getY()) {
17523                 // on same row.
17524                 crow.end = cells[i];
17525                 continue;
17526             }
17527             // different row.
17528             rows.push(crow);
17529             crow = {
17530                 start: cells[i],
17531                 end : cells[i]
17532             };
17533             
17534         }
17535         
17536         rows.push(crow);
17537         ev.els = [];
17538         ev.rows = rows;
17539         ev.cells = cells;
17540         
17541         cells[0].events.push(ev);
17542         
17543         this.calevents.push(ev);
17544     },
17545     
17546     clearEvents: function() {
17547         
17548         if(!this.calevents){
17549             return;
17550         }
17551         
17552         Roo.each(this.cells.elements, function(c){
17553             c.row = 0;
17554             c.events = [];
17555             c.more = [];
17556         });
17557         
17558         Roo.each(this.calevents, function(e) {
17559             Roo.each(e.els, function(el) {
17560                 el.un('mouseenter' ,this.onEventEnter, this);
17561                 el.un('mouseleave' ,this.onEventLeave, this);
17562                 el.remove();
17563             },this);
17564         },this);
17565         
17566         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17567             e.remove();
17568         });
17569         
17570     },
17571     
17572     renderEvents: function()
17573     {   
17574         var _this = this;
17575         
17576         this.cells.each(function(c) {
17577             
17578             if(c.row < 5){
17579                 return;
17580             }
17581             
17582             var ev = c.events;
17583             
17584             var r = 4;
17585             if(c.row != c.events.length){
17586                 r = 4 - (4 - (c.row - c.events.length));
17587             }
17588             
17589             c.events = ev.slice(0, r);
17590             c.more = ev.slice(r);
17591             
17592             if(c.more.length && c.more.length == 1){
17593                 c.events.push(c.more.pop());
17594             }
17595             
17596             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17597             
17598         });
17599             
17600         this.cells.each(function(c) {
17601             
17602             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17603             
17604             
17605             for (var e = 0; e < c.events.length; e++){
17606                 var ev = c.events[e];
17607                 var rows = ev.rows;
17608                 
17609                 for(var i = 0; i < rows.length; i++) {
17610                 
17611                     // how many rows should it span..
17612
17613                     var  cfg = {
17614                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17615                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17616
17617                         unselectable : "on",
17618                         cn : [
17619                             {
17620                                 cls: 'fc-event-inner',
17621                                 cn : [
17622     //                                {
17623     //                                  tag:'span',
17624     //                                  cls: 'fc-event-time',
17625     //                                  html : cells.length > 1 ? '' : ev.time
17626     //                                },
17627                                     {
17628                                       tag:'span',
17629                                       cls: 'fc-event-title',
17630                                       html : String.format('{0}', ev.title)
17631                                     }
17632
17633
17634                                 ]
17635                             },
17636                             {
17637                                 cls: 'ui-resizable-handle ui-resizable-e',
17638                                 html : '&nbsp;&nbsp;&nbsp'
17639                             }
17640
17641                         ]
17642                     };
17643
17644                     if (i == 0) {
17645                         cfg.cls += ' fc-event-start';
17646                     }
17647                     if ((i+1) == rows.length) {
17648                         cfg.cls += ' fc-event-end';
17649                     }
17650
17651                     var ctr = _this.el.select('.fc-event-container',true).first();
17652                     var cg = ctr.createChild(cfg);
17653
17654                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17655                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17656
17657                     var r = (c.more.length) ? 1 : 0;
17658                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17659                     cg.setWidth(ebox.right - sbox.x -2);
17660
17661                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17662                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17663                     cg.on('click', _this.onEventClick, _this, ev);
17664
17665                     ev.els.push(cg);
17666                     
17667                 }
17668                 
17669             }
17670             
17671             
17672             if(c.more.length){
17673                 var  cfg = {
17674                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17675                     style : 'position: absolute',
17676                     unselectable : "on",
17677                     cn : [
17678                         {
17679                             cls: 'fc-event-inner',
17680                             cn : [
17681                                 {
17682                                   tag:'span',
17683                                   cls: 'fc-event-title',
17684                                   html : 'More'
17685                                 }
17686
17687
17688                             ]
17689                         },
17690                         {
17691                             cls: 'ui-resizable-handle ui-resizable-e',
17692                             html : '&nbsp;&nbsp;&nbsp'
17693                         }
17694
17695                     ]
17696                 };
17697
17698                 var ctr = _this.el.select('.fc-event-container',true).first();
17699                 var cg = ctr.createChild(cfg);
17700
17701                 var sbox = c.select('.fc-day-content',true).first().getBox();
17702                 var ebox = c.select('.fc-day-content',true).first().getBox();
17703                 //Roo.log(cg);
17704                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17705                 cg.setWidth(ebox.right - sbox.x -2);
17706
17707                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17708                 
17709             }
17710             
17711         });
17712         
17713         
17714         
17715     },
17716     
17717     onEventEnter: function (e, el,event,d) {
17718         this.fireEvent('evententer', this, el, event);
17719     },
17720     
17721     onEventLeave: function (e, el,event,d) {
17722         this.fireEvent('eventleave', this, el, event);
17723     },
17724     
17725     onEventClick: function (e, el,event,d) {
17726         this.fireEvent('eventclick', this, el, event);
17727     },
17728     
17729     onMonthChange: function () {
17730         this.store.load();
17731     },
17732     
17733     onMoreEventClick: function(e, el, more)
17734     {
17735         var _this = this;
17736         
17737         this.calpopover.placement = 'right';
17738         this.calpopover.setTitle('More');
17739         
17740         this.calpopover.setContent('');
17741         
17742         var ctr = this.calpopover.el.select('.popover-content', true).first();
17743         
17744         Roo.each(more, function(m){
17745             var cfg = {
17746                 cls : 'fc-event-hori fc-event-draggable',
17747                 html : m.title
17748             };
17749             var cg = ctr.createChild(cfg);
17750             
17751             cg.on('click', _this.onEventClick, _this, m);
17752         });
17753         
17754         this.calpopover.show(el);
17755         
17756         
17757     },
17758     
17759     onLoad: function () 
17760     {   
17761         this.calevents = [];
17762         var cal = this;
17763         
17764         if(this.store.getCount() > 0){
17765             this.store.data.each(function(d){
17766                cal.addItem({
17767                     id : d.data.id,
17768                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17769                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17770                     time : d.data.start_time,
17771                     title : d.data.title,
17772                     description : d.data.description,
17773                     venue : d.data.venue
17774                 });
17775             });
17776         }
17777         
17778         this.renderEvents();
17779         
17780         if(this.calevents.length && this.loadMask){
17781             this.maskEl.hide();
17782         }
17783     },
17784     
17785     onBeforeLoad: function()
17786     {
17787         this.clearEvents();
17788         if(this.loadMask){
17789             this.maskEl.show();
17790         }
17791     }
17792 });
17793
17794  
17795  /*
17796  * - LGPL
17797  *
17798  * element
17799  * 
17800  */
17801
17802 /**
17803  * @class Roo.bootstrap.Popover
17804  * @extends Roo.bootstrap.Component
17805  * Bootstrap Popover class
17806  * @cfg {String} html contents of the popover   (or false to use children..)
17807  * @cfg {String} title of popover (or false to hide)
17808  * @cfg {String} placement how it is placed
17809  * @cfg {String} trigger click || hover (or false to trigger manually)
17810  * @cfg {String} over what (parent or false to trigger manually.)
17811  * @cfg {Number} delay - delay before showing
17812  
17813  * @constructor
17814  * Create a new Popover
17815  * @param {Object} config The config object
17816  */
17817
17818 Roo.bootstrap.Popover = function(config){
17819     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17820     
17821     this.addEvents({
17822         // raw events
17823          /**
17824          * @event show
17825          * After the popover show
17826          * 
17827          * @param {Roo.bootstrap.Popover} this
17828          */
17829         "show" : true,
17830         /**
17831          * @event hide
17832          * After the popover hide
17833          * 
17834          * @param {Roo.bootstrap.Popover} this
17835          */
17836         "hide" : true
17837     });
17838 };
17839
17840 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17841     
17842     title: 'Fill in a title',
17843     html: false,
17844     
17845     placement : 'right',
17846     trigger : 'hover', // hover
17847     
17848     delay : 0,
17849     
17850     over: 'parent',
17851     
17852     can_build_overlaid : false,
17853     
17854     getChildContainer : function()
17855     {
17856         return this.el.select('.popover-content',true).first();
17857     },
17858     
17859     getAutoCreate : function(){
17860          
17861         var cfg = {
17862            cls : 'popover roo-dynamic',
17863            style: 'display:block',
17864            cn : [
17865                 {
17866                     cls : 'arrow'
17867                 },
17868                 {
17869                     cls : 'popover-inner',
17870                     cn : [
17871                         {
17872                             tag: 'h3',
17873                             cls: 'popover-title popover-header',
17874                             html : this.title
17875                         },
17876                         {
17877                             cls : 'popover-content popover-body',
17878                             html : this.html
17879                         }
17880                     ]
17881                     
17882                 }
17883            ]
17884         };
17885         
17886         return cfg;
17887     },
17888     setTitle: function(str)
17889     {
17890         this.title = str;
17891         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17892     },
17893     setContent: function(str)
17894     {
17895         this.html = str;
17896         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17897     },
17898     // as it get's added to the bottom of the page.
17899     onRender : function(ct, position)
17900     {
17901         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17902         if(!this.el){
17903             var cfg = Roo.apply({},  this.getAutoCreate());
17904             cfg.id = Roo.id();
17905             
17906             if (this.cls) {
17907                 cfg.cls += ' ' + this.cls;
17908             }
17909             if (this.style) {
17910                 cfg.style = this.style;
17911             }
17912             //Roo.log("adding to ");
17913             this.el = Roo.get(document.body).createChild(cfg, position);
17914 //            Roo.log(this.el);
17915         }
17916         this.initEvents();
17917     },
17918     
17919     initEvents : function()
17920     {
17921         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17922         this.el.enableDisplayMode('block');
17923         this.el.hide();
17924         if (this.over === false) {
17925             return; 
17926         }
17927         if (this.triggers === false) {
17928             return;
17929         }
17930         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17931         var triggers = this.trigger ? this.trigger.split(' ') : [];
17932         Roo.each(triggers, function(trigger) {
17933         
17934             if (trigger == 'click') {
17935                 on_el.on('click', this.toggle, this);
17936             } else if (trigger != 'manual') {
17937                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17938                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17939       
17940                 on_el.on(eventIn  ,this.enter, this);
17941                 on_el.on(eventOut, this.leave, this);
17942             }
17943         }, this);
17944         
17945     },
17946     
17947     
17948     // private
17949     timeout : null,
17950     hoverState : null,
17951     
17952     toggle : function () {
17953         this.hoverState == 'in' ? this.leave() : this.enter();
17954     },
17955     
17956     enter : function () {
17957         
17958         clearTimeout(this.timeout);
17959     
17960         this.hoverState = 'in';
17961     
17962         if (!this.delay || !this.delay.show) {
17963             this.show();
17964             return;
17965         }
17966         var _t = this;
17967         this.timeout = setTimeout(function () {
17968             if (_t.hoverState == 'in') {
17969                 _t.show();
17970             }
17971         }, this.delay.show)
17972     },
17973     
17974     leave : function() {
17975         clearTimeout(this.timeout);
17976     
17977         this.hoverState = 'out';
17978     
17979         if (!this.delay || !this.delay.hide) {
17980             this.hide();
17981             return;
17982         }
17983         var _t = this;
17984         this.timeout = setTimeout(function () {
17985             if (_t.hoverState == 'out') {
17986                 _t.hide();
17987             }
17988         }, this.delay.hide)
17989     },
17990     
17991     show : function (on_el)
17992     {
17993         if (!on_el) {
17994             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17995         }
17996         
17997         // set content.
17998         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17999         if (this.html !== false) {
18000             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18001         }
18002         this.el.removeClass([
18003             'fade','top','bottom', 'left', 'right','in',
18004             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18005         ]);
18006         if (!this.title.length) {
18007             this.el.select('.popover-title',true).hide();
18008         }
18009         
18010         var placement = typeof this.placement == 'function' ?
18011             this.placement.call(this, this.el, on_el) :
18012             this.placement;
18013             
18014         var autoToken = /\s?auto?\s?/i;
18015         var autoPlace = autoToken.test(placement);
18016         if (autoPlace) {
18017             placement = placement.replace(autoToken, '') || 'top';
18018         }
18019         
18020         //this.el.detach()
18021         //this.el.setXY([0,0]);
18022         this.el.show();
18023         this.el.dom.style.display='block';
18024         this.el.addClass(placement);
18025         
18026         //this.el.appendTo(on_el);
18027         
18028         var p = this.getPosition();
18029         var box = this.el.getBox();
18030         
18031         if (autoPlace) {
18032             // fixme..
18033         }
18034         var align = Roo.bootstrap.Popover.alignment[placement];
18035         
18036 //        Roo.log(align);
18037         this.el.alignTo(on_el, align[0],align[1]);
18038         //var arrow = this.el.select('.arrow',true).first();
18039         //arrow.set(align[2], 
18040         
18041         this.el.addClass('in');
18042         
18043         
18044         if (this.el.hasClass('fade')) {
18045             // fade it?
18046         }
18047         
18048         this.hoverState = 'in';
18049         
18050         this.fireEvent('show', this);
18051         
18052     },
18053     hide : function()
18054     {
18055         this.el.setXY([0,0]);
18056         this.el.removeClass('in');
18057         this.el.hide();
18058         this.hoverState = null;
18059         
18060         this.fireEvent('hide', this);
18061     }
18062     
18063 });
18064
18065 Roo.bootstrap.Popover.alignment = {
18066     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18067     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18068     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18069     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18070 };
18071
18072  /*
18073  * - LGPL
18074  *
18075  * Progress
18076  * 
18077  */
18078
18079 /**
18080  * @class Roo.bootstrap.Progress
18081  * @extends Roo.bootstrap.Component
18082  * Bootstrap Progress class
18083  * @cfg {Boolean} striped striped of the progress bar
18084  * @cfg {Boolean} active animated of the progress bar
18085  * 
18086  * 
18087  * @constructor
18088  * Create a new Progress
18089  * @param {Object} config The config object
18090  */
18091
18092 Roo.bootstrap.Progress = function(config){
18093     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18094 };
18095
18096 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18097     
18098     striped : false,
18099     active: false,
18100     
18101     getAutoCreate : function(){
18102         var cfg = {
18103             tag: 'div',
18104             cls: 'progress'
18105         };
18106         
18107         
18108         if(this.striped){
18109             cfg.cls += ' progress-striped';
18110         }
18111       
18112         if(this.active){
18113             cfg.cls += ' active';
18114         }
18115         
18116         
18117         return cfg;
18118     }
18119    
18120 });
18121
18122  
18123
18124  /*
18125  * - LGPL
18126  *
18127  * ProgressBar
18128  * 
18129  */
18130
18131 /**
18132  * @class Roo.bootstrap.ProgressBar
18133  * @extends Roo.bootstrap.Component
18134  * Bootstrap ProgressBar class
18135  * @cfg {Number} aria_valuenow aria-value now
18136  * @cfg {Number} aria_valuemin aria-value min
18137  * @cfg {Number} aria_valuemax aria-value max
18138  * @cfg {String} label label for the progress bar
18139  * @cfg {String} panel (success | info | warning | danger )
18140  * @cfg {String} role role of the progress bar
18141  * @cfg {String} sr_only text
18142  * 
18143  * 
18144  * @constructor
18145  * Create a new ProgressBar
18146  * @param {Object} config The config object
18147  */
18148
18149 Roo.bootstrap.ProgressBar = function(config){
18150     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18151 };
18152
18153 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18154     
18155     aria_valuenow : 0,
18156     aria_valuemin : 0,
18157     aria_valuemax : 100,
18158     label : false,
18159     panel : false,
18160     role : false,
18161     sr_only: false,
18162     
18163     getAutoCreate : function()
18164     {
18165         
18166         var cfg = {
18167             tag: 'div',
18168             cls: 'progress-bar',
18169             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18170         };
18171         
18172         if(this.sr_only){
18173             cfg.cn = {
18174                 tag: 'span',
18175                 cls: 'sr-only',
18176                 html: this.sr_only
18177             }
18178         }
18179         
18180         if(this.role){
18181             cfg.role = this.role;
18182         }
18183         
18184         if(this.aria_valuenow){
18185             cfg['aria-valuenow'] = this.aria_valuenow;
18186         }
18187         
18188         if(this.aria_valuemin){
18189             cfg['aria-valuemin'] = this.aria_valuemin;
18190         }
18191         
18192         if(this.aria_valuemax){
18193             cfg['aria-valuemax'] = this.aria_valuemax;
18194         }
18195         
18196         if(this.label && !this.sr_only){
18197             cfg.html = this.label;
18198         }
18199         
18200         if(this.panel){
18201             cfg.cls += ' progress-bar-' + this.panel;
18202         }
18203         
18204         return cfg;
18205     },
18206     
18207     update : function(aria_valuenow)
18208     {
18209         this.aria_valuenow = aria_valuenow;
18210         
18211         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18212     }
18213    
18214 });
18215
18216  
18217
18218  /*
18219  * - LGPL
18220  *
18221  * column
18222  * 
18223  */
18224
18225 /**
18226  * @class Roo.bootstrap.TabGroup
18227  * @extends Roo.bootstrap.Column
18228  * Bootstrap Column class
18229  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18230  * @cfg {Boolean} carousel true to make the group behave like a carousel
18231  * @cfg {Boolean} bullets show bullets for the panels
18232  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18233  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18234  * @cfg {Boolean} showarrow (true|false) show arrow default true
18235  * 
18236  * @constructor
18237  * Create a new TabGroup
18238  * @param {Object} config The config object
18239  */
18240
18241 Roo.bootstrap.TabGroup = function(config){
18242     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18243     if (!this.navId) {
18244         this.navId = Roo.id();
18245     }
18246     this.tabs = [];
18247     Roo.bootstrap.TabGroup.register(this);
18248     
18249 };
18250
18251 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18252     
18253     carousel : false,
18254     transition : false,
18255     bullets : 0,
18256     timer : 0,
18257     autoslide : false,
18258     slideFn : false,
18259     slideOnTouch : false,
18260     showarrow : true,
18261     
18262     getAutoCreate : function()
18263     {
18264         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18265         
18266         cfg.cls += ' tab-content';
18267         
18268         if (this.carousel) {
18269             cfg.cls += ' carousel slide';
18270             
18271             cfg.cn = [{
18272                cls : 'carousel-inner',
18273                cn : []
18274             }];
18275         
18276             if(this.bullets  && !Roo.isTouch){
18277                 
18278                 var bullets = {
18279                     cls : 'carousel-bullets',
18280                     cn : []
18281                 };
18282                
18283                 if(this.bullets_cls){
18284                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18285                 }
18286                 
18287                 bullets.cn.push({
18288                     cls : 'clear'
18289                 });
18290                 
18291                 cfg.cn[0].cn.push(bullets);
18292             }
18293             
18294             if(this.showarrow){
18295                 cfg.cn[0].cn.push({
18296                     tag : 'div',
18297                     class : 'carousel-arrow',
18298                     cn : [
18299                         {
18300                             tag : 'div',
18301                             class : 'carousel-prev',
18302                             cn : [
18303                                 {
18304                                     tag : 'i',
18305                                     class : 'fa fa-chevron-left'
18306                                 }
18307                             ]
18308                         },
18309                         {
18310                             tag : 'div',
18311                             class : 'carousel-next',
18312                             cn : [
18313                                 {
18314                                     tag : 'i',
18315                                     class : 'fa fa-chevron-right'
18316                                 }
18317                             ]
18318                         }
18319                     ]
18320                 });
18321             }
18322             
18323         }
18324         
18325         return cfg;
18326     },
18327     
18328     initEvents:  function()
18329     {
18330 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18331 //            this.el.on("touchstart", this.onTouchStart, this);
18332 //        }
18333         
18334         if(this.autoslide){
18335             var _this = this;
18336             
18337             this.slideFn = window.setInterval(function() {
18338                 _this.showPanelNext();
18339             }, this.timer);
18340         }
18341         
18342         if(this.showarrow){
18343             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18344             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18345         }
18346         
18347         
18348     },
18349     
18350 //    onTouchStart : function(e, el, o)
18351 //    {
18352 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18353 //            return;
18354 //        }
18355 //        
18356 //        this.showPanelNext();
18357 //    },
18358     
18359     
18360     getChildContainer : function()
18361     {
18362         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18363     },
18364     
18365     /**
18366     * register a Navigation item
18367     * @param {Roo.bootstrap.NavItem} the navitem to add
18368     */
18369     register : function(item)
18370     {
18371         this.tabs.push( item);
18372         item.navId = this.navId; // not really needed..
18373         this.addBullet();
18374     
18375     },
18376     
18377     getActivePanel : function()
18378     {
18379         var r = false;
18380         Roo.each(this.tabs, function(t) {
18381             if (t.active) {
18382                 r = t;
18383                 return false;
18384             }
18385             return null;
18386         });
18387         return r;
18388         
18389     },
18390     getPanelByName : function(n)
18391     {
18392         var r = false;
18393         Roo.each(this.tabs, function(t) {
18394             if (t.tabId == n) {
18395                 r = t;
18396                 return false;
18397             }
18398             return null;
18399         });
18400         return r;
18401     },
18402     indexOfPanel : function(p)
18403     {
18404         var r = false;
18405         Roo.each(this.tabs, function(t,i) {
18406             if (t.tabId == p.tabId) {
18407                 r = i;
18408                 return false;
18409             }
18410             return null;
18411         });
18412         return r;
18413     },
18414     /**
18415      * show a specific panel
18416      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18417      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18418      */
18419     showPanel : function (pan)
18420     {
18421         if(this.transition || typeof(pan) == 'undefined'){
18422             Roo.log("waiting for the transitionend");
18423             return false;
18424         }
18425         
18426         if (typeof(pan) == 'number') {
18427             pan = this.tabs[pan];
18428         }
18429         
18430         if (typeof(pan) == 'string') {
18431             pan = this.getPanelByName(pan);
18432         }
18433         
18434         var cur = this.getActivePanel();
18435         
18436         if(!pan || !cur){
18437             Roo.log('pan or acitve pan is undefined');
18438             return false;
18439         }
18440         
18441         if (pan.tabId == this.getActivePanel().tabId) {
18442             return true;
18443         }
18444         
18445         if (false === cur.fireEvent('beforedeactivate')) {
18446             return false;
18447         }
18448         
18449         if(this.bullets > 0 && !Roo.isTouch){
18450             this.setActiveBullet(this.indexOfPanel(pan));
18451         }
18452         
18453         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18454             
18455             //class="carousel-item carousel-item-next carousel-item-left"
18456             
18457             this.transition = true;
18458             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18459             var lr = dir == 'next' ? 'left' : 'right';
18460             pan.el.addClass(dir); // or prev
18461             pan.el.addClass('carousel-item-' + dir); // or prev
18462             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18463             cur.el.addClass(lr); // or right
18464             pan.el.addClass(lr);
18465             cur.el.addClass('carousel-item-' +lr); // or right
18466             pan.el.addClass('carousel-item-' +lr);
18467             
18468             
18469             var _this = this;
18470             cur.el.on('transitionend', function() {
18471                 Roo.log("trans end?");
18472                 
18473                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18474                 pan.setActive(true);
18475                 
18476                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18477                 cur.setActive(false);
18478                 
18479                 _this.transition = false;
18480                 
18481             }, this, { single:  true } );
18482             
18483             return true;
18484         }
18485         
18486         cur.setActive(false);
18487         pan.setActive(true);
18488         
18489         return true;
18490         
18491     },
18492     showPanelNext : function()
18493     {
18494         var i = this.indexOfPanel(this.getActivePanel());
18495         
18496         if (i >= this.tabs.length - 1 && !this.autoslide) {
18497             return;
18498         }
18499         
18500         if (i >= this.tabs.length - 1 && this.autoslide) {
18501             i = -1;
18502         }
18503         
18504         this.showPanel(this.tabs[i+1]);
18505     },
18506     
18507     showPanelPrev : function()
18508     {
18509         var i = this.indexOfPanel(this.getActivePanel());
18510         
18511         if (i  < 1 && !this.autoslide) {
18512             return;
18513         }
18514         
18515         if (i < 1 && this.autoslide) {
18516             i = this.tabs.length;
18517         }
18518         
18519         this.showPanel(this.tabs[i-1]);
18520     },
18521     
18522     
18523     addBullet: function()
18524     {
18525         if(!this.bullets || Roo.isTouch){
18526             return;
18527         }
18528         var ctr = this.el.select('.carousel-bullets',true).first();
18529         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18530         var bullet = ctr.createChild({
18531             cls : 'bullet bullet-' + i
18532         },ctr.dom.lastChild);
18533         
18534         
18535         var _this = this;
18536         
18537         bullet.on('click', (function(e, el, o, ii, t){
18538
18539             e.preventDefault();
18540
18541             this.showPanel(ii);
18542
18543             if(this.autoslide && this.slideFn){
18544                 clearInterval(this.slideFn);
18545                 this.slideFn = window.setInterval(function() {
18546                     _this.showPanelNext();
18547                 }, this.timer);
18548             }
18549
18550         }).createDelegate(this, [i, bullet], true));
18551                 
18552         
18553     },
18554      
18555     setActiveBullet : function(i)
18556     {
18557         if(Roo.isTouch){
18558             return;
18559         }
18560         
18561         Roo.each(this.el.select('.bullet', true).elements, function(el){
18562             el.removeClass('selected');
18563         });
18564
18565         var bullet = this.el.select('.bullet-' + i, true).first();
18566         
18567         if(!bullet){
18568             return;
18569         }
18570         
18571         bullet.addClass('selected');
18572     }
18573     
18574     
18575   
18576 });
18577
18578  
18579
18580  
18581  
18582 Roo.apply(Roo.bootstrap.TabGroup, {
18583     
18584     groups: {},
18585      /**
18586     * register a Navigation Group
18587     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18588     */
18589     register : function(navgrp)
18590     {
18591         this.groups[navgrp.navId] = navgrp;
18592         
18593     },
18594     /**
18595     * fetch a Navigation Group based on the navigation ID
18596     * if one does not exist , it will get created.
18597     * @param {string} the navgroup to add
18598     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18599     */
18600     get: function(navId) {
18601         if (typeof(this.groups[navId]) == 'undefined') {
18602             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18603         }
18604         return this.groups[navId] ;
18605     }
18606     
18607     
18608     
18609 });
18610
18611  /*
18612  * - LGPL
18613  *
18614  * TabPanel
18615  * 
18616  */
18617
18618 /**
18619  * @class Roo.bootstrap.TabPanel
18620  * @extends Roo.bootstrap.Component
18621  * Bootstrap TabPanel class
18622  * @cfg {Boolean} active panel active
18623  * @cfg {String} html panel content
18624  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18625  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18626  * @cfg {String} href click to link..
18627  * 
18628  * 
18629  * @constructor
18630  * Create a new TabPanel
18631  * @param {Object} config The config object
18632  */
18633
18634 Roo.bootstrap.TabPanel = function(config){
18635     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18636     this.addEvents({
18637         /**
18638              * @event changed
18639              * Fires when the active status changes
18640              * @param {Roo.bootstrap.TabPanel} this
18641              * @param {Boolean} state the new state
18642             
18643          */
18644         'changed': true,
18645         /**
18646              * @event beforedeactivate
18647              * Fires before a tab is de-activated - can be used to do validation on a form.
18648              * @param {Roo.bootstrap.TabPanel} this
18649              * @return {Boolean} false if there is an error
18650             
18651          */
18652         'beforedeactivate': true
18653      });
18654     
18655     this.tabId = this.tabId || Roo.id();
18656   
18657 };
18658
18659 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18660     
18661     active: false,
18662     html: false,
18663     tabId: false,
18664     navId : false,
18665     href : '',
18666     
18667     getAutoCreate : function(){
18668         
18669         
18670         var cfg = {
18671             tag: 'div',
18672             // item is needed for carousel - not sure if it has any effect otherwise
18673             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18674             html: this.html || ''
18675         };
18676         
18677         if(this.active){
18678             cfg.cls += ' active';
18679         }
18680         
18681         if(this.tabId){
18682             cfg.tabId = this.tabId;
18683         }
18684         
18685         
18686         
18687         return cfg;
18688     },
18689     
18690     initEvents:  function()
18691     {
18692         var p = this.parent();
18693         
18694         this.navId = this.navId || p.navId;
18695         
18696         if (typeof(this.navId) != 'undefined') {
18697             // not really needed.. but just in case.. parent should be a NavGroup.
18698             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18699             
18700             tg.register(this);
18701             
18702             var i = tg.tabs.length - 1;
18703             
18704             if(this.active && tg.bullets > 0 && i < tg.bullets){
18705                 tg.setActiveBullet(i);
18706             }
18707         }
18708         
18709         this.el.on('click', this.onClick, this);
18710         
18711         if(Roo.isTouch){
18712             this.el.on("touchstart", this.onTouchStart, this);
18713             this.el.on("touchmove", this.onTouchMove, this);
18714             this.el.on("touchend", this.onTouchEnd, this);
18715         }
18716         
18717     },
18718     
18719     onRender : function(ct, position)
18720     {
18721         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18722     },
18723     
18724     setActive : function(state)
18725     {
18726         Roo.log("panel - set active " + this.tabId + "=" + state);
18727         
18728         this.active = state;
18729         if (!state) {
18730             this.el.removeClass('active');
18731             
18732         } else  if (!this.el.hasClass('active')) {
18733             this.el.addClass('active');
18734         }
18735         
18736         this.fireEvent('changed', this, state);
18737     },
18738     
18739     onClick : function(e)
18740     {
18741         e.preventDefault();
18742         
18743         if(!this.href.length){
18744             return;
18745         }
18746         
18747         window.location.href = this.href;
18748     },
18749     
18750     startX : 0,
18751     startY : 0,
18752     endX : 0,
18753     endY : 0,
18754     swiping : false,
18755     
18756     onTouchStart : function(e)
18757     {
18758         this.swiping = false;
18759         
18760         this.startX = e.browserEvent.touches[0].clientX;
18761         this.startY = e.browserEvent.touches[0].clientY;
18762     },
18763     
18764     onTouchMove : function(e)
18765     {
18766         this.swiping = true;
18767         
18768         this.endX = e.browserEvent.touches[0].clientX;
18769         this.endY = e.browserEvent.touches[0].clientY;
18770     },
18771     
18772     onTouchEnd : function(e)
18773     {
18774         if(!this.swiping){
18775             this.onClick(e);
18776             return;
18777         }
18778         
18779         var tabGroup = this.parent();
18780         
18781         if(this.endX > this.startX){ // swiping right
18782             tabGroup.showPanelPrev();
18783             return;
18784         }
18785         
18786         if(this.startX > this.endX){ // swiping left
18787             tabGroup.showPanelNext();
18788             return;
18789         }
18790     }
18791     
18792     
18793 });
18794  
18795
18796  
18797
18798  /*
18799  * - LGPL
18800  *
18801  * DateField
18802  * 
18803  */
18804
18805 /**
18806  * @class Roo.bootstrap.DateField
18807  * @extends Roo.bootstrap.Input
18808  * Bootstrap DateField class
18809  * @cfg {Number} weekStart default 0
18810  * @cfg {String} viewMode default empty, (months|years)
18811  * @cfg {String} minViewMode default empty, (months|years)
18812  * @cfg {Number} startDate default -Infinity
18813  * @cfg {Number} endDate default Infinity
18814  * @cfg {Boolean} todayHighlight default false
18815  * @cfg {Boolean} todayBtn default false
18816  * @cfg {Boolean} calendarWeeks default false
18817  * @cfg {Object} daysOfWeekDisabled default empty
18818  * @cfg {Boolean} singleMode default false (true | false)
18819  * 
18820  * @cfg {Boolean} keyboardNavigation default true
18821  * @cfg {String} language default en
18822  * 
18823  * @constructor
18824  * Create a new DateField
18825  * @param {Object} config The config object
18826  */
18827
18828 Roo.bootstrap.DateField = function(config){
18829     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18830      this.addEvents({
18831             /**
18832              * @event show
18833              * Fires when this field show.
18834              * @param {Roo.bootstrap.DateField} this
18835              * @param {Mixed} date The date value
18836              */
18837             show : true,
18838             /**
18839              * @event show
18840              * Fires when this field hide.
18841              * @param {Roo.bootstrap.DateField} this
18842              * @param {Mixed} date The date value
18843              */
18844             hide : true,
18845             /**
18846              * @event select
18847              * Fires when select a date.
18848              * @param {Roo.bootstrap.DateField} this
18849              * @param {Mixed} date The date value
18850              */
18851             select : true,
18852             /**
18853              * @event beforeselect
18854              * Fires when before select a date.
18855              * @param {Roo.bootstrap.DateField} this
18856              * @param {Mixed} date The date value
18857              */
18858             beforeselect : true
18859         });
18860 };
18861
18862 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18863     
18864     /**
18865      * @cfg {String} format
18866      * The default date format string which can be overriden for localization support.  The format must be
18867      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18868      */
18869     format : "m/d/y",
18870     /**
18871      * @cfg {String} altFormats
18872      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18873      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18874      */
18875     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18876     
18877     weekStart : 0,
18878     
18879     viewMode : '',
18880     
18881     minViewMode : '',
18882     
18883     todayHighlight : false,
18884     
18885     todayBtn: false,
18886     
18887     language: 'en',
18888     
18889     keyboardNavigation: true,
18890     
18891     calendarWeeks: false,
18892     
18893     startDate: -Infinity,
18894     
18895     endDate: Infinity,
18896     
18897     daysOfWeekDisabled: [],
18898     
18899     _events: [],
18900     
18901     singleMode : false,
18902     
18903     UTCDate: function()
18904     {
18905         return new Date(Date.UTC.apply(Date, arguments));
18906     },
18907     
18908     UTCToday: function()
18909     {
18910         var today = new Date();
18911         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18912     },
18913     
18914     getDate: function() {
18915             var d = this.getUTCDate();
18916             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18917     },
18918     
18919     getUTCDate: function() {
18920             return this.date;
18921     },
18922     
18923     setDate: function(d) {
18924             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18925     },
18926     
18927     setUTCDate: function(d) {
18928             this.date = d;
18929             this.setValue(this.formatDate(this.date));
18930     },
18931         
18932     onRender: function(ct, position)
18933     {
18934         
18935         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18936         
18937         this.language = this.language || 'en';
18938         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18939         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18940         
18941         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18942         this.format = this.format || 'm/d/y';
18943         this.isInline = false;
18944         this.isInput = true;
18945         this.component = this.el.select('.add-on', true).first() || false;
18946         this.component = (this.component && this.component.length === 0) ? false : this.component;
18947         this.hasInput = this.component && this.inputEl().length;
18948         
18949         if (typeof(this.minViewMode === 'string')) {
18950             switch (this.minViewMode) {
18951                 case 'months':
18952                     this.minViewMode = 1;
18953                     break;
18954                 case 'years':
18955                     this.minViewMode = 2;
18956                     break;
18957                 default:
18958                     this.minViewMode = 0;
18959                     break;
18960             }
18961         }
18962         
18963         if (typeof(this.viewMode === 'string')) {
18964             switch (this.viewMode) {
18965                 case 'months':
18966                     this.viewMode = 1;
18967                     break;
18968                 case 'years':
18969                     this.viewMode = 2;
18970                     break;
18971                 default:
18972                     this.viewMode = 0;
18973                     break;
18974             }
18975         }
18976                 
18977         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18978         
18979 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18980         
18981         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18982         
18983         this.picker().on('mousedown', this.onMousedown, this);
18984         this.picker().on('click', this.onClick, this);
18985         
18986         this.picker().addClass('datepicker-dropdown');
18987         
18988         this.startViewMode = this.viewMode;
18989         
18990         if(this.singleMode){
18991             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18992                 v.setVisibilityMode(Roo.Element.DISPLAY);
18993                 v.hide();
18994             });
18995             
18996             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18997                 v.setStyle('width', '189px');
18998             });
18999         }
19000         
19001         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19002             if(!this.calendarWeeks){
19003                 v.remove();
19004                 return;
19005             }
19006             
19007             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19008             v.attr('colspan', function(i, val){
19009                 return parseInt(val) + 1;
19010             });
19011         });
19012                         
19013         
19014         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19015         
19016         this.setStartDate(this.startDate);
19017         this.setEndDate(this.endDate);
19018         
19019         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19020         
19021         this.fillDow();
19022         this.fillMonths();
19023         this.update();
19024         this.showMode();
19025         
19026         if(this.isInline) {
19027             this.showPopup();
19028         }
19029     },
19030     
19031     picker : function()
19032     {
19033         return this.pickerEl;
19034 //        return this.el.select('.datepicker', true).first();
19035     },
19036     
19037     fillDow: function()
19038     {
19039         var dowCnt = this.weekStart;
19040         
19041         var dow = {
19042             tag: 'tr',
19043             cn: [
19044                 
19045             ]
19046         };
19047         
19048         if(this.calendarWeeks){
19049             dow.cn.push({
19050                 tag: 'th',
19051                 cls: 'cw',
19052                 html: '&nbsp;'
19053             })
19054         }
19055         
19056         while (dowCnt < this.weekStart + 7) {
19057             dow.cn.push({
19058                 tag: 'th',
19059                 cls: 'dow',
19060                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19061             });
19062         }
19063         
19064         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19065     },
19066     
19067     fillMonths: function()
19068     {    
19069         var i = 0;
19070         var months = this.picker().select('>.datepicker-months td', true).first();
19071         
19072         months.dom.innerHTML = '';
19073         
19074         while (i < 12) {
19075             var month = {
19076                 tag: 'span',
19077                 cls: 'month',
19078                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19079             };
19080             
19081             months.createChild(month);
19082         }
19083         
19084     },
19085     
19086     update: function()
19087     {
19088         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;
19089         
19090         if (this.date < this.startDate) {
19091             this.viewDate = new Date(this.startDate);
19092         } else if (this.date > this.endDate) {
19093             this.viewDate = new Date(this.endDate);
19094         } else {
19095             this.viewDate = new Date(this.date);
19096         }
19097         
19098         this.fill();
19099     },
19100     
19101     fill: function() 
19102     {
19103         var d = new Date(this.viewDate),
19104                 year = d.getUTCFullYear(),
19105                 month = d.getUTCMonth(),
19106                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19107                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19108                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19109                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19110                 currentDate = this.date && this.date.valueOf(),
19111                 today = this.UTCToday();
19112         
19113         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19114         
19115 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19116         
19117 //        this.picker.select('>tfoot th.today').
19118 //                                              .text(dates[this.language].today)
19119 //                                              .toggle(this.todayBtn !== false);
19120     
19121         this.updateNavArrows();
19122         this.fillMonths();
19123                                                 
19124         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19125         
19126         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19127          
19128         prevMonth.setUTCDate(day);
19129         
19130         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19131         
19132         var nextMonth = new Date(prevMonth);
19133         
19134         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19135         
19136         nextMonth = nextMonth.valueOf();
19137         
19138         var fillMonths = false;
19139         
19140         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19141         
19142         while(prevMonth.valueOf() <= nextMonth) {
19143             var clsName = '';
19144             
19145             if (prevMonth.getUTCDay() === this.weekStart) {
19146                 if(fillMonths){
19147                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19148                 }
19149                     
19150                 fillMonths = {
19151                     tag: 'tr',
19152                     cn: []
19153                 };
19154                 
19155                 if(this.calendarWeeks){
19156                     // ISO 8601: First week contains first thursday.
19157                     // ISO also states week starts on Monday, but we can be more abstract here.
19158                     var
19159                     // Start of current week: based on weekstart/current date
19160                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19161                     // Thursday of this week
19162                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19163                     // First Thursday of year, year from thursday
19164                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19165                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19166                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19167                     
19168                     fillMonths.cn.push({
19169                         tag: 'td',
19170                         cls: 'cw',
19171                         html: calWeek
19172                     });
19173                 }
19174             }
19175             
19176             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19177                 clsName += ' old';
19178             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19179                 clsName += ' new';
19180             }
19181             if (this.todayHighlight &&
19182                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19183                 prevMonth.getUTCMonth() == today.getMonth() &&
19184                 prevMonth.getUTCDate() == today.getDate()) {
19185                 clsName += ' today';
19186             }
19187             
19188             if (currentDate && prevMonth.valueOf() === currentDate) {
19189                 clsName += ' active';
19190             }
19191             
19192             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19193                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19194                     clsName += ' disabled';
19195             }
19196             
19197             fillMonths.cn.push({
19198                 tag: 'td',
19199                 cls: 'day ' + clsName,
19200                 html: prevMonth.getDate()
19201             });
19202             
19203             prevMonth.setDate(prevMonth.getDate()+1);
19204         }
19205           
19206         var currentYear = this.date && this.date.getUTCFullYear();
19207         var currentMonth = this.date && this.date.getUTCMonth();
19208         
19209         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19210         
19211         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19212             v.removeClass('active');
19213             
19214             if(currentYear === year && k === currentMonth){
19215                 v.addClass('active');
19216             }
19217             
19218             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19219                 v.addClass('disabled');
19220             }
19221             
19222         });
19223         
19224         
19225         year = parseInt(year/10, 10) * 10;
19226         
19227         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19228         
19229         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19230         
19231         year -= 1;
19232         for (var i = -1; i < 11; i++) {
19233             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19234                 tag: 'span',
19235                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19236                 html: year
19237             });
19238             
19239             year += 1;
19240         }
19241     },
19242     
19243     showMode: function(dir) 
19244     {
19245         if (dir) {
19246             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19247         }
19248         
19249         Roo.each(this.picker().select('>div',true).elements, function(v){
19250             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19251             v.hide();
19252         });
19253         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19254     },
19255     
19256     place: function()
19257     {
19258         if(this.isInline) {
19259             return;
19260         }
19261         
19262         this.picker().removeClass(['bottom', 'top']);
19263         
19264         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19265             /*
19266              * place to the top of element!
19267              *
19268              */
19269             
19270             this.picker().addClass('top');
19271             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19272             
19273             return;
19274         }
19275         
19276         this.picker().addClass('bottom');
19277         
19278         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19279     },
19280     
19281     parseDate : function(value)
19282     {
19283         if(!value || value instanceof Date){
19284             return value;
19285         }
19286         var v = Date.parseDate(value, this.format);
19287         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19288             v = Date.parseDate(value, 'Y-m-d');
19289         }
19290         if(!v && this.altFormats){
19291             if(!this.altFormatsArray){
19292                 this.altFormatsArray = this.altFormats.split("|");
19293             }
19294             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19295                 v = Date.parseDate(value, this.altFormatsArray[i]);
19296             }
19297         }
19298         return v;
19299     },
19300     
19301     formatDate : function(date, fmt)
19302     {   
19303         return (!date || !(date instanceof Date)) ?
19304         date : date.dateFormat(fmt || this.format);
19305     },
19306     
19307     onFocus : function()
19308     {
19309         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19310         this.showPopup();
19311     },
19312     
19313     onBlur : function()
19314     {
19315         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19316         
19317         var d = this.inputEl().getValue();
19318         
19319         this.setValue(d);
19320                 
19321         this.hidePopup();
19322     },
19323     
19324     showPopup : function()
19325     {
19326         this.picker().show();
19327         this.update();
19328         this.place();
19329         
19330         this.fireEvent('showpopup', this, this.date);
19331     },
19332     
19333     hidePopup : function()
19334     {
19335         if(this.isInline) {
19336             return;
19337         }
19338         this.picker().hide();
19339         this.viewMode = this.startViewMode;
19340         this.showMode();
19341         
19342         this.fireEvent('hidepopup', this, this.date);
19343         
19344     },
19345     
19346     onMousedown: function(e)
19347     {
19348         e.stopPropagation();
19349         e.preventDefault();
19350     },
19351     
19352     keyup: function(e)
19353     {
19354         Roo.bootstrap.DateField.superclass.keyup.call(this);
19355         this.update();
19356     },
19357
19358     setValue: function(v)
19359     {
19360         if(this.fireEvent('beforeselect', this, v) !== false){
19361             var d = new Date(this.parseDate(v) ).clearTime();
19362         
19363             if(isNaN(d.getTime())){
19364                 this.date = this.viewDate = '';
19365                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19366                 return;
19367             }
19368
19369             v = this.formatDate(d);
19370
19371             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19372
19373             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19374
19375             this.update();
19376
19377             this.fireEvent('select', this, this.date);
19378         }
19379     },
19380     
19381     getValue: function()
19382     {
19383         return this.formatDate(this.date);
19384     },
19385     
19386     fireKey: function(e)
19387     {
19388         if (!this.picker().isVisible()){
19389             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19390                 this.showPopup();
19391             }
19392             return;
19393         }
19394         
19395         var dateChanged = false,
19396         dir, day, month,
19397         newDate, newViewDate;
19398         
19399         switch(e.keyCode){
19400             case 27: // escape
19401                 this.hidePopup();
19402                 e.preventDefault();
19403                 break;
19404             case 37: // left
19405             case 39: // right
19406                 if (!this.keyboardNavigation) {
19407                     break;
19408                 }
19409                 dir = e.keyCode == 37 ? -1 : 1;
19410                 
19411                 if (e.ctrlKey){
19412                     newDate = this.moveYear(this.date, dir);
19413                     newViewDate = this.moveYear(this.viewDate, dir);
19414                 } else if (e.shiftKey){
19415                     newDate = this.moveMonth(this.date, dir);
19416                     newViewDate = this.moveMonth(this.viewDate, dir);
19417                 } else {
19418                     newDate = new Date(this.date);
19419                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19420                     newViewDate = new Date(this.viewDate);
19421                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19422                 }
19423                 if (this.dateWithinRange(newDate)){
19424                     this.date = newDate;
19425                     this.viewDate = newViewDate;
19426                     this.setValue(this.formatDate(this.date));
19427 //                    this.update();
19428                     e.preventDefault();
19429                     dateChanged = true;
19430                 }
19431                 break;
19432             case 38: // up
19433             case 40: // down
19434                 if (!this.keyboardNavigation) {
19435                     break;
19436                 }
19437                 dir = e.keyCode == 38 ? -1 : 1;
19438                 if (e.ctrlKey){
19439                     newDate = this.moveYear(this.date, dir);
19440                     newViewDate = this.moveYear(this.viewDate, dir);
19441                 } else if (e.shiftKey){
19442                     newDate = this.moveMonth(this.date, dir);
19443                     newViewDate = this.moveMonth(this.viewDate, dir);
19444                 } else {
19445                     newDate = new Date(this.date);
19446                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19447                     newViewDate = new Date(this.viewDate);
19448                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19449                 }
19450                 if (this.dateWithinRange(newDate)){
19451                     this.date = newDate;
19452                     this.viewDate = newViewDate;
19453                     this.setValue(this.formatDate(this.date));
19454 //                    this.update();
19455                     e.preventDefault();
19456                     dateChanged = true;
19457                 }
19458                 break;
19459             case 13: // enter
19460                 this.setValue(this.formatDate(this.date));
19461                 this.hidePopup();
19462                 e.preventDefault();
19463                 break;
19464             case 9: // tab
19465                 this.setValue(this.formatDate(this.date));
19466                 this.hidePopup();
19467                 break;
19468             case 16: // shift
19469             case 17: // ctrl
19470             case 18: // alt
19471                 break;
19472             default :
19473                 this.hidePopup();
19474                 
19475         }
19476     },
19477     
19478     
19479     onClick: function(e) 
19480     {
19481         e.stopPropagation();
19482         e.preventDefault();
19483         
19484         var target = e.getTarget();
19485         
19486         if(target.nodeName.toLowerCase() === 'i'){
19487             target = Roo.get(target).dom.parentNode;
19488         }
19489         
19490         var nodeName = target.nodeName;
19491         var className = target.className;
19492         var html = target.innerHTML;
19493         //Roo.log(nodeName);
19494         
19495         switch(nodeName.toLowerCase()) {
19496             case 'th':
19497                 switch(className) {
19498                     case 'switch':
19499                         this.showMode(1);
19500                         break;
19501                     case 'prev':
19502                     case 'next':
19503                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19504                         switch(this.viewMode){
19505                                 case 0:
19506                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19507                                         break;
19508                                 case 1:
19509                                 case 2:
19510                                         this.viewDate = this.moveYear(this.viewDate, dir);
19511                                         break;
19512                         }
19513                         this.fill();
19514                         break;
19515                     case 'today':
19516                         var date = new Date();
19517                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19518 //                        this.fill()
19519                         this.setValue(this.formatDate(this.date));
19520                         
19521                         this.hidePopup();
19522                         break;
19523                 }
19524                 break;
19525             case 'span':
19526                 if (className.indexOf('disabled') < 0) {
19527                     this.viewDate.setUTCDate(1);
19528                     if (className.indexOf('month') > -1) {
19529                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19530                     } else {
19531                         var year = parseInt(html, 10) || 0;
19532                         this.viewDate.setUTCFullYear(year);
19533                         
19534                     }
19535                     
19536                     if(this.singleMode){
19537                         this.setValue(this.formatDate(this.viewDate));
19538                         this.hidePopup();
19539                         return;
19540                     }
19541                     
19542                     this.showMode(-1);
19543                     this.fill();
19544                 }
19545                 break;
19546                 
19547             case 'td':
19548                 //Roo.log(className);
19549                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19550                     var day = parseInt(html, 10) || 1;
19551                     var year = this.viewDate.getUTCFullYear(),
19552                         month = this.viewDate.getUTCMonth();
19553
19554                     if (className.indexOf('old') > -1) {
19555                         if(month === 0 ){
19556                             month = 11;
19557                             year -= 1;
19558                         }else{
19559                             month -= 1;
19560                         }
19561                     } else if (className.indexOf('new') > -1) {
19562                         if (month == 11) {
19563                             month = 0;
19564                             year += 1;
19565                         } else {
19566                             month += 1;
19567                         }
19568                     }
19569                     //Roo.log([year,month,day]);
19570                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19571                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19572 //                    this.fill();
19573                     //Roo.log(this.formatDate(this.date));
19574                     this.setValue(this.formatDate(this.date));
19575                     this.hidePopup();
19576                 }
19577                 break;
19578         }
19579     },
19580     
19581     setStartDate: function(startDate)
19582     {
19583         this.startDate = startDate || -Infinity;
19584         if (this.startDate !== -Infinity) {
19585             this.startDate = this.parseDate(this.startDate);
19586         }
19587         this.update();
19588         this.updateNavArrows();
19589     },
19590
19591     setEndDate: function(endDate)
19592     {
19593         this.endDate = endDate || Infinity;
19594         if (this.endDate !== Infinity) {
19595             this.endDate = this.parseDate(this.endDate);
19596         }
19597         this.update();
19598         this.updateNavArrows();
19599     },
19600     
19601     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19602     {
19603         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19604         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19605             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19606         }
19607         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19608             return parseInt(d, 10);
19609         });
19610         this.update();
19611         this.updateNavArrows();
19612     },
19613     
19614     updateNavArrows: function() 
19615     {
19616         if(this.singleMode){
19617             return;
19618         }
19619         
19620         var d = new Date(this.viewDate),
19621         year = d.getUTCFullYear(),
19622         month = d.getUTCMonth();
19623         
19624         Roo.each(this.picker().select('.prev', true).elements, function(v){
19625             v.show();
19626             switch (this.viewMode) {
19627                 case 0:
19628
19629                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19630                         v.hide();
19631                     }
19632                     break;
19633                 case 1:
19634                 case 2:
19635                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19636                         v.hide();
19637                     }
19638                     break;
19639             }
19640         });
19641         
19642         Roo.each(this.picker().select('.next', true).elements, function(v){
19643             v.show();
19644             switch (this.viewMode) {
19645                 case 0:
19646
19647                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19648                         v.hide();
19649                     }
19650                     break;
19651                 case 1:
19652                 case 2:
19653                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19654                         v.hide();
19655                     }
19656                     break;
19657             }
19658         })
19659     },
19660     
19661     moveMonth: function(date, dir)
19662     {
19663         if (!dir) {
19664             return date;
19665         }
19666         var new_date = new Date(date.valueOf()),
19667         day = new_date.getUTCDate(),
19668         month = new_date.getUTCMonth(),
19669         mag = Math.abs(dir),
19670         new_month, test;
19671         dir = dir > 0 ? 1 : -1;
19672         if (mag == 1){
19673             test = dir == -1
19674             // If going back one month, make sure month is not current month
19675             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19676             ? function(){
19677                 return new_date.getUTCMonth() == month;
19678             }
19679             // If going forward one month, make sure month is as expected
19680             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19681             : function(){
19682                 return new_date.getUTCMonth() != new_month;
19683             };
19684             new_month = month + dir;
19685             new_date.setUTCMonth(new_month);
19686             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19687             if (new_month < 0 || new_month > 11) {
19688                 new_month = (new_month + 12) % 12;
19689             }
19690         } else {
19691             // For magnitudes >1, move one month at a time...
19692             for (var i=0; i<mag; i++) {
19693                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19694                 new_date = this.moveMonth(new_date, dir);
19695             }
19696             // ...then reset the day, keeping it in the new month
19697             new_month = new_date.getUTCMonth();
19698             new_date.setUTCDate(day);
19699             test = function(){
19700                 return new_month != new_date.getUTCMonth();
19701             };
19702         }
19703         // Common date-resetting loop -- if date is beyond end of month, make it
19704         // end of month
19705         while (test()){
19706             new_date.setUTCDate(--day);
19707             new_date.setUTCMonth(new_month);
19708         }
19709         return new_date;
19710     },
19711
19712     moveYear: function(date, dir)
19713     {
19714         return this.moveMonth(date, dir*12);
19715     },
19716
19717     dateWithinRange: function(date)
19718     {
19719         return date >= this.startDate && date <= this.endDate;
19720     },
19721
19722     
19723     remove: function() 
19724     {
19725         this.picker().remove();
19726     },
19727     
19728     validateValue : function(value)
19729     {
19730         if(this.getVisibilityEl().hasClass('hidden')){
19731             return true;
19732         }
19733         
19734         if(value.length < 1)  {
19735             if(this.allowBlank){
19736                 return true;
19737             }
19738             return false;
19739         }
19740         
19741         if(value.length < this.minLength){
19742             return false;
19743         }
19744         if(value.length > this.maxLength){
19745             return false;
19746         }
19747         if(this.vtype){
19748             var vt = Roo.form.VTypes;
19749             if(!vt[this.vtype](value, this)){
19750                 return false;
19751             }
19752         }
19753         if(typeof this.validator == "function"){
19754             var msg = this.validator(value);
19755             if(msg !== true){
19756                 return false;
19757             }
19758         }
19759         
19760         if(this.regex && !this.regex.test(value)){
19761             return false;
19762         }
19763         
19764         if(typeof(this.parseDate(value)) == 'undefined'){
19765             return false;
19766         }
19767         
19768         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19769             return false;
19770         }      
19771         
19772         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19773             return false;
19774         } 
19775         
19776         
19777         return true;
19778     },
19779     
19780     reset : function()
19781     {
19782         this.date = this.viewDate = '';
19783         
19784         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19785     }
19786    
19787 });
19788
19789 Roo.apply(Roo.bootstrap.DateField,  {
19790     
19791     head : {
19792         tag: 'thead',
19793         cn: [
19794         {
19795             tag: 'tr',
19796             cn: [
19797             {
19798                 tag: 'th',
19799                 cls: 'prev',
19800                 html: '<i class="fa fa-arrow-left"/>'
19801             },
19802             {
19803                 tag: 'th',
19804                 cls: 'switch',
19805                 colspan: '5'
19806             },
19807             {
19808                 tag: 'th',
19809                 cls: 'next',
19810                 html: '<i class="fa fa-arrow-right"/>'
19811             }
19812
19813             ]
19814         }
19815         ]
19816     },
19817     
19818     content : {
19819         tag: 'tbody',
19820         cn: [
19821         {
19822             tag: 'tr',
19823             cn: [
19824             {
19825                 tag: 'td',
19826                 colspan: '7'
19827             }
19828             ]
19829         }
19830         ]
19831     },
19832     
19833     footer : {
19834         tag: 'tfoot',
19835         cn: [
19836         {
19837             tag: 'tr',
19838             cn: [
19839             {
19840                 tag: 'th',
19841                 colspan: '7',
19842                 cls: 'today'
19843             }
19844                     
19845             ]
19846         }
19847         ]
19848     },
19849     
19850     dates:{
19851         en: {
19852             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19853             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19854             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19855             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19856             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19857             today: "Today"
19858         }
19859     },
19860     
19861     modes: [
19862     {
19863         clsName: 'days',
19864         navFnc: 'Month',
19865         navStep: 1
19866     },
19867     {
19868         clsName: 'months',
19869         navFnc: 'FullYear',
19870         navStep: 1
19871     },
19872     {
19873         clsName: 'years',
19874         navFnc: 'FullYear',
19875         navStep: 10
19876     }]
19877 });
19878
19879 Roo.apply(Roo.bootstrap.DateField,  {
19880   
19881     template : {
19882         tag: 'div',
19883         cls: 'datepicker dropdown-menu roo-dynamic',
19884         cn: [
19885         {
19886             tag: 'div',
19887             cls: 'datepicker-days',
19888             cn: [
19889             {
19890                 tag: 'table',
19891                 cls: 'table-condensed',
19892                 cn:[
19893                 Roo.bootstrap.DateField.head,
19894                 {
19895                     tag: 'tbody'
19896                 },
19897                 Roo.bootstrap.DateField.footer
19898                 ]
19899             }
19900             ]
19901         },
19902         {
19903             tag: 'div',
19904             cls: 'datepicker-months',
19905             cn: [
19906             {
19907                 tag: 'table',
19908                 cls: 'table-condensed',
19909                 cn:[
19910                 Roo.bootstrap.DateField.head,
19911                 Roo.bootstrap.DateField.content,
19912                 Roo.bootstrap.DateField.footer
19913                 ]
19914             }
19915             ]
19916         },
19917         {
19918             tag: 'div',
19919             cls: 'datepicker-years',
19920             cn: [
19921             {
19922                 tag: 'table',
19923                 cls: 'table-condensed',
19924                 cn:[
19925                 Roo.bootstrap.DateField.head,
19926                 Roo.bootstrap.DateField.content,
19927                 Roo.bootstrap.DateField.footer
19928                 ]
19929             }
19930             ]
19931         }
19932         ]
19933     }
19934 });
19935
19936  
19937
19938  /*
19939  * - LGPL
19940  *
19941  * TimeField
19942  * 
19943  */
19944
19945 /**
19946  * @class Roo.bootstrap.TimeField
19947  * @extends Roo.bootstrap.Input
19948  * Bootstrap DateField class
19949  * 
19950  * 
19951  * @constructor
19952  * Create a new TimeField
19953  * @param {Object} config The config object
19954  */
19955
19956 Roo.bootstrap.TimeField = function(config){
19957     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19958     this.addEvents({
19959             /**
19960              * @event show
19961              * Fires when this field show.
19962              * @param {Roo.bootstrap.DateField} thisthis
19963              * @param {Mixed} date The date value
19964              */
19965             show : true,
19966             /**
19967              * @event show
19968              * Fires when this field hide.
19969              * @param {Roo.bootstrap.DateField} this
19970              * @param {Mixed} date The date value
19971              */
19972             hide : true,
19973             /**
19974              * @event select
19975              * Fires when select a date.
19976              * @param {Roo.bootstrap.DateField} this
19977              * @param {Mixed} date The date value
19978              */
19979             select : true
19980         });
19981 };
19982
19983 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19984     
19985     /**
19986      * @cfg {String} format
19987      * The default time format string which can be overriden for localization support.  The format must be
19988      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19989      */
19990     format : "H:i",
19991        
19992     onRender: function(ct, position)
19993     {
19994         
19995         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19996                 
19997         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19998         
19999         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20000         
20001         this.pop = this.picker().select('>.datepicker-time',true).first();
20002         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20003         
20004         this.picker().on('mousedown', this.onMousedown, this);
20005         this.picker().on('click', this.onClick, this);
20006         
20007         this.picker().addClass('datepicker-dropdown');
20008     
20009         this.fillTime();
20010         this.update();
20011             
20012         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20013         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20014         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20015         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20016         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20017         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20018
20019     },
20020     
20021     fireKey: function(e){
20022         if (!this.picker().isVisible()){
20023             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20024                 this.show();
20025             }
20026             return;
20027         }
20028
20029         e.preventDefault();
20030         
20031         switch(e.keyCode){
20032             case 27: // escape
20033                 this.hide();
20034                 break;
20035             case 37: // left
20036             case 39: // right
20037                 this.onTogglePeriod();
20038                 break;
20039             case 38: // up
20040                 this.onIncrementMinutes();
20041                 break;
20042             case 40: // down
20043                 this.onDecrementMinutes();
20044                 break;
20045             case 13: // enter
20046             case 9: // tab
20047                 this.setTime();
20048                 break;
20049         }
20050     },
20051     
20052     onClick: function(e) {
20053         e.stopPropagation();
20054         e.preventDefault();
20055     },
20056     
20057     picker : function()
20058     {
20059         return this.el.select('.datepicker', true).first();
20060     },
20061     
20062     fillTime: function()
20063     {    
20064         var time = this.pop.select('tbody', true).first();
20065         
20066         time.dom.innerHTML = '';
20067         
20068         time.createChild({
20069             tag: 'tr',
20070             cn: [
20071                 {
20072                     tag: 'td',
20073                     cn: [
20074                         {
20075                             tag: 'a',
20076                             href: '#',
20077                             cls: 'btn',
20078                             cn: [
20079                                 {
20080                                     tag: 'span',
20081                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20082                                 }
20083                             ]
20084                         } 
20085                     ]
20086                 },
20087                 {
20088                     tag: 'td',
20089                     cls: 'separator'
20090                 },
20091                 {
20092                     tag: 'td',
20093                     cn: [
20094                         {
20095                             tag: 'a',
20096                             href: '#',
20097                             cls: 'btn',
20098                             cn: [
20099                                 {
20100                                     tag: 'span',
20101                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20102                                 }
20103                             ]
20104                         }
20105                     ]
20106                 },
20107                 {
20108                     tag: 'td',
20109                     cls: 'separator'
20110                 }
20111             ]
20112         });
20113         
20114         time.createChild({
20115             tag: 'tr',
20116             cn: [
20117                 {
20118                     tag: 'td',
20119                     cn: [
20120                         {
20121                             tag: 'span',
20122                             cls: 'timepicker-hour',
20123                             html: '00'
20124                         }  
20125                     ]
20126                 },
20127                 {
20128                     tag: 'td',
20129                     cls: 'separator',
20130                     html: ':'
20131                 },
20132                 {
20133                     tag: 'td',
20134                     cn: [
20135                         {
20136                             tag: 'span',
20137                             cls: 'timepicker-minute',
20138                             html: '00'
20139                         }  
20140                     ]
20141                 },
20142                 {
20143                     tag: 'td',
20144                     cls: 'separator'
20145                 },
20146                 {
20147                     tag: 'td',
20148                     cn: [
20149                         {
20150                             tag: 'button',
20151                             type: 'button',
20152                             cls: 'btn btn-primary period',
20153                             html: 'AM'
20154                             
20155                         }
20156                     ]
20157                 }
20158             ]
20159         });
20160         
20161         time.createChild({
20162             tag: 'tr',
20163             cn: [
20164                 {
20165                     tag: 'td',
20166                     cn: [
20167                         {
20168                             tag: 'a',
20169                             href: '#',
20170                             cls: 'btn',
20171                             cn: [
20172                                 {
20173                                     tag: 'span',
20174                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20175                                 }
20176                             ]
20177                         }
20178                     ]
20179                 },
20180                 {
20181                     tag: 'td',
20182                     cls: 'separator'
20183                 },
20184                 {
20185                     tag: 'td',
20186                     cn: [
20187                         {
20188                             tag: 'a',
20189                             href: '#',
20190                             cls: 'btn',
20191                             cn: [
20192                                 {
20193                                     tag: 'span',
20194                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20195                                 }
20196                             ]
20197                         }
20198                     ]
20199                 },
20200                 {
20201                     tag: 'td',
20202                     cls: 'separator'
20203                 }
20204             ]
20205         });
20206         
20207     },
20208     
20209     update: function()
20210     {
20211         
20212         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20213         
20214         this.fill();
20215     },
20216     
20217     fill: function() 
20218     {
20219         var hours = this.time.getHours();
20220         var minutes = this.time.getMinutes();
20221         var period = 'AM';
20222         
20223         if(hours > 11){
20224             period = 'PM';
20225         }
20226         
20227         if(hours == 0){
20228             hours = 12;
20229         }
20230         
20231         
20232         if(hours > 12){
20233             hours = hours - 12;
20234         }
20235         
20236         if(hours < 10){
20237             hours = '0' + hours;
20238         }
20239         
20240         if(minutes < 10){
20241             minutes = '0' + minutes;
20242         }
20243         
20244         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20245         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20246         this.pop.select('button', true).first().dom.innerHTML = period;
20247         
20248     },
20249     
20250     place: function()
20251     {   
20252         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20253         
20254         var cls = ['bottom'];
20255         
20256         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20257             cls.pop();
20258             cls.push('top');
20259         }
20260         
20261         cls.push('right');
20262         
20263         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20264             cls.pop();
20265             cls.push('left');
20266         }
20267         
20268         this.picker().addClass(cls.join('-'));
20269         
20270         var _this = this;
20271         
20272         Roo.each(cls, function(c){
20273             if(c == 'bottom'){
20274                 _this.picker().setTop(_this.inputEl().getHeight());
20275                 return;
20276             }
20277             if(c == 'top'){
20278                 _this.picker().setTop(0 - _this.picker().getHeight());
20279                 return;
20280             }
20281             
20282             if(c == 'left'){
20283                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20284                 return;
20285             }
20286             if(c == 'right'){
20287                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20288                 return;
20289             }
20290         });
20291         
20292     },
20293   
20294     onFocus : function()
20295     {
20296         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20297         this.show();
20298     },
20299     
20300     onBlur : function()
20301     {
20302         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20303         this.hide();
20304     },
20305     
20306     show : function()
20307     {
20308         this.picker().show();
20309         this.pop.show();
20310         this.update();
20311         this.place();
20312         
20313         this.fireEvent('show', this, this.date);
20314     },
20315     
20316     hide : function()
20317     {
20318         this.picker().hide();
20319         this.pop.hide();
20320         
20321         this.fireEvent('hide', this, this.date);
20322     },
20323     
20324     setTime : function()
20325     {
20326         this.hide();
20327         this.setValue(this.time.format(this.format));
20328         
20329         this.fireEvent('select', this, this.date);
20330         
20331         
20332     },
20333     
20334     onMousedown: function(e){
20335         e.stopPropagation();
20336         e.preventDefault();
20337     },
20338     
20339     onIncrementHours: function()
20340     {
20341         Roo.log('onIncrementHours');
20342         this.time = this.time.add(Date.HOUR, 1);
20343         this.update();
20344         
20345     },
20346     
20347     onDecrementHours: function()
20348     {
20349         Roo.log('onDecrementHours');
20350         this.time = this.time.add(Date.HOUR, -1);
20351         this.update();
20352     },
20353     
20354     onIncrementMinutes: function()
20355     {
20356         Roo.log('onIncrementMinutes');
20357         this.time = this.time.add(Date.MINUTE, 1);
20358         this.update();
20359     },
20360     
20361     onDecrementMinutes: function()
20362     {
20363         Roo.log('onDecrementMinutes');
20364         this.time = this.time.add(Date.MINUTE, -1);
20365         this.update();
20366     },
20367     
20368     onTogglePeriod: function()
20369     {
20370         Roo.log('onTogglePeriod');
20371         this.time = this.time.add(Date.HOUR, 12);
20372         this.update();
20373     }
20374     
20375    
20376 });
20377
20378 Roo.apply(Roo.bootstrap.TimeField,  {
20379     
20380     content : {
20381         tag: 'tbody',
20382         cn: [
20383             {
20384                 tag: 'tr',
20385                 cn: [
20386                 {
20387                     tag: 'td',
20388                     colspan: '7'
20389                 }
20390                 ]
20391             }
20392         ]
20393     },
20394     
20395     footer : {
20396         tag: 'tfoot',
20397         cn: [
20398             {
20399                 tag: 'tr',
20400                 cn: [
20401                 {
20402                     tag: 'th',
20403                     colspan: '7',
20404                     cls: '',
20405                     cn: [
20406                         {
20407                             tag: 'button',
20408                             cls: 'btn btn-info ok',
20409                             html: 'OK'
20410                         }
20411                     ]
20412                 }
20413
20414                 ]
20415             }
20416         ]
20417     }
20418 });
20419
20420 Roo.apply(Roo.bootstrap.TimeField,  {
20421   
20422     template : {
20423         tag: 'div',
20424         cls: 'datepicker dropdown-menu',
20425         cn: [
20426             {
20427                 tag: 'div',
20428                 cls: 'datepicker-time',
20429                 cn: [
20430                 {
20431                     tag: 'table',
20432                     cls: 'table-condensed',
20433                     cn:[
20434                     Roo.bootstrap.TimeField.content,
20435                     Roo.bootstrap.TimeField.footer
20436                     ]
20437                 }
20438                 ]
20439             }
20440         ]
20441     }
20442 });
20443
20444  
20445
20446  /*
20447  * - LGPL
20448  *
20449  * MonthField
20450  * 
20451  */
20452
20453 /**
20454  * @class Roo.bootstrap.MonthField
20455  * @extends Roo.bootstrap.Input
20456  * Bootstrap MonthField class
20457  * 
20458  * @cfg {String} language default en
20459  * 
20460  * @constructor
20461  * Create a new MonthField
20462  * @param {Object} config The config object
20463  */
20464
20465 Roo.bootstrap.MonthField = function(config){
20466     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20467     
20468     this.addEvents({
20469         /**
20470          * @event show
20471          * Fires when this field show.
20472          * @param {Roo.bootstrap.MonthField} this
20473          * @param {Mixed} date The date value
20474          */
20475         show : true,
20476         /**
20477          * @event show
20478          * Fires when this field hide.
20479          * @param {Roo.bootstrap.MonthField} this
20480          * @param {Mixed} date The date value
20481          */
20482         hide : true,
20483         /**
20484          * @event select
20485          * Fires when select a date.
20486          * @param {Roo.bootstrap.MonthField} this
20487          * @param {String} oldvalue The old value
20488          * @param {String} newvalue The new value
20489          */
20490         select : true
20491     });
20492 };
20493
20494 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20495     
20496     onRender: function(ct, position)
20497     {
20498         
20499         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20500         
20501         this.language = this.language || 'en';
20502         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20503         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20504         
20505         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20506         this.isInline = false;
20507         this.isInput = true;
20508         this.component = this.el.select('.add-on', true).first() || false;
20509         this.component = (this.component && this.component.length === 0) ? false : this.component;
20510         this.hasInput = this.component && this.inputEL().length;
20511         
20512         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20513         
20514         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20515         
20516         this.picker().on('mousedown', this.onMousedown, this);
20517         this.picker().on('click', this.onClick, this);
20518         
20519         this.picker().addClass('datepicker-dropdown');
20520         
20521         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20522             v.setStyle('width', '189px');
20523         });
20524         
20525         this.fillMonths();
20526         
20527         this.update();
20528         
20529         if(this.isInline) {
20530             this.show();
20531         }
20532         
20533     },
20534     
20535     setValue: function(v, suppressEvent)
20536     {   
20537         var o = this.getValue();
20538         
20539         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20540         
20541         this.update();
20542
20543         if(suppressEvent !== true){
20544             this.fireEvent('select', this, o, v);
20545         }
20546         
20547     },
20548     
20549     getValue: function()
20550     {
20551         return this.value;
20552     },
20553     
20554     onClick: function(e) 
20555     {
20556         e.stopPropagation();
20557         e.preventDefault();
20558         
20559         var target = e.getTarget();
20560         
20561         if(target.nodeName.toLowerCase() === 'i'){
20562             target = Roo.get(target).dom.parentNode;
20563         }
20564         
20565         var nodeName = target.nodeName;
20566         var className = target.className;
20567         var html = target.innerHTML;
20568         
20569         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20570             return;
20571         }
20572         
20573         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20574         
20575         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20576         
20577         this.hide();
20578                         
20579     },
20580     
20581     picker : function()
20582     {
20583         return this.pickerEl;
20584     },
20585     
20586     fillMonths: function()
20587     {    
20588         var i = 0;
20589         var months = this.picker().select('>.datepicker-months td', true).first();
20590         
20591         months.dom.innerHTML = '';
20592         
20593         while (i < 12) {
20594             var month = {
20595                 tag: 'span',
20596                 cls: 'month',
20597                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20598             };
20599             
20600             months.createChild(month);
20601         }
20602         
20603     },
20604     
20605     update: function()
20606     {
20607         var _this = this;
20608         
20609         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20610             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20611         }
20612         
20613         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20614             e.removeClass('active');
20615             
20616             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20617                 e.addClass('active');
20618             }
20619         })
20620     },
20621     
20622     place: function()
20623     {
20624         if(this.isInline) {
20625             return;
20626         }
20627         
20628         this.picker().removeClass(['bottom', 'top']);
20629         
20630         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20631             /*
20632              * place to the top of element!
20633              *
20634              */
20635             
20636             this.picker().addClass('top');
20637             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20638             
20639             return;
20640         }
20641         
20642         this.picker().addClass('bottom');
20643         
20644         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20645     },
20646     
20647     onFocus : function()
20648     {
20649         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20650         this.show();
20651     },
20652     
20653     onBlur : function()
20654     {
20655         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20656         
20657         var d = this.inputEl().getValue();
20658         
20659         this.setValue(d);
20660                 
20661         this.hide();
20662     },
20663     
20664     show : function()
20665     {
20666         this.picker().show();
20667         this.picker().select('>.datepicker-months', true).first().show();
20668         this.update();
20669         this.place();
20670         
20671         this.fireEvent('show', this, this.date);
20672     },
20673     
20674     hide : function()
20675     {
20676         if(this.isInline) {
20677             return;
20678         }
20679         this.picker().hide();
20680         this.fireEvent('hide', this, this.date);
20681         
20682     },
20683     
20684     onMousedown: function(e)
20685     {
20686         e.stopPropagation();
20687         e.preventDefault();
20688     },
20689     
20690     keyup: function(e)
20691     {
20692         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20693         this.update();
20694     },
20695
20696     fireKey: function(e)
20697     {
20698         if (!this.picker().isVisible()){
20699             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20700                 this.show();
20701             }
20702             return;
20703         }
20704         
20705         var dir;
20706         
20707         switch(e.keyCode){
20708             case 27: // escape
20709                 this.hide();
20710                 e.preventDefault();
20711                 break;
20712             case 37: // left
20713             case 39: // right
20714                 dir = e.keyCode == 37 ? -1 : 1;
20715                 
20716                 this.vIndex = this.vIndex + dir;
20717                 
20718                 if(this.vIndex < 0){
20719                     this.vIndex = 0;
20720                 }
20721                 
20722                 if(this.vIndex > 11){
20723                     this.vIndex = 11;
20724                 }
20725                 
20726                 if(isNaN(this.vIndex)){
20727                     this.vIndex = 0;
20728                 }
20729                 
20730                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20731                 
20732                 break;
20733             case 38: // up
20734             case 40: // down
20735                 
20736                 dir = e.keyCode == 38 ? -1 : 1;
20737                 
20738                 this.vIndex = this.vIndex + dir * 4;
20739                 
20740                 if(this.vIndex < 0){
20741                     this.vIndex = 0;
20742                 }
20743                 
20744                 if(this.vIndex > 11){
20745                     this.vIndex = 11;
20746                 }
20747                 
20748                 if(isNaN(this.vIndex)){
20749                     this.vIndex = 0;
20750                 }
20751                 
20752                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20753                 break;
20754                 
20755             case 13: // enter
20756                 
20757                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20758                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20759                 }
20760                 
20761                 this.hide();
20762                 e.preventDefault();
20763                 break;
20764             case 9: // tab
20765                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20766                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20767                 }
20768                 this.hide();
20769                 break;
20770             case 16: // shift
20771             case 17: // ctrl
20772             case 18: // alt
20773                 break;
20774             default :
20775                 this.hide();
20776                 
20777         }
20778     },
20779     
20780     remove: function() 
20781     {
20782         this.picker().remove();
20783     }
20784    
20785 });
20786
20787 Roo.apply(Roo.bootstrap.MonthField,  {
20788     
20789     content : {
20790         tag: 'tbody',
20791         cn: [
20792         {
20793             tag: 'tr',
20794             cn: [
20795             {
20796                 tag: 'td',
20797                 colspan: '7'
20798             }
20799             ]
20800         }
20801         ]
20802     },
20803     
20804     dates:{
20805         en: {
20806             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20807             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20808         }
20809     }
20810 });
20811
20812 Roo.apply(Roo.bootstrap.MonthField,  {
20813   
20814     template : {
20815         tag: 'div',
20816         cls: 'datepicker dropdown-menu roo-dynamic',
20817         cn: [
20818             {
20819                 tag: 'div',
20820                 cls: 'datepicker-months',
20821                 cn: [
20822                 {
20823                     tag: 'table',
20824                     cls: 'table-condensed',
20825                     cn:[
20826                         Roo.bootstrap.DateField.content
20827                     ]
20828                 }
20829                 ]
20830             }
20831         ]
20832     }
20833 });
20834
20835  
20836
20837  
20838  /*
20839  * - LGPL
20840  *
20841  * CheckBox
20842  * 
20843  */
20844
20845 /**
20846  * @class Roo.bootstrap.CheckBox
20847  * @extends Roo.bootstrap.Input
20848  * Bootstrap CheckBox class
20849  * 
20850  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20851  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20852  * @cfg {String} boxLabel The text that appears beside the checkbox
20853  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20854  * @cfg {Boolean} checked initnal the element
20855  * @cfg {Boolean} inline inline the element (default false)
20856  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20857  * @cfg {String} tooltip label tooltip
20858  * 
20859  * @constructor
20860  * Create a new CheckBox
20861  * @param {Object} config The config object
20862  */
20863
20864 Roo.bootstrap.CheckBox = function(config){
20865     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20866    
20867     this.addEvents({
20868         /**
20869         * @event check
20870         * Fires when the element is checked or unchecked.
20871         * @param {Roo.bootstrap.CheckBox} this This input
20872         * @param {Boolean} checked The new checked value
20873         */
20874        check : true,
20875        /**
20876         * @event click
20877         * Fires when the element is click.
20878         * @param {Roo.bootstrap.CheckBox} this This input
20879         */
20880        click : true
20881     });
20882     
20883 };
20884
20885 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20886   
20887     inputType: 'checkbox',
20888     inputValue: 1,
20889     valueOff: 0,
20890     boxLabel: false,
20891     checked: false,
20892     weight : false,
20893     inline: false,
20894     tooltip : '',
20895     
20896     getAutoCreate : function()
20897     {
20898         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20899         
20900         var id = Roo.id();
20901         
20902         var cfg = {};
20903         
20904         cfg.cls = 'form-group ' + this.inputType; //input-group
20905         
20906         if(this.inline){
20907             cfg.cls += ' ' + this.inputType + '-inline';
20908         }
20909         
20910         var input =  {
20911             tag: 'input',
20912             id : id,
20913             type : this.inputType,
20914             value : this.inputValue,
20915             cls : 'roo-' + this.inputType, //'form-box',
20916             placeholder : this.placeholder || ''
20917             
20918         };
20919         
20920         if(this.inputType != 'radio'){
20921             var hidden =  {
20922                 tag: 'input',
20923                 type : 'hidden',
20924                 cls : 'roo-hidden-value',
20925                 value : this.checked ? this.inputValue : this.valueOff
20926             };
20927         }
20928         
20929             
20930         if (this.weight) { // Validity check?
20931             cfg.cls += " " + this.inputType + "-" + this.weight;
20932         }
20933         
20934         if (this.disabled) {
20935             input.disabled=true;
20936         }
20937         
20938         if(this.checked){
20939             input.checked = this.checked;
20940         }
20941         
20942         if (this.name) {
20943             
20944             input.name = this.name;
20945             
20946             if(this.inputType != 'radio'){
20947                 hidden.name = this.name;
20948                 input.name = '_hidden_' + this.name;
20949             }
20950         }
20951         
20952         if (this.size) {
20953             input.cls += ' input-' + this.size;
20954         }
20955         
20956         var settings=this;
20957         
20958         ['xs','sm','md','lg'].map(function(size){
20959             if (settings[size]) {
20960                 cfg.cls += ' col-' + size + '-' + settings[size];
20961             }
20962         });
20963         
20964         var inputblock = input;
20965          
20966         if (this.before || this.after) {
20967             
20968             inputblock = {
20969                 cls : 'input-group',
20970                 cn :  [] 
20971             };
20972             
20973             if (this.before) {
20974                 inputblock.cn.push({
20975                     tag :'span',
20976                     cls : 'input-group-addon',
20977                     html : this.before
20978                 });
20979             }
20980             
20981             inputblock.cn.push(input);
20982             
20983             if(this.inputType != 'radio'){
20984                 inputblock.cn.push(hidden);
20985             }
20986             
20987             if (this.after) {
20988                 inputblock.cn.push({
20989                     tag :'span',
20990                     cls : 'input-group-addon',
20991                     html : this.after
20992                 });
20993             }
20994             
20995         }
20996         
20997         if (align ==='left' && this.fieldLabel.length) {
20998 //                Roo.log("left and has label");
20999             cfg.cn = [
21000                 {
21001                     tag: 'label',
21002                     'for' :  id,
21003                     cls : 'control-label',
21004                     html : this.fieldLabel
21005                 },
21006                 {
21007                     cls : "", 
21008                     cn: [
21009                         inputblock
21010                     ]
21011                 }
21012             ];
21013             
21014             if(this.labelWidth > 12){
21015                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21016             }
21017             
21018             if(this.labelWidth < 13 && this.labelmd == 0){
21019                 this.labelmd = this.labelWidth;
21020             }
21021             
21022             if(this.labellg > 0){
21023                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21024                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21025             }
21026             
21027             if(this.labelmd > 0){
21028                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21029                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21030             }
21031             
21032             if(this.labelsm > 0){
21033                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21034                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21035             }
21036             
21037             if(this.labelxs > 0){
21038                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21039                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21040             }
21041             
21042         } else if ( this.fieldLabel.length) {
21043 //                Roo.log(" label");
21044                 cfg.cn = [
21045                    
21046                     {
21047                         tag: this.boxLabel ? 'span' : 'label',
21048                         'for': id,
21049                         cls: 'control-label box-input-label',
21050                         //cls : 'input-group-addon',
21051                         html : this.fieldLabel
21052                     },
21053                     
21054                     inputblock
21055                     
21056                 ];
21057
21058         } else {
21059             
21060 //                Roo.log(" no label && no align");
21061                 cfg.cn = [  inputblock ] ;
21062                 
21063                 
21064         }
21065         
21066         if(this.boxLabel){
21067              var boxLabelCfg = {
21068                 tag: 'label',
21069                 //'for': id, // box label is handled by onclick - so no for...
21070                 cls: 'box-label',
21071                 html: this.boxLabel
21072             };
21073             
21074             if(this.tooltip){
21075                 boxLabelCfg.tooltip = this.tooltip;
21076             }
21077              
21078             cfg.cn.push(boxLabelCfg);
21079         }
21080         
21081         if(this.inputType != 'radio'){
21082             cfg.cn.push(hidden);
21083         }
21084         
21085         return cfg;
21086         
21087     },
21088     
21089     /**
21090      * return the real input element.
21091      */
21092     inputEl: function ()
21093     {
21094         return this.el.select('input.roo-' + this.inputType,true).first();
21095     },
21096     hiddenEl: function ()
21097     {
21098         return this.el.select('input.roo-hidden-value',true).first();
21099     },
21100     
21101     labelEl: function()
21102     {
21103         return this.el.select('label.control-label',true).first();
21104     },
21105     /* depricated... */
21106     
21107     label: function()
21108     {
21109         return this.labelEl();
21110     },
21111     
21112     boxLabelEl: function()
21113     {
21114         return this.el.select('label.box-label',true).first();
21115     },
21116     
21117     initEvents : function()
21118     {
21119 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21120         
21121         this.inputEl().on('click', this.onClick,  this);
21122         
21123         if (this.boxLabel) { 
21124             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21125         }
21126         
21127         this.startValue = this.getValue();
21128         
21129         if(this.groupId){
21130             Roo.bootstrap.CheckBox.register(this);
21131         }
21132     },
21133     
21134     onClick : function(e)
21135     {   
21136         if(this.fireEvent('click', this, e) !== false){
21137             this.setChecked(!this.checked);
21138         }
21139         
21140     },
21141     
21142     setChecked : function(state,suppressEvent)
21143     {
21144         this.startValue = this.getValue();
21145
21146         if(this.inputType == 'radio'){
21147             
21148             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21149                 e.dom.checked = false;
21150             });
21151             
21152             this.inputEl().dom.checked = true;
21153             
21154             this.inputEl().dom.value = this.inputValue;
21155             
21156             if(suppressEvent !== true){
21157                 this.fireEvent('check', this, true);
21158             }
21159             
21160             this.validate();
21161             
21162             return;
21163         }
21164         
21165         this.checked = state;
21166         
21167         this.inputEl().dom.checked = state;
21168         
21169         
21170         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21171         
21172         if(suppressEvent !== true){
21173             this.fireEvent('check', this, state);
21174         }
21175         
21176         this.validate();
21177     },
21178     
21179     getValue : function()
21180     {
21181         if(this.inputType == 'radio'){
21182             return this.getGroupValue();
21183         }
21184         
21185         return this.hiddenEl().dom.value;
21186         
21187     },
21188     
21189     getGroupValue : function()
21190     {
21191         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21192             return '';
21193         }
21194         
21195         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21196     },
21197     
21198     setValue : function(v,suppressEvent)
21199     {
21200         if(this.inputType == 'radio'){
21201             this.setGroupValue(v, suppressEvent);
21202             return;
21203         }
21204         
21205         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21206         
21207         this.validate();
21208     },
21209     
21210     setGroupValue : function(v, suppressEvent)
21211     {
21212         this.startValue = this.getValue();
21213         
21214         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21215             e.dom.checked = false;
21216             
21217             if(e.dom.value == v){
21218                 e.dom.checked = true;
21219             }
21220         });
21221         
21222         if(suppressEvent !== true){
21223             this.fireEvent('check', this, true);
21224         }
21225
21226         this.validate();
21227         
21228         return;
21229     },
21230     
21231     validate : function()
21232     {
21233         if(this.getVisibilityEl().hasClass('hidden')){
21234             return true;
21235         }
21236         
21237         if(
21238                 this.disabled || 
21239                 (this.inputType == 'radio' && this.validateRadio()) ||
21240                 (this.inputType == 'checkbox' && this.validateCheckbox())
21241         ){
21242             this.markValid();
21243             return true;
21244         }
21245         
21246         this.markInvalid();
21247         return false;
21248     },
21249     
21250     validateRadio : function()
21251     {
21252         if(this.getVisibilityEl().hasClass('hidden')){
21253             return true;
21254         }
21255         
21256         if(this.allowBlank){
21257             return true;
21258         }
21259         
21260         var valid = false;
21261         
21262         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21263             if(!e.dom.checked){
21264                 return;
21265             }
21266             
21267             valid = true;
21268             
21269             return false;
21270         });
21271         
21272         return valid;
21273     },
21274     
21275     validateCheckbox : function()
21276     {
21277         if(!this.groupId){
21278             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21279             //return (this.getValue() == this.inputValue) ? true : false;
21280         }
21281         
21282         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21283         
21284         if(!group){
21285             return false;
21286         }
21287         
21288         var r = false;
21289         
21290         for(var i in group){
21291             if(group[i].el.isVisible(true)){
21292                 r = false;
21293                 break;
21294             }
21295             
21296             r = true;
21297         }
21298         
21299         for(var i in group){
21300             if(r){
21301                 break;
21302             }
21303             
21304             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21305         }
21306         
21307         return r;
21308     },
21309     
21310     /**
21311      * Mark this field as valid
21312      */
21313     markValid : function()
21314     {
21315         var _this = this;
21316         
21317         this.fireEvent('valid', this);
21318         
21319         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21320         
21321         if(this.groupId){
21322             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21323         }
21324         
21325         if(label){
21326             label.markValid();
21327         }
21328
21329         if(this.inputType == 'radio'){
21330             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21331                 var fg = e.findParent('.form-group', false, true);
21332                 if (Roo.bootstrap.version == 3) {
21333                     fg.removeClass([_this.invalidClass, _this.validClass]);
21334                     fg.addClass(_this.validClass);
21335                 } else {
21336                     fg.removeClass(['is-valid', 'is-invalid']);
21337                     fg.addClass('is-valid');
21338                 }
21339             });
21340             
21341             return;
21342         }
21343
21344         if(!this.groupId){
21345             var fg = this.el.findParent('.form-group', false, true);
21346             if (Roo.bootstrap.version == 3) {
21347                 fg.removeClass([this.invalidClass, this.validClass]);
21348                 fg.addClass(this.validClass);
21349             } else {
21350                 fg.removeClass(['is-valid', 'is-invalid']);
21351                 fg.addClass('is-valid');
21352             }
21353             return;
21354         }
21355         
21356         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21357         
21358         if(!group){
21359             return;
21360         }
21361         
21362         for(var i in group){
21363             var fg = group[i].el.findParent('.form-group', false, true);
21364             if (Roo.bootstrap.version == 3) {
21365                 fg.removeClass([this.invalidClass, this.validClass]);
21366                 fg.addClass(this.validClass);
21367             } else {
21368                 fg.removeClass(['is-valid', 'is-invalid']);
21369                 fg.addClass('is-valid');
21370             }
21371         }
21372     },
21373     
21374      /**
21375      * Mark this field as invalid
21376      * @param {String} msg The validation message
21377      */
21378     markInvalid : function(msg)
21379     {
21380         if(this.allowBlank){
21381             return;
21382         }
21383         
21384         var _this = this;
21385         
21386         this.fireEvent('invalid', this, msg);
21387         
21388         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21389         
21390         if(this.groupId){
21391             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21392         }
21393         
21394         if(label){
21395             label.markInvalid();
21396         }
21397             
21398         if(this.inputType == 'radio'){
21399             
21400             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21401                 var fg = e.findParent('.form-group', false, true);
21402                 if (Roo.bootstrap.version == 3) {
21403                     fg.removeClass([_this.invalidClass, _this.validClass]);
21404                     fg.addClass(_this.invalidClass);
21405                 } else {
21406                     fg.removeClass(['is-invalid', 'is-valid']);
21407                     fg.addClass('is-invalid');
21408                 }
21409             });
21410             
21411             return;
21412         }
21413         
21414         if(!this.groupId){
21415             var fg = this.el.findParent('.form-group', false, true);
21416             if (Roo.bootstrap.version == 3) {
21417                 fg.removeClass([_this.invalidClass, _this.validClass]);
21418                 fg.addClass(_this.invalidClass);
21419             } else {
21420                 fg.removeClass(['is-invalid', 'is-valid']);
21421                 fg.addClass('is-invalid');
21422             }
21423             return;
21424         }
21425         
21426         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21427         
21428         if(!group){
21429             return;
21430         }
21431         
21432         for(var i in group){
21433             var fg = group[i].el.findParent('.form-group', false, true);
21434             if (Roo.bootstrap.version == 3) {
21435                 fg.removeClass([_this.invalidClass, _this.validClass]);
21436                 fg.addClass(_this.invalidClass);
21437             } else {
21438                 fg.removeClass(['is-invalid', 'is-valid']);
21439                 fg.addClass('is-invalid');
21440             }
21441         }
21442         
21443     },
21444     
21445     clearInvalid : function()
21446     {
21447         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21448         
21449         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21450         
21451         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21452         
21453         if (label && label.iconEl) {
21454             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21455             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21456         }
21457     },
21458     
21459     disable : function()
21460     {
21461         if(this.inputType != 'radio'){
21462             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21463             return;
21464         }
21465         
21466         var _this = this;
21467         
21468         if(this.rendered){
21469             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21470                 _this.getActionEl().addClass(this.disabledClass);
21471                 e.dom.disabled = true;
21472             });
21473         }
21474         
21475         this.disabled = true;
21476         this.fireEvent("disable", this);
21477         return this;
21478     },
21479
21480     enable : function()
21481     {
21482         if(this.inputType != 'radio'){
21483             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21484             return;
21485         }
21486         
21487         var _this = this;
21488         
21489         if(this.rendered){
21490             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21491                 _this.getActionEl().removeClass(this.disabledClass);
21492                 e.dom.disabled = false;
21493             });
21494         }
21495         
21496         this.disabled = false;
21497         this.fireEvent("enable", this);
21498         return this;
21499     },
21500     
21501     setBoxLabel : function(v)
21502     {
21503         this.boxLabel = v;
21504         
21505         if(this.rendered){
21506             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21507         }
21508     }
21509
21510 });
21511
21512 Roo.apply(Roo.bootstrap.CheckBox, {
21513     
21514     groups: {},
21515     
21516      /**
21517     * register a CheckBox Group
21518     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21519     */
21520     register : function(checkbox)
21521     {
21522         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21523             this.groups[checkbox.groupId] = {};
21524         }
21525         
21526         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21527             return;
21528         }
21529         
21530         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21531         
21532     },
21533     /**
21534     * fetch a CheckBox Group based on the group ID
21535     * @param {string} the group ID
21536     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21537     */
21538     get: function(groupId) {
21539         if (typeof(this.groups[groupId]) == 'undefined') {
21540             return false;
21541         }
21542         
21543         return this.groups[groupId] ;
21544     }
21545     
21546     
21547 });
21548 /*
21549  * - LGPL
21550  *
21551  * RadioItem
21552  * 
21553  */
21554
21555 /**
21556  * @class Roo.bootstrap.Radio
21557  * @extends Roo.bootstrap.Component
21558  * Bootstrap Radio class
21559  * @cfg {String} boxLabel - the label associated
21560  * @cfg {String} value - the value of radio
21561  * 
21562  * @constructor
21563  * Create a new Radio
21564  * @param {Object} config The config object
21565  */
21566 Roo.bootstrap.Radio = function(config){
21567     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21568     
21569 };
21570
21571 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21572     
21573     boxLabel : '',
21574     
21575     value : '',
21576     
21577     getAutoCreate : function()
21578     {
21579         var cfg = {
21580             tag : 'div',
21581             cls : 'form-group radio',
21582             cn : [
21583                 {
21584                     tag : 'label',
21585                     cls : 'box-label',
21586                     html : this.boxLabel
21587                 }
21588             ]
21589         };
21590         
21591         return cfg;
21592     },
21593     
21594     initEvents : function() 
21595     {
21596         this.parent().register(this);
21597         
21598         this.el.on('click', this.onClick, this);
21599         
21600     },
21601     
21602     onClick : function(e)
21603     {
21604         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21605             this.setChecked(true);
21606         }
21607     },
21608     
21609     setChecked : function(state, suppressEvent)
21610     {
21611         this.parent().setValue(this.value, suppressEvent);
21612         
21613     },
21614     
21615     setBoxLabel : function(v)
21616     {
21617         this.boxLabel = v;
21618         
21619         if(this.rendered){
21620             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21621         }
21622     }
21623     
21624 });
21625  
21626
21627  /*
21628  * - LGPL
21629  *
21630  * Input
21631  * 
21632  */
21633
21634 /**
21635  * @class Roo.bootstrap.SecurePass
21636  * @extends Roo.bootstrap.Input
21637  * Bootstrap SecurePass class
21638  *
21639  * 
21640  * @constructor
21641  * Create a new SecurePass
21642  * @param {Object} config The config object
21643  */
21644  
21645 Roo.bootstrap.SecurePass = function (config) {
21646     // these go here, so the translation tool can replace them..
21647     this.errors = {
21648         PwdEmpty: "Please type a password, and then retype it to confirm.",
21649         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21650         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21651         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21652         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21653         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21654         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21655         TooWeak: "Your password is Too Weak."
21656     },
21657     this.meterLabel = "Password strength:";
21658     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21659     this.meterClass = [
21660         "roo-password-meter-tooweak", 
21661         "roo-password-meter-weak", 
21662         "roo-password-meter-medium", 
21663         "roo-password-meter-strong", 
21664         "roo-password-meter-grey"
21665     ];
21666     
21667     this.errors = {};
21668     
21669     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21670 }
21671
21672 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21673     /**
21674      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21675      * {
21676      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21677      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21678      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21679      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21680      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21681      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21682      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21683      * })
21684      */
21685     // private
21686     
21687     meterWidth: 300,
21688     errorMsg :'',    
21689     errors: false,
21690     imageRoot: '/',
21691     /**
21692      * @cfg {String/Object} Label for the strength meter (defaults to
21693      * 'Password strength:')
21694      */
21695     // private
21696     meterLabel: '',
21697     /**
21698      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21699      * ['Weak', 'Medium', 'Strong'])
21700      */
21701     // private    
21702     pwdStrengths: false,    
21703     // private
21704     strength: 0,
21705     // private
21706     _lastPwd: null,
21707     // private
21708     kCapitalLetter: 0,
21709     kSmallLetter: 1,
21710     kDigit: 2,
21711     kPunctuation: 3,
21712     
21713     insecure: false,
21714     // private
21715     initEvents: function ()
21716     {
21717         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21718
21719         if (this.el.is('input[type=password]') && Roo.isSafari) {
21720             this.el.on('keydown', this.SafariOnKeyDown, this);
21721         }
21722
21723         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21724     },
21725     // private
21726     onRender: function (ct, position)
21727     {
21728         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21729         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21730         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21731
21732         this.trigger.createChild({
21733                    cn: [
21734                     {
21735                     //id: 'PwdMeter',
21736                     tag: 'div',
21737                     cls: 'roo-password-meter-grey col-xs-12',
21738                     style: {
21739                         //width: 0,
21740                         //width: this.meterWidth + 'px'                                                
21741                         }
21742                     },
21743                     {                            
21744                          cls: 'roo-password-meter-text'                          
21745                     }
21746                 ]            
21747         });
21748
21749          
21750         if (this.hideTrigger) {
21751             this.trigger.setDisplayed(false);
21752         }
21753         this.setSize(this.width || '', this.height || '');
21754     },
21755     // private
21756     onDestroy: function ()
21757     {
21758         if (this.trigger) {
21759             this.trigger.removeAllListeners();
21760             this.trigger.remove();
21761         }
21762         if (this.wrap) {
21763             this.wrap.remove();
21764         }
21765         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21766     },
21767     // private
21768     checkStrength: function ()
21769     {
21770         var pwd = this.inputEl().getValue();
21771         if (pwd == this._lastPwd) {
21772             return;
21773         }
21774
21775         var strength;
21776         if (this.ClientSideStrongPassword(pwd)) {
21777             strength = 3;
21778         } else if (this.ClientSideMediumPassword(pwd)) {
21779             strength = 2;
21780         } else if (this.ClientSideWeakPassword(pwd)) {
21781             strength = 1;
21782         } else {
21783             strength = 0;
21784         }
21785         
21786         Roo.log('strength1: ' + strength);
21787         
21788         //var pm = this.trigger.child('div/div/div').dom;
21789         var pm = this.trigger.child('div/div');
21790         pm.removeClass(this.meterClass);
21791         pm.addClass(this.meterClass[strength]);
21792                 
21793         
21794         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21795                 
21796         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21797         
21798         this._lastPwd = pwd;
21799     },
21800     reset: function ()
21801     {
21802         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21803         
21804         this._lastPwd = '';
21805         
21806         var pm = this.trigger.child('div/div');
21807         pm.removeClass(this.meterClass);
21808         pm.addClass('roo-password-meter-grey');        
21809         
21810         
21811         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21812         
21813         pt.innerHTML = '';
21814         this.inputEl().dom.type='password';
21815     },
21816     // private
21817     validateValue: function (value)
21818     {
21819         
21820         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21821             return false;
21822         }
21823         if (value.length == 0) {
21824             if (this.allowBlank) {
21825                 this.clearInvalid();
21826                 return true;
21827             }
21828
21829             this.markInvalid(this.errors.PwdEmpty);
21830             this.errorMsg = this.errors.PwdEmpty;
21831             return false;
21832         }
21833         
21834         if(this.insecure){
21835             return true;
21836         }
21837         
21838         if ('[\x21-\x7e]*'.match(value)) {
21839             this.markInvalid(this.errors.PwdBadChar);
21840             this.errorMsg = this.errors.PwdBadChar;
21841             return false;
21842         }
21843         if (value.length < 6) {
21844             this.markInvalid(this.errors.PwdShort);
21845             this.errorMsg = this.errors.PwdShort;
21846             return false;
21847         }
21848         if (value.length > 16) {
21849             this.markInvalid(this.errors.PwdLong);
21850             this.errorMsg = this.errors.PwdLong;
21851             return false;
21852         }
21853         var strength;
21854         if (this.ClientSideStrongPassword(value)) {
21855             strength = 3;
21856         } else if (this.ClientSideMediumPassword(value)) {
21857             strength = 2;
21858         } else if (this.ClientSideWeakPassword(value)) {
21859             strength = 1;
21860         } else {
21861             strength = 0;
21862         }
21863
21864         
21865         if (strength < 2) {
21866             //this.markInvalid(this.errors.TooWeak);
21867             this.errorMsg = this.errors.TooWeak;
21868             //return false;
21869         }
21870         
21871         
21872         console.log('strength2: ' + strength);
21873         
21874         //var pm = this.trigger.child('div/div/div').dom;
21875         
21876         var pm = this.trigger.child('div/div');
21877         pm.removeClass(this.meterClass);
21878         pm.addClass(this.meterClass[strength]);
21879                 
21880         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21881                 
21882         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21883         
21884         this.errorMsg = ''; 
21885         return true;
21886     },
21887     // private
21888     CharacterSetChecks: function (type)
21889     {
21890         this.type = type;
21891         this.fResult = false;
21892     },
21893     // private
21894     isctype: function (character, type)
21895     {
21896         switch (type) {  
21897             case this.kCapitalLetter:
21898                 if (character >= 'A' && character <= 'Z') {
21899                     return true;
21900                 }
21901                 break;
21902             
21903             case this.kSmallLetter:
21904                 if (character >= 'a' && character <= 'z') {
21905                     return true;
21906                 }
21907                 break;
21908             
21909             case this.kDigit:
21910                 if (character >= '0' && character <= '9') {
21911                     return true;
21912                 }
21913                 break;
21914             
21915             case this.kPunctuation:
21916                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21917                     return true;
21918                 }
21919                 break;
21920             
21921             default:
21922                 return false;
21923         }
21924
21925     },
21926     // private
21927     IsLongEnough: function (pwd, size)
21928     {
21929         return !(pwd == null || isNaN(size) || pwd.length < size);
21930     },
21931     // private
21932     SpansEnoughCharacterSets: function (word, nb)
21933     {
21934         if (!this.IsLongEnough(word, nb))
21935         {
21936             return false;
21937         }
21938
21939         var characterSetChecks = new Array(
21940             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21941             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21942         );
21943         
21944         for (var index = 0; index < word.length; ++index) {
21945             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21946                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21947                     characterSetChecks[nCharSet].fResult = true;
21948                     break;
21949                 }
21950             }
21951         }
21952
21953         var nCharSets = 0;
21954         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21955             if (characterSetChecks[nCharSet].fResult) {
21956                 ++nCharSets;
21957             }
21958         }
21959
21960         if (nCharSets < nb) {
21961             return false;
21962         }
21963         return true;
21964     },
21965     // private
21966     ClientSideStrongPassword: function (pwd)
21967     {
21968         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21969     },
21970     // private
21971     ClientSideMediumPassword: function (pwd)
21972     {
21973         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21974     },
21975     // private
21976     ClientSideWeakPassword: function (pwd)
21977     {
21978         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21979     }
21980           
21981 })//<script type="text/javascript">
21982
21983 /*
21984  * Based  Ext JS Library 1.1.1
21985  * Copyright(c) 2006-2007, Ext JS, LLC.
21986  * LGPL
21987  *
21988  */
21989  
21990 /**
21991  * @class Roo.HtmlEditorCore
21992  * @extends Roo.Component
21993  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21994  *
21995  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21996  */
21997
21998 Roo.HtmlEditorCore = function(config){
21999     
22000     
22001     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22002     
22003     
22004     this.addEvents({
22005         /**
22006          * @event initialize
22007          * Fires when the editor is fully initialized (including the iframe)
22008          * @param {Roo.HtmlEditorCore} this
22009          */
22010         initialize: true,
22011         /**
22012          * @event activate
22013          * Fires when the editor is first receives the focus. Any insertion must wait
22014          * until after this event.
22015          * @param {Roo.HtmlEditorCore} this
22016          */
22017         activate: true,
22018          /**
22019          * @event beforesync
22020          * Fires before the textarea is updated with content from the editor iframe. Return false
22021          * to cancel the sync.
22022          * @param {Roo.HtmlEditorCore} this
22023          * @param {String} html
22024          */
22025         beforesync: true,
22026          /**
22027          * @event beforepush
22028          * Fires before the iframe editor is updated with content from the textarea. Return false
22029          * to cancel the push.
22030          * @param {Roo.HtmlEditorCore} this
22031          * @param {String} html
22032          */
22033         beforepush: true,
22034          /**
22035          * @event sync
22036          * Fires when the textarea is updated with content from the editor iframe.
22037          * @param {Roo.HtmlEditorCore} this
22038          * @param {String} html
22039          */
22040         sync: true,
22041          /**
22042          * @event push
22043          * Fires when the iframe editor is updated with content from the textarea.
22044          * @param {Roo.HtmlEditorCore} this
22045          * @param {String} html
22046          */
22047         push: true,
22048         
22049         /**
22050          * @event editorevent
22051          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22052          * @param {Roo.HtmlEditorCore} this
22053          */
22054         editorevent: true
22055         
22056     });
22057     
22058     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22059     
22060     // defaults : white / black...
22061     this.applyBlacklists();
22062     
22063     
22064     
22065 };
22066
22067
22068 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22069
22070
22071      /**
22072      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22073      */
22074     
22075     owner : false,
22076     
22077      /**
22078      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22079      *                        Roo.resizable.
22080      */
22081     resizable : false,
22082      /**
22083      * @cfg {Number} height (in pixels)
22084      */   
22085     height: 300,
22086    /**
22087      * @cfg {Number} width (in pixels)
22088      */   
22089     width: 500,
22090     
22091     /**
22092      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22093      * 
22094      */
22095     stylesheets: false,
22096     
22097     // id of frame..
22098     frameId: false,
22099     
22100     // private properties
22101     validationEvent : false,
22102     deferHeight: true,
22103     initialized : false,
22104     activated : false,
22105     sourceEditMode : false,
22106     onFocus : Roo.emptyFn,
22107     iframePad:3,
22108     hideMode:'offsets',
22109     
22110     clearUp: true,
22111     
22112     // blacklist + whitelisted elements..
22113     black: false,
22114     white: false,
22115      
22116     bodyCls : '',
22117
22118     /**
22119      * Protected method that will not generally be called directly. It
22120      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22121      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22122      */
22123     getDocMarkup : function(){
22124         // body styles..
22125         var st = '';
22126         
22127         // inherit styels from page...?? 
22128         if (this.stylesheets === false) {
22129             
22130             Roo.get(document.head).select('style').each(function(node) {
22131                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22132             });
22133             
22134             Roo.get(document.head).select('link').each(function(node) { 
22135                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22136             });
22137             
22138         } else if (!this.stylesheets.length) {
22139                 // simple..
22140                 st = '<style type="text/css">' +
22141                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22142                    '</style>';
22143         } else { 
22144             st = '<style type="text/css">' +
22145                     this.stylesheets +
22146                 '</style>';
22147         }
22148         
22149         st +=  '<style type="text/css">' +
22150             'IMG { cursor: pointer } ' +
22151         '</style>';
22152
22153         var cls = 'roo-htmleditor-body';
22154         
22155         if(this.bodyCls.length){
22156             cls += ' ' + this.bodyCls;
22157         }
22158         
22159         return '<html><head>' + st  +
22160             //<style type="text/css">' +
22161             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22162             //'</style>' +
22163             ' </head><body class="' +  cls + '"></body></html>';
22164     },
22165
22166     // private
22167     onRender : function(ct, position)
22168     {
22169         var _t = this;
22170         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22171         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22172         
22173         
22174         this.el.dom.style.border = '0 none';
22175         this.el.dom.setAttribute('tabIndex', -1);
22176         this.el.addClass('x-hidden hide');
22177         
22178         
22179         
22180         if(Roo.isIE){ // fix IE 1px bogus margin
22181             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22182         }
22183        
22184         
22185         this.frameId = Roo.id();
22186         
22187          
22188         
22189         var iframe = this.owner.wrap.createChild({
22190             tag: 'iframe',
22191             cls: 'form-control', // bootstrap..
22192             id: this.frameId,
22193             name: this.frameId,
22194             frameBorder : 'no',
22195             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22196         }, this.el
22197         );
22198         
22199         
22200         this.iframe = iframe.dom;
22201
22202          this.assignDocWin();
22203         
22204         this.doc.designMode = 'on';
22205        
22206         this.doc.open();
22207         this.doc.write(this.getDocMarkup());
22208         this.doc.close();
22209
22210         
22211         var task = { // must defer to wait for browser to be ready
22212             run : function(){
22213                 //console.log("run task?" + this.doc.readyState);
22214                 this.assignDocWin();
22215                 if(this.doc.body || this.doc.readyState == 'complete'){
22216                     try {
22217                         this.doc.designMode="on";
22218                     } catch (e) {
22219                         return;
22220                     }
22221                     Roo.TaskMgr.stop(task);
22222                     this.initEditor.defer(10, this);
22223                 }
22224             },
22225             interval : 10,
22226             duration: 10000,
22227             scope: this
22228         };
22229         Roo.TaskMgr.start(task);
22230
22231     },
22232
22233     // private
22234     onResize : function(w, h)
22235     {
22236          Roo.log('resize: ' +w + ',' + h );
22237         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22238         if(!this.iframe){
22239             return;
22240         }
22241         if(typeof w == 'number'){
22242             
22243             this.iframe.style.width = w + 'px';
22244         }
22245         if(typeof h == 'number'){
22246             
22247             this.iframe.style.height = h + 'px';
22248             if(this.doc){
22249                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22250             }
22251         }
22252         
22253     },
22254
22255     /**
22256      * Toggles the editor between standard and source edit mode.
22257      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22258      */
22259     toggleSourceEdit : function(sourceEditMode){
22260         
22261         this.sourceEditMode = sourceEditMode === true;
22262         
22263         if(this.sourceEditMode){
22264  
22265             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22266             
22267         }else{
22268             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22269             //this.iframe.className = '';
22270             this.deferFocus();
22271         }
22272         //this.setSize(this.owner.wrap.getSize());
22273         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22274     },
22275
22276     
22277   
22278
22279     /**
22280      * Protected method that will not generally be called directly. If you need/want
22281      * custom HTML cleanup, this is the method you should override.
22282      * @param {String} html The HTML to be cleaned
22283      * return {String} The cleaned HTML
22284      */
22285     cleanHtml : function(html){
22286         html = String(html);
22287         if(html.length > 5){
22288             if(Roo.isSafari){ // strip safari nonsense
22289                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22290             }
22291         }
22292         if(html == '&nbsp;'){
22293             html = '';
22294         }
22295         return html;
22296     },
22297
22298     /**
22299      * HTML Editor -> Textarea
22300      * Protected method that will not generally be called directly. Syncs the contents
22301      * of the editor iframe with the textarea.
22302      */
22303     syncValue : function(){
22304         if(this.initialized){
22305             var bd = (this.doc.body || this.doc.documentElement);
22306             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22307             var html = bd.innerHTML;
22308             if(Roo.isSafari){
22309                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22310                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22311                 if(m && m[1]){
22312                     html = '<div style="'+m[0]+'">' + html + '</div>';
22313                 }
22314             }
22315             html = this.cleanHtml(html);
22316             // fix up the special chars.. normaly like back quotes in word...
22317             // however we do not want to do this with chinese..
22318             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22319                 var cc = b.charCodeAt();
22320                 if (
22321                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22322                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22323                     (cc >= 0xf900 && cc < 0xfb00 )
22324                 ) {
22325                         return b;
22326                 }
22327                 return "&#"+cc+";" 
22328             });
22329             if(this.owner.fireEvent('beforesync', this, html) !== false){
22330                 this.el.dom.value = html;
22331                 this.owner.fireEvent('sync', this, html);
22332             }
22333         }
22334     },
22335
22336     /**
22337      * Protected method that will not generally be called directly. Pushes the value of the textarea
22338      * into the iframe editor.
22339      */
22340     pushValue : function(){
22341         if(this.initialized){
22342             var v = this.el.dom.value.trim();
22343             
22344 //            if(v.length < 1){
22345 //                v = '&#160;';
22346 //            }
22347             
22348             if(this.owner.fireEvent('beforepush', this, v) !== false){
22349                 var d = (this.doc.body || this.doc.documentElement);
22350                 d.innerHTML = v;
22351                 this.cleanUpPaste();
22352                 this.el.dom.value = d.innerHTML;
22353                 this.owner.fireEvent('push', this, v);
22354             }
22355         }
22356     },
22357
22358     // private
22359     deferFocus : function(){
22360         this.focus.defer(10, this);
22361     },
22362
22363     // doc'ed in Field
22364     focus : function(){
22365         if(this.win && !this.sourceEditMode){
22366             this.win.focus();
22367         }else{
22368             this.el.focus();
22369         }
22370     },
22371     
22372     assignDocWin: function()
22373     {
22374         var iframe = this.iframe;
22375         
22376          if(Roo.isIE){
22377             this.doc = iframe.contentWindow.document;
22378             this.win = iframe.contentWindow;
22379         } else {
22380 //            if (!Roo.get(this.frameId)) {
22381 //                return;
22382 //            }
22383 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22384 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22385             
22386             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22387                 return;
22388             }
22389             
22390             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22391             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22392         }
22393     },
22394     
22395     // private
22396     initEditor : function(){
22397         //console.log("INIT EDITOR");
22398         this.assignDocWin();
22399         
22400         
22401         
22402         this.doc.designMode="on";
22403         this.doc.open();
22404         this.doc.write(this.getDocMarkup());
22405         this.doc.close();
22406         
22407         var dbody = (this.doc.body || this.doc.documentElement);
22408         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22409         // this copies styles from the containing element into thsi one..
22410         // not sure why we need all of this..
22411         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22412         
22413         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22414         //ss['background-attachment'] = 'fixed'; // w3c
22415         dbody.bgProperties = 'fixed'; // ie
22416         //Roo.DomHelper.applyStyles(dbody, ss);
22417         Roo.EventManager.on(this.doc, {
22418             //'mousedown': this.onEditorEvent,
22419             'mouseup': this.onEditorEvent,
22420             'dblclick': this.onEditorEvent,
22421             'click': this.onEditorEvent,
22422             'keyup': this.onEditorEvent,
22423             buffer:100,
22424             scope: this
22425         });
22426         if(Roo.isGecko){
22427             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22428         }
22429         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22430             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22431         }
22432         this.initialized = true;
22433
22434         this.owner.fireEvent('initialize', this);
22435         this.pushValue();
22436     },
22437
22438     // private
22439     onDestroy : function(){
22440         
22441         
22442         
22443         if(this.rendered){
22444             
22445             //for (var i =0; i < this.toolbars.length;i++) {
22446             //    // fixme - ask toolbars for heights?
22447             //    this.toolbars[i].onDestroy();
22448            // }
22449             
22450             //this.wrap.dom.innerHTML = '';
22451             //this.wrap.remove();
22452         }
22453     },
22454
22455     // private
22456     onFirstFocus : function(){
22457         
22458         this.assignDocWin();
22459         
22460         
22461         this.activated = true;
22462          
22463     
22464         if(Roo.isGecko){ // prevent silly gecko errors
22465             this.win.focus();
22466             var s = this.win.getSelection();
22467             if(!s.focusNode || s.focusNode.nodeType != 3){
22468                 var r = s.getRangeAt(0);
22469                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22470                 r.collapse(true);
22471                 this.deferFocus();
22472             }
22473             try{
22474                 this.execCmd('useCSS', true);
22475                 this.execCmd('styleWithCSS', false);
22476             }catch(e){}
22477         }
22478         this.owner.fireEvent('activate', this);
22479     },
22480
22481     // private
22482     adjustFont: function(btn){
22483         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22484         //if(Roo.isSafari){ // safari
22485         //    adjust *= 2;
22486        // }
22487         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22488         if(Roo.isSafari){ // safari
22489             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22490             v =  (v < 10) ? 10 : v;
22491             v =  (v > 48) ? 48 : v;
22492             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22493             
22494         }
22495         
22496         
22497         v = Math.max(1, v+adjust);
22498         
22499         this.execCmd('FontSize', v  );
22500     },
22501
22502     onEditorEvent : function(e)
22503     {
22504         this.owner.fireEvent('editorevent', this, e);
22505       //  this.updateToolbar();
22506         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22507     },
22508
22509     insertTag : function(tg)
22510     {
22511         // could be a bit smarter... -> wrap the current selected tRoo..
22512         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22513             
22514             range = this.createRange(this.getSelection());
22515             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22516             wrappingNode.appendChild(range.extractContents());
22517             range.insertNode(wrappingNode);
22518
22519             return;
22520             
22521             
22522             
22523         }
22524         this.execCmd("formatblock",   tg);
22525         
22526     },
22527     
22528     insertText : function(txt)
22529     {
22530         
22531         
22532         var range = this.createRange();
22533         range.deleteContents();
22534                //alert(Sender.getAttribute('label'));
22535                
22536         range.insertNode(this.doc.createTextNode(txt));
22537     } ,
22538     
22539      
22540
22541     /**
22542      * Executes a Midas editor command on the editor document and performs necessary focus and
22543      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22544      * @param {String} cmd The Midas command
22545      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22546      */
22547     relayCmd : function(cmd, value){
22548         this.win.focus();
22549         this.execCmd(cmd, value);
22550         this.owner.fireEvent('editorevent', this);
22551         //this.updateToolbar();
22552         this.owner.deferFocus();
22553     },
22554
22555     /**
22556      * Executes a Midas editor command directly on the editor document.
22557      * For visual commands, you should use {@link #relayCmd} instead.
22558      * <b>This should only be called after the editor is initialized.</b>
22559      * @param {String} cmd The Midas command
22560      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22561      */
22562     execCmd : function(cmd, value){
22563         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22564         this.syncValue();
22565     },
22566  
22567  
22568    
22569     /**
22570      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22571      * to insert tRoo.
22572      * @param {String} text | dom node.. 
22573      */
22574     insertAtCursor : function(text)
22575     {
22576         
22577         if(!this.activated){
22578             return;
22579         }
22580         /*
22581         if(Roo.isIE){
22582             this.win.focus();
22583             var r = this.doc.selection.createRange();
22584             if(r){
22585                 r.collapse(true);
22586                 r.pasteHTML(text);
22587                 this.syncValue();
22588                 this.deferFocus();
22589             
22590             }
22591             return;
22592         }
22593         */
22594         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22595             this.win.focus();
22596             
22597             
22598             // from jquery ui (MIT licenced)
22599             var range, node;
22600             var win = this.win;
22601             
22602             if (win.getSelection && win.getSelection().getRangeAt) {
22603                 range = win.getSelection().getRangeAt(0);
22604                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22605                 range.insertNode(node);
22606             } else if (win.document.selection && win.document.selection.createRange) {
22607                 // no firefox support
22608                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22609                 win.document.selection.createRange().pasteHTML(txt);
22610             } else {
22611                 // no firefox support
22612                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22613                 this.execCmd('InsertHTML', txt);
22614             } 
22615             
22616             this.syncValue();
22617             
22618             this.deferFocus();
22619         }
22620     },
22621  // private
22622     mozKeyPress : function(e){
22623         if(e.ctrlKey){
22624             var c = e.getCharCode(), cmd;
22625           
22626             if(c > 0){
22627                 c = String.fromCharCode(c).toLowerCase();
22628                 switch(c){
22629                     case 'b':
22630                         cmd = 'bold';
22631                         break;
22632                     case 'i':
22633                         cmd = 'italic';
22634                         break;
22635                     
22636                     case 'u':
22637                         cmd = 'underline';
22638                         break;
22639                     
22640                     case 'v':
22641                         this.cleanUpPaste.defer(100, this);
22642                         return;
22643                         
22644                 }
22645                 if(cmd){
22646                     this.win.focus();
22647                     this.execCmd(cmd);
22648                     this.deferFocus();
22649                     e.preventDefault();
22650                 }
22651                 
22652             }
22653         }
22654     },
22655
22656     // private
22657     fixKeys : function(){ // load time branching for fastest keydown performance
22658         if(Roo.isIE){
22659             return function(e){
22660                 var k = e.getKey(), r;
22661                 if(k == e.TAB){
22662                     e.stopEvent();
22663                     r = this.doc.selection.createRange();
22664                     if(r){
22665                         r.collapse(true);
22666                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22667                         this.deferFocus();
22668                     }
22669                     return;
22670                 }
22671                 
22672                 if(k == e.ENTER){
22673                     r = this.doc.selection.createRange();
22674                     if(r){
22675                         var target = r.parentElement();
22676                         if(!target || target.tagName.toLowerCase() != 'li'){
22677                             e.stopEvent();
22678                             r.pasteHTML('<br />');
22679                             r.collapse(false);
22680                             r.select();
22681                         }
22682                     }
22683                 }
22684                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22685                     this.cleanUpPaste.defer(100, this);
22686                     return;
22687                 }
22688                 
22689                 
22690             };
22691         }else if(Roo.isOpera){
22692             return function(e){
22693                 var k = e.getKey();
22694                 if(k == e.TAB){
22695                     e.stopEvent();
22696                     this.win.focus();
22697                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22698                     this.deferFocus();
22699                 }
22700                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22701                     this.cleanUpPaste.defer(100, this);
22702                     return;
22703                 }
22704                 
22705             };
22706         }else if(Roo.isSafari){
22707             return function(e){
22708                 var k = e.getKey();
22709                 
22710                 if(k == e.TAB){
22711                     e.stopEvent();
22712                     this.execCmd('InsertText','\t');
22713                     this.deferFocus();
22714                     return;
22715                 }
22716                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22717                     this.cleanUpPaste.defer(100, this);
22718                     return;
22719                 }
22720                 
22721              };
22722         }
22723     }(),
22724     
22725     getAllAncestors: function()
22726     {
22727         var p = this.getSelectedNode();
22728         var a = [];
22729         if (!p) {
22730             a.push(p); // push blank onto stack..
22731             p = this.getParentElement();
22732         }
22733         
22734         
22735         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22736             a.push(p);
22737             p = p.parentNode;
22738         }
22739         a.push(this.doc.body);
22740         return a;
22741     },
22742     lastSel : false,
22743     lastSelNode : false,
22744     
22745     
22746     getSelection : function() 
22747     {
22748         this.assignDocWin();
22749         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22750     },
22751     
22752     getSelectedNode: function() 
22753     {
22754         // this may only work on Gecko!!!
22755         
22756         // should we cache this!!!!
22757         
22758         
22759         
22760          
22761         var range = this.createRange(this.getSelection()).cloneRange();
22762         
22763         if (Roo.isIE) {
22764             var parent = range.parentElement();
22765             while (true) {
22766                 var testRange = range.duplicate();
22767                 testRange.moveToElementText(parent);
22768                 if (testRange.inRange(range)) {
22769                     break;
22770                 }
22771                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22772                     break;
22773                 }
22774                 parent = parent.parentElement;
22775             }
22776             return parent;
22777         }
22778         
22779         // is ancestor a text element.
22780         var ac =  range.commonAncestorContainer;
22781         if (ac.nodeType == 3) {
22782             ac = ac.parentNode;
22783         }
22784         
22785         var ar = ac.childNodes;
22786          
22787         var nodes = [];
22788         var other_nodes = [];
22789         var has_other_nodes = false;
22790         for (var i=0;i<ar.length;i++) {
22791             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22792                 continue;
22793             }
22794             // fullly contained node.
22795             
22796             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22797                 nodes.push(ar[i]);
22798                 continue;
22799             }
22800             
22801             // probably selected..
22802             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22803                 other_nodes.push(ar[i]);
22804                 continue;
22805             }
22806             // outer..
22807             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22808                 continue;
22809             }
22810             
22811             
22812             has_other_nodes = true;
22813         }
22814         if (!nodes.length && other_nodes.length) {
22815             nodes= other_nodes;
22816         }
22817         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22818             return false;
22819         }
22820         
22821         return nodes[0];
22822     },
22823     createRange: function(sel)
22824     {
22825         // this has strange effects when using with 
22826         // top toolbar - not sure if it's a great idea.
22827         //this.editor.contentWindow.focus();
22828         if (typeof sel != "undefined") {
22829             try {
22830                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22831             } catch(e) {
22832                 return this.doc.createRange();
22833             }
22834         } else {
22835             return this.doc.createRange();
22836         }
22837     },
22838     getParentElement: function()
22839     {
22840         
22841         this.assignDocWin();
22842         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22843         
22844         var range = this.createRange(sel);
22845          
22846         try {
22847             var p = range.commonAncestorContainer;
22848             while (p.nodeType == 3) { // text node
22849                 p = p.parentNode;
22850             }
22851             return p;
22852         } catch (e) {
22853             return null;
22854         }
22855     
22856     },
22857     /***
22858      *
22859      * Range intersection.. the hard stuff...
22860      *  '-1' = before
22861      *  '0' = hits..
22862      *  '1' = after.
22863      *         [ -- selected range --- ]
22864      *   [fail]                        [fail]
22865      *
22866      *    basically..
22867      *      if end is before start or  hits it. fail.
22868      *      if start is after end or hits it fail.
22869      *
22870      *   if either hits (but other is outside. - then it's not 
22871      *   
22872      *    
22873      **/
22874     
22875     
22876     // @see http://www.thismuchiknow.co.uk/?p=64.
22877     rangeIntersectsNode : function(range, node)
22878     {
22879         var nodeRange = node.ownerDocument.createRange();
22880         try {
22881             nodeRange.selectNode(node);
22882         } catch (e) {
22883             nodeRange.selectNodeContents(node);
22884         }
22885     
22886         var rangeStartRange = range.cloneRange();
22887         rangeStartRange.collapse(true);
22888     
22889         var rangeEndRange = range.cloneRange();
22890         rangeEndRange.collapse(false);
22891     
22892         var nodeStartRange = nodeRange.cloneRange();
22893         nodeStartRange.collapse(true);
22894     
22895         var nodeEndRange = nodeRange.cloneRange();
22896         nodeEndRange.collapse(false);
22897     
22898         return rangeStartRange.compareBoundaryPoints(
22899                  Range.START_TO_START, nodeEndRange) == -1 &&
22900                rangeEndRange.compareBoundaryPoints(
22901                  Range.START_TO_START, nodeStartRange) == 1;
22902         
22903          
22904     },
22905     rangeCompareNode : function(range, node)
22906     {
22907         var nodeRange = node.ownerDocument.createRange();
22908         try {
22909             nodeRange.selectNode(node);
22910         } catch (e) {
22911             nodeRange.selectNodeContents(node);
22912         }
22913         
22914         
22915         range.collapse(true);
22916     
22917         nodeRange.collapse(true);
22918      
22919         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22920         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22921          
22922         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22923         
22924         var nodeIsBefore   =  ss == 1;
22925         var nodeIsAfter    = ee == -1;
22926         
22927         if (nodeIsBefore && nodeIsAfter) {
22928             return 0; // outer
22929         }
22930         if (!nodeIsBefore && nodeIsAfter) {
22931             return 1; //right trailed.
22932         }
22933         
22934         if (nodeIsBefore && !nodeIsAfter) {
22935             return 2;  // left trailed.
22936         }
22937         // fully contined.
22938         return 3;
22939     },
22940
22941     // private? - in a new class?
22942     cleanUpPaste :  function()
22943     {
22944         // cleans up the whole document..
22945         Roo.log('cleanuppaste');
22946         
22947         this.cleanUpChildren(this.doc.body);
22948         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22949         if (clean != this.doc.body.innerHTML) {
22950             this.doc.body.innerHTML = clean;
22951         }
22952         
22953     },
22954     
22955     cleanWordChars : function(input) {// change the chars to hex code
22956         var he = Roo.HtmlEditorCore;
22957         
22958         var output = input;
22959         Roo.each(he.swapCodes, function(sw) { 
22960             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22961             
22962             output = output.replace(swapper, sw[1]);
22963         });
22964         
22965         return output;
22966     },
22967     
22968     
22969     cleanUpChildren : function (n)
22970     {
22971         if (!n.childNodes.length) {
22972             return;
22973         }
22974         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22975            this.cleanUpChild(n.childNodes[i]);
22976         }
22977     },
22978     
22979     
22980         
22981     
22982     cleanUpChild : function (node)
22983     {
22984         var ed = this;
22985         //console.log(node);
22986         if (node.nodeName == "#text") {
22987             // clean up silly Windows -- stuff?
22988             return; 
22989         }
22990         if (node.nodeName == "#comment") {
22991             node.parentNode.removeChild(node);
22992             // clean up silly Windows -- stuff?
22993             return; 
22994         }
22995         var lcname = node.tagName.toLowerCase();
22996         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22997         // whitelist of tags..
22998         
22999         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23000             // remove node.
23001             node.parentNode.removeChild(node);
23002             return;
23003             
23004         }
23005         
23006         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23007         
23008         // remove <a name=....> as rendering on yahoo mailer is borked with this.
23009         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23010         
23011         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23012         //    remove_keep_children = true;
23013         //}
23014         
23015         if (remove_keep_children) {
23016             this.cleanUpChildren(node);
23017             // inserts everything just before this node...
23018             while (node.childNodes.length) {
23019                 var cn = node.childNodes[0];
23020                 node.removeChild(cn);
23021                 node.parentNode.insertBefore(cn, node);
23022             }
23023             node.parentNode.removeChild(node);
23024             return;
23025         }
23026         
23027         if (!node.attributes || !node.attributes.length) {
23028             this.cleanUpChildren(node);
23029             return;
23030         }
23031         
23032         function cleanAttr(n,v)
23033         {
23034             
23035             if (v.match(/^\./) || v.match(/^\//)) {
23036                 return;
23037             }
23038             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23039                 return;
23040             }
23041             if (v.match(/^#/)) {
23042                 return;
23043             }
23044 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23045             node.removeAttribute(n);
23046             
23047         }
23048         
23049         var cwhite = this.cwhite;
23050         var cblack = this.cblack;
23051             
23052         function cleanStyle(n,v)
23053         {
23054             if (v.match(/expression/)) { //XSS?? should we even bother..
23055                 node.removeAttribute(n);
23056                 return;
23057             }
23058             
23059             var parts = v.split(/;/);
23060             var clean = [];
23061             
23062             Roo.each(parts, function(p) {
23063                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23064                 if (!p.length) {
23065                     return true;
23066                 }
23067                 var l = p.split(':').shift().replace(/\s+/g,'');
23068                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23069                 
23070                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23071 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23072                     //node.removeAttribute(n);
23073                     return true;
23074                 }
23075                 //Roo.log()
23076                 // only allow 'c whitelisted system attributes'
23077                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23078 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23079                     //node.removeAttribute(n);
23080                     return true;
23081                 }
23082                 
23083                 
23084                  
23085                 
23086                 clean.push(p);
23087                 return true;
23088             });
23089             if (clean.length) { 
23090                 node.setAttribute(n, clean.join(';'));
23091             } else {
23092                 node.removeAttribute(n);
23093             }
23094             
23095         }
23096         
23097         
23098         for (var i = node.attributes.length-1; i > -1 ; i--) {
23099             var a = node.attributes[i];
23100             //console.log(a);
23101             
23102             if (a.name.toLowerCase().substr(0,2)=='on')  {
23103                 node.removeAttribute(a.name);
23104                 continue;
23105             }
23106             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23107                 node.removeAttribute(a.name);
23108                 continue;
23109             }
23110             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23111                 cleanAttr(a.name,a.value); // fixme..
23112                 continue;
23113             }
23114             if (a.name == 'style') {
23115                 cleanStyle(a.name,a.value);
23116                 continue;
23117             }
23118             /// clean up MS crap..
23119             // tecnically this should be a list of valid class'es..
23120             
23121             
23122             if (a.name == 'class') {
23123                 if (a.value.match(/^Mso/)) {
23124                     node.className = '';
23125                 }
23126                 
23127                 if (a.value.match(/^body$/)) {
23128                     node.className = '';
23129                 }
23130                 continue;
23131             }
23132             
23133             // style cleanup!?
23134             // class cleanup?
23135             
23136         }
23137         
23138         
23139         this.cleanUpChildren(node);
23140         
23141         
23142     },
23143     
23144     /**
23145      * Clean up MS wordisms...
23146      */
23147     cleanWord : function(node)
23148     {
23149         
23150         
23151         if (!node) {
23152             this.cleanWord(this.doc.body);
23153             return;
23154         }
23155         if (node.nodeName == "#text") {
23156             // clean up silly Windows -- stuff?
23157             return; 
23158         }
23159         if (node.nodeName == "#comment") {
23160             node.parentNode.removeChild(node);
23161             // clean up silly Windows -- stuff?
23162             return; 
23163         }
23164         
23165         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23166             node.parentNode.removeChild(node);
23167             return;
23168         }
23169         
23170         // remove - but keep children..
23171         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23172             while (node.childNodes.length) {
23173                 var cn = node.childNodes[0];
23174                 node.removeChild(cn);
23175                 node.parentNode.insertBefore(cn, node);
23176             }
23177             node.parentNode.removeChild(node);
23178             this.iterateChildren(node, this.cleanWord);
23179             return;
23180         }
23181         // clean styles
23182         if (node.className.length) {
23183             
23184             var cn = node.className.split(/\W+/);
23185             var cna = [];
23186             Roo.each(cn, function(cls) {
23187                 if (cls.match(/Mso[a-zA-Z]+/)) {
23188                     return;
23189                 }
23190                 cna.push(cls);
23191             });
23192             node.className = cna.length ? cna.join(' ') : '';
23193             if (!cna.length) {
23194                 node.removeAttribute("class");
23195             }
23196         }
23197         
23198         if (node.hasAttribute("lang")) {
23199             node.removeAttribute("lang");
23200         }
23201         
23202         if (node.hasAttribute("style")) {
23203             
23204             var styles = node.getAttribute("style").split(";");
23205             var nstyle = [];
23206             Roo.each(styles, function(s) {
23207                 if (!s.match(/:/)) {
23208                     return;
23209                 }
23210                 var kv = s.split(":");
23211                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23212                     return;
23213                 }
23214                 // what ever is left... we allow.
23215                 nstyle.push(s);
23216             });
23217             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23218             if (!nstyle.length) {
23219                 node.removeAttribute('style');
23220             }
23221         }
23222         this.iterateChildren(node, this.cleanWord);
23223         
23224         
23225         
23226     },
23227     /**
23228      * iterateChildren of a Node, calling fn each time, using this as the scole..
23229      * @param {DomNode} node node to iterate children of.
23230      * @param {Function} fn method of this class to call on each item.
23231      */
23232     iterateChildren : function(node, fn)
23233     {
23234         if (!node.childNodes.length) {
23235                 return;
23236         }
23237         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23238            fn.call(this, node.childNodes[i])
23239         }
23240     },
23241     
23242     
23243     /**
23244      * cleanTableWidths.
23245      *
23246      * Quite often pasting from word etc.. results in tables with column and widths.
23247      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23248      *
23249      */
23250     cleanTableWidths : function(node)
23251     {
23252          
23253          
23254         if (!node) {
23255             this.cleanTableWidths(this.doc.body);
23256             return;
23257         }
23258         
23259         // ignore list...
23260         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23261             return; 
23262         }
23263         Roo.log(node.tagName);
23264         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23265             this.iterateChildren(node, this.cleanTableWidths);
23266             return;
23267         }
23268         if (node.hasAttribute('width')) {
23269             node.removeAttribute('width');
23270         }
23271         
23272          
23273         if (node.hasAttribute("style")) {
23274             // pretty basic...
23275             
23276             var styles = node.getAttribute("style").split(";");
23277             var nstyle = [];
23278             Roo.each(styles, function(s) {
23279                 if (!s.match(/:/)) {
23280                     return;
23281                 }
23282                 var kv = s.split(":");
23283                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23284                     return;
23285                 }
23286                 // what ever is left... we allow.
23287                 nstyle.push(s);
23288             });
23289             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23290             if (!nstyle.length) {
23291                 node.removeAttribute('style');
23292             }
23293         }
23294         
23295         this.iterateChildren(node, this.cleanTableWidths);
23296         
23297         
23298     },
23299     
23300     
23301     
23302     
23303     domToHTML : function(currentElement, depth, nopadtext) {
23304         
23305         depth = depth || 0;
23306         nopadtext = nopadtext || false;
23307     
23308         if (!currentElement) {
23309             return this.domToHTML(this.doc.body);
23310         }
23311         
23312         //Roo.log(currentElement);
23313         var j;
23314         var allText = false;
23315         var nodeName = currentElement.nodeName;
23316         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23317         
23318         if  (nodeName == '#text') {
23319             
23320             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23321         }
23322         
23323         
23324         var ret = '';
23325         if (nodeName != 'BODY') {
23326              
23327             var i = 0;
23328             // Prints the node tagName, such as <A>, <IMG>, etc
23329             if (tagName) {
23330                 var attr = [];
23331                 for(i = 0; i < currentElement.attributes.length;i++) {
23332                     // quoting?
23333                     var aname = currentElement.attributes.item(i).name;
23334                     if (!currentElement.attributes.item(i).value.length) {
23335                         continue;
23336                     }
23337                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23338                 }
23339                 
23340                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23341             } 
23342             else {
23343                 
23344                 // eack
23345             }
23346         } else {
23347             tagName = false;
23348         }
23349         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23350             return ret;
23351         }
23352         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23353             nopadtext = true;
23354         }
23355         
23356         
23357         // Traverse the tree
23358         i = 0;
23359         var currentElementChild = currentElement.childNodes.item(i);
23360         var allText = true;
23361         var innerHTML  = '';
23362         lastnode = '';
23363         while (currentElementChild) {
23364             // Formatting code (indent the tree so it looks nice on the screen)
23365             var nopad = nopadtext;
23366             if (lastnode == 'SPAN') {
23367                 nopad  = true;
23368             }
23369             // text
23370             if  (currentElementChild.nodeName == '#text') {
23371                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23372                 toadd = nopadtext ? toadd : toadd.trim();
23373                 if (!nopad && toadd.length > 80) {
23374                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23375                 }
23376                 innerHTML  += toadd;
23377                 
23378                 i++;
23379                 currentElementChild = currentElement.childNodes.item(i);
23380                 lastNode = '';
23381                 continue;
23382             }
23383             allText = false;
23384             
23385             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23386                 
23387             // Recursively traverse the tree structure of the child node
23388             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23389             lastnode = currentElementChild.nodeName;
23390             i++;
23391             currentElementChild=currentElement.childNodes.item(i);
23392         }
23393         
23394         ret += innerHTML;
23395         
23396         if (!allText) {
23397                 // The remaining code is mostly for formatting the tree
23398             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23399         }
23400         
23401         
23402         if (tagName) {
23403             ret+= "</"+tagName+">";
23404         }
23405         return ret;
23406         
23407     },
23408         
23409     applyBlacklists : function()
23410     {
23411         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23412         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23413         
23414         this.white = [];
23415         this.black = [];
23416         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23417             if (b.indexOf(tag) > -1) {
23418                 return;
23419             }
23420             this.white.push(tag);
23421             
23422         }, this);
23423         
23424         Roo.each(w, function(tag) {
23425             if (b.indexOf(tag) > -1) {
23426                 return;
23427             }
23428             if (this.white.indexOf(tag) > -1) {
23429                 return;
23430             }
23431             this.white.push(tag);
23432             
23433         }, this);
23434         
23435         
23436         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23437             if (w.indexOf(tag) > -1) {
23438                 return;
23439             }
23440             this.black.push(tag);
23441             
23442         }, this);
23443         
23444         Roo.each(b, function(tag) {
23445             if (w.indexOf(tag) > -1) {
23446                 return;
23447             }
23448             if (this.black.indexOf(tag) > -1) {
23449                 return;
23450             }
23451             this.black.push(tag);
23452             
23453         }, this);
23454         
23455         
23456         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23457         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23458         
23459         this.cwhite = [];
23460         this.cblack = [];
23461         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23462             if (b.indexOf(tag) > -1) {
23463                 return;
23464             }
23465             this.cwhite.push(tag);
23466             
23467         }, this);
23468         
23469         Roo.each(w, function(tag) {
23470             if (b.indexOf(tag) > -1) {
23471                 return;
23472             }
23473             if (this.cwhite.indexOf(tag) > -1) {
23474                 return;
23475             }
23476             this.cwhite.push(tag);
23477             
23478         }, this);
23479         
23480         
23481         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23482             if (w.indexOf(tag) > -1) {
23483                 return;
23484             }
23485             this.cblack.push(tag);
23486             
23487         }, this);
23488         
23489         Roo.each(b, function(tag) {
23490             if (w.indexOf(tag) > -1) {
23491                 return;
23492             }
23493             if (this.cblack.indexOf(tag) > -1) {
23494                 return;
23495             }
23496             this.cblack.push(tag);
23497             
23498         }, this);
23499     },
23500     
23501     setStylesheets : function(stylesheets)
23502     {
23503         if(typeof(stylesheets) == 'string'){
23504             Roo.get(this.iframe.contentDocument.head).createChild({
23505                 tag : 'link',
23506                 rel : 'stylesheet',
23507                 type : 'text/css',
23508                 href : stylesheets
23509             });
23510             
23511             return;
23512         }
23513         var _this = this;
23514      
23515         Roo.each(stylesheets, function(s) {
23516             if(!s.length){
23517                 return;
23518             }
23519             
23520             Roo.get(_this.iframe.contentDocument.head).createChild({
23521                 tag : 'link',
23522                 rel : 'stylesheet',
23523                 type : 'text/css',
23524                 href : s
23525             });
23526         });
23527
23528         
23529     },
23530     
23531     removeStylesheets : function()
23532     {
23533         var _this = this;
23534         
23535         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23536             s.remove();
23537         });
23538     },
23539     
23540     setStyle : function(style)
23541     {
23542         Roo.get(this.iframe.contentDocument.head).createChild({
23543             tag : 'style',
23544             type : 'text/css',
23545             html : style
23546         });
23547
23548         return;
23549     }
23550     
23551     // hide stuff that is not compatible
23552     /**
23553      * @event blur
23554      * @hide
23555      */
23556     /**
23557      * @event change
23558      * @hide
23559      */
23560     /**
23561      * @event focus
23562      * @hide
23563      */
23564     /**
23565      * @event specialkey
23566      * @hide
23567      */
23568     /**
23569      * @cfg {String} fieldClass @hide
23570      */
23571     /**
23572      * @cfg {String} focusClass @hide
23573      */
23574     /**
23575      * @cfg {String} autoCreate @hide
23576      */
23577     /**
23578      * @cfg {String} inputType @hide
23579      */
23580     /**
23581      * @cfg {String} invalidClass @hide
23582      */
23583     /**
23584      * @cfg {String} invalidText @hide
23585      */
23586     /**
23587      * @cfg {String} msgFx @hide
23588      */
23589     /**
23590      * @cfg {String} validateOnBlur @hide
23591      */
23592 });
23593
23594 Roo.HtmlEditorCore.white = [
23595         'area', 'br', 'img', 'input', 'hr', 'wbr',
23596         
23597        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23598        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23599        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23600        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23601        'table',   'ul',         'xmp', 
23602        
23603        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23604       'thead',   'tr', 
23605      
23606       'dir', 'menu', 'ol', 'ul', 'dl',
23607        
23608       'embed',  'object'
23609 ];
23610
23611
23612 Roo.HtmlEditorCore.black = [
23613     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23614         'applet', // 
23615         'base',   'basefont', 'bgsound', 'blink',  'body', 
23616         'frame',  'frameset', 'head',    'html',   'ilayer', 
23617         'iframe', 'layer',  'link',     'meta',    'object',   
23618         'script', 'style' ,'title',  'xml' // clean later..
23619 ];
23620 Roo.HtmlEditorCore.clean = [
23621     'script', 'style', 'title', 'xml'
23622 ];
23623 Roo.HtmlEditorCore.remove = [
23624     'font'
23625 ];
23626 // attributes..
23627
23628 Roo.HtmlEditorCore.ablack = [
23629     'on'
23630 ];
23631     
23632 Roo.HtmlEditorCore.aclean = [ 
23633     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23634 ];
23635
23636 // protocols..
23637 Roo.HtmlEditorCore.pwhite= [
23638         'http',  'https',  'mailto'
23639 ];
23640
23641 // white listed style attributes.
23642 Roo.HtmlEditorCore.cwhite= [
23643       //  'text-align', /// default is to allow most things..
23644       
23645          
23646 //        'font-size'//??
23647 ];
23648
23649 // black listed style attributes.
23650 Roo.HtmlEditorCore.cblack= [
23651       //  'font-size' -- this can be set by the project 
23652 ];
23653
23654
23655 Roo.HtmlEditorCore.swapCodes   =[ 
23656     [    8211, "--" ], 
23657     [    8212, "--" ], 
23658     [    8216,  "'" ],  
23659     [    8217, "'" ],  
23660     [    8220, '"' ],  
23661     [    8221, '"' ],  
23662     [    8226, "*" ],  
23663     [    8230, "..." ]
23664 ]; 
23665
23666     /*
23667  * - LGPL
23668  *
23669  * HtmlEditor
23670  * 
23671  */
23672
23673 /**
23674  * @class Roo.bootstrap.HtmlEditor
23675  * @extends Roo.bootstrap.TextArea
23676  * Bootstrap HtmlEditor class
23677
23678  * @constructor
23679  * Create a new HtmlEditor
23680  * @param {Object} config The config object
23681  */
23682
23683 Roo.bootstrap.HtmlEditor = function(config){
23684     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23685     if (!this.toolbars) {
23686         this.toolbars = [];
23687     }
23688     
23689     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23690     this.addEvents({
23691             /**
23692              * @event initialize
23693              * Fires when the editor is fully initialized (including the iframe)
23694              * @param {HtmlEditor} this
23695              */
23696             initialize: true,
23697             /**
23698              * @event activate
23699              * Fires when the editor is first receives the focus. Any insertion must wait
23700              * until after this event.
23701              * @param {HtmlEditor} this
23702              */
23703             activate: true,
23704              /**
23705              * @event beforesync
23706              * Fires before the textarea is updated with content from the editor iframe. Return false
23707              * to cancel the sync.
23708              * @param {HtmlEditor} this
23709              * @param {String} html
23710              */
23711             beforesync: true,
23712              /**
23713              * @event beforepush
23714              * Fires before the iframe editor is updated with content from the textarea. Return false
23715              * to cancel the push.
23716              * @param {HtmlEditor} this
23717              * @param {String} html
23718              */
23719             beforepush: true,
23720              /**
23721              * @event sync
23722              * Fires when the textarea is updated with content from the editor iframe.
23723              * @param {HtmlEditor} this
23724              * @param {String} html
23725              */
23726             sync: true,
23727              /**
23728              * @event push
23729              * Fires when the iframe editor is updated with content from the textarea.
23730              * @param {HtmlEditor} this
23731              * @param {String} html
23732              */
23733             push: true,
23734              /**
23735              * @event editmodechange
23736              * Fires when the editor switches edit modes
23737              * @param {HtmlEditor} this
23738              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23739              */
23740             editmodechange: true,
23741             /**
23742              * @event editorevent
23743              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23744              * @param {HtmlEditor} this
23745              */
23746             editorevent: true,
23747             /**
23748              * @event firstfocus
23749              * Fires when on first focus - needed by toolbars..
23750              * @param {HtmlEditor} this
23751              */
23752             firstfocus: true,
23753             /**
23754              * @event autosave
23755              * Auto save the htmlEditor value as a file into Events
23756              * @param {HtmlEditor} this
23757              */
23758             autosave: true,
23759             /**
23760              * @event savedpreview
23761              * preview the saved version of htmlEditor
23762              * @param {HtmlEditor} this
23763              */
23764             savedpreview: true
23765         });
23766 };
23767
23768
23769 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23770     
23771     
23772       /**
23773      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23774      */
23775     toolbars : false,
23776     
23777      /**
23778     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23779     */
23780     btns : [],
23781    
23782      /**
23783      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23784      *                        Roo.resizable.
23785      */
23786     resizable : false,
23787      /**
23788      * @cfg {Number} height (in pixels)
23789      */   
23790     height: 300,
23791    /**
23792      * @cfg {Number} width (in pixels)
23793      */   
23794     width: false,
23795     
23796     /**
23797      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23798      * 
23799      */
23800     stylesheets: false,
23801     
23802     // id of frame..
23803     frameId: false,
23804     
23805     // private properties
23806     validationEvent : false,
23807     deferHeight: true,
23808     initialized : false,
23809     activated : false,
23810     
23811     onFocus : Roo.emptyFn,
23812     iframePad:3,
23813     hideMode:'offsets',
23814     
23815     tbContainer : false,
23816     
23817     bodyCls : '',
23818     
23819     toolbarContainer :function() {
23820         return this.wrap.select('.x-html-editor-tb',true).first();
23821     },
23822
23823     /**
23824      * Protected method that will not generally be called directly. It
23825      * is called when the editor creates its toolbar. Override this method if you need to
23826      * add custom toolbar buttons.
23827      * @param {HtmlEditor} editor
23828      */
23829     createToolbar : function(){
23830         Roo.log('renewing');
23831         Roo.log("create toolbars");
23832         
23833         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23834         this.toolbars[0].render(this.toolbarContainer());
23835         
23836         return;
23837         
23838 //        if (!editor.toolbars || !editor.toolbars.length) {
23839 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23840 //        }
23841 //        
23842 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23843 //            editor.toolbars[i] = Roo.factory(
23844 //                    typeof(editor.toolbars[i]) == 'string' ?
23845 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23846 //                Roo.bootstrap.HtmlEditor);
23847 //            editor.toolbars[i].init(editor);
23848 //        }
23849     },
23850
23851      
23852     // private
23853     onRender : function(ct, position)
23854     {
23855        // Roo.log("Call onRender: " + this.xtype);
23856         var _t = this;
23857         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23858       
23859         this.wrap = this.inputEl().wrap({
23860             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23861         });
23862         
23863         this.editorcore.onRender(ct, position);
23864          
23865         if (this.resizable) {
23866             this.resizeEl = new Roo.Resizable(this.wrap, {
23867                 pinned : true,
23868                 wrap: true,
23869                 dynamic : true,
23870                 minHeight : this.height,
23871                 height: this.height,
23872                 handles : this.resizable,
23873                 width: this.width,
23874                 listeners : {
23875                     resize : function(r, w, h) {
23876                         _t.onResize(w,h); // -something
23877                     }
23878                 }
23879             });
23880             
23881         }
23882         this.createToolbar(this);
23883        
23884         
23885         if(!this.width && this.resizable){
23886             this.setSize(this.wrap.getSize());
23887         }
23888         if (this.resizeEl) {
23889             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23890             // should trigger onReize..
23891         }
23892         
23893     },
23894
23895     // private
23896     onResize : function(w, h)
23897     {
23898         Roo.log('resize: ' +w + ',' + h );
23899         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23900         var ew = false;
23901         var eh = false;
23902         
23903         if(this.inputEl() ){
23904             if(typeof w == 'number'){
23905                 var aw = w - this.wrap.getFrameWidth('lr');
23906                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23907                 ew = aw;
23908             }
23909             if(typeof h == 'number'){
23910                  var tbh = -11;  // fixme it needs to tool bar size!
23911                 for (var i =0; i < this.toolbars.length;i++) {
23912                     // fixme - ask toolbars for heights?
23913                     tbh += this.toolbars[i].el.getHeight();
23914                     //if (this.toolbars[i].footer) {
23915                     //    tbh += this.toolbars[i].footer.el.getHeight();
23916                     //}
23917                 }
23918               
23919                 
23920                 
23921                 
23922                 
23923                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23924                 ah -= 5; // knock a few pixes off for look..
23925                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23926                 var eh = ah;
23927             }
23928         }
23929         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23930         this.editorcore.onResize(ew,eh);
23931         
23932     },
23933
23934     /**
23935      * Toggles the editor between standard and source edit mode.
23936      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23937      */
23938     toggleSourceEdit : function(sourceEditMode)
23939     {
23940         this.editorcore.toggleSourceEdit(sourceEditMode);
23941         
23942         if(this.editorcore.sourceEditMode){
23943             Roo.log('editor - showing textarea');
23944             
23945 //            Roo.log('in');
23946 //            Roo.log(this.syncValue());
23947             this.syncValue();
23948             this.inputEl().removeClass(['hide', 'x-hidden']);
23949             this.inputEl().dom.removeAttribute('tabIndex');
23950             this.inputEl().focus();
23951         }else{
23952             Roo.log('editor - hiding textarea');
23953 //            Roo.log('out')
23954 //            Roo.log(this.pushValue()); 
23955             this.pushValue();
23956             
23957             this.inputEl().addClass(['hide', 'x-hidden']);
23958             this.inputEl().dom.setAttribute('tabIndex', -1);
23959             //this.deferFocus();
23960         }
23961          
23962         if(this.resizable){
23963             this.setSize(this.wrap.getSize());
23964         }
23965         
23966         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23967     },
23968  
23969     // private (for BoxComponent)
23970     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23971
23972     // private (for BoxComponent)
23973     getResizeEl : function(){
23974         return this.wrap;
23975     },
23976
23977     // private (for BoxComponent)
23978     getPositionEl : function(){
23979         return this.wrap;
23980     },
23981
23982     // private
23983     initEvents : function(){
23984         this.originalValue = this.getValue();
23985     },
23986
23987 //    /**
23988 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23989 //     * @method
23990 //     */
23991 //    markInvalid : Roo.emptyFn,
23992 //    /**
23993 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23994 //     * @method
23995 //     */
23996 //    clearInvalid : Roo.emptyFn,
23997
23998     setValue : function(v){
23999         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24000         this.editorcore.pushValue();
24001     },
24002
24003      
24004     // private
24005     deferFocus : function(){
24006         this.focus.defer(10, this);
24007     },
24008
24009     // doc'ed in Field
24010     focus : function(){
24011         this.editorcore.focus();
24012         
24013     },
24014       
24015
24016     // private
24017     onDestroy : function(){
24018         
24019         
24020         
24021         if(this.rendered){
24022             
24023             for (var i =0; i < this.toolbars.length;i++) {
24024                 // fixme - ask toolbars for heights?
24025                 this.toolbars[i].onDestroy();
24026             }
24027             
24028             this.wrap.dom.innerHTML = '';
24029             this.wrap.remove();
24030         }
24031     },
24032
24033     // private
24034     onFirstFocus : function(){
24035         //Roo.log("onFirstFocus");
24036         this.editorcore.onFirstFocus();
24037          for (var i =0; i < this.toolbars.length;i++) {
24038             this.toolbars[i].onFirstFocus();
24039         }
24040         
24041     },
24042     
24043     // private
24044     syncValue : function()
24045     {   
24046         this.editorcore.syncValue();
24047     },
24048     
24049     pushValue : function()
24050     {   
24051         this.editorcore.pushValue();
24052     }
24053      
24054     
24055     // hide stuff that is not compatible
24056     /**
24057      * @event blur
24058      * @hide
24059      */
24060     /**
24061      * @event change
24062      * @hide
24063      */
24064     /**
24065      * @event focus
24066      * @hide
24067      */
24068     /**
24069      * @event specialkey
24070      * @hide
24071      */
24072     /**
24073      * @cfg {String} fieldClass @hide
24074      */
24075     /**
24076      * @cfg {String} focusClass @hide
24077      */
24078     /**
24079      * @cfg {String} autoCreate @hide
24080      */
24081     /**
24082      * @cfg {String} inputType @hide
24083      */
24084      
24085     /**
24086      * @cfg {String} invalidText @hide
24087      */
24088     /**
24089      * @cfg {String} msgFx @hide
24090      */
24091     /**
24092      * @cfg {String} validateOnBlur @hide
24093      */
24094 });
24095  
24096     
24097    
24098    
24099    
24100       
24101 Roo.namespace('Roo.bootstrap.htmleditor');
24102 /**
24103  * @class Roo.bootstrap.HtmlEditorToolbar1
24104  * Basic Toolbar
24105  * 
24106  * @example
24107  * Usage:
24108  *
24109  new Roo.bootstrap.HtmlEditor({
24110     ....
24111     toolbars : [
24112         new Roo.bootstrap.HtmlEditorToolbar1({
24113             disable : { fonts: 1 , format: 1, ..., ... , ...],
24114             btns : [ .... ]
24115         })
24116     }
24117      
24118  * 
24119  * @cfg {Object} disable List of elements to disable..
24120  * @cfg {Array} btns List of additional buttons.
24121  * 
24122  * 
24123  * NEEDS Extra CSS? 
24124  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24125  */
24126  
24127 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24128 {
24129     
24130     Roo.apply(this, config);
24131     
24132     // default disabled, based on 'good practice'..
24133     this.disable = this.disable || {};
24134     Roo.applyIf(this.disable, {
24135         fontSize : true,
24136         colors : true,
24137         specialElements : true
24138     });
24139     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24140     
24141     this.editor = config.editor;
24142     this.editorcore = config.editor.editorcore;
24143     
24144     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24145     
24146     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24147     // dont call parent... till later.
24148 }
24149 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24150      
24151     bar : true,
24152     
24153     editor : false,
24154     editorcore : false,
24155     
24156     
24157     formats : [
24158         "p" ,  
24159         "h1","h2","h3","h4","h5","h6", 
24160         "pre", "code", 
24161         "abbr", "acronym", "address", "cite", "samp", "var",
24162         'div','span'
24163     ],
24164     
24165     onRender : function(ct, position)
24166     {
24167        // Roo.log("Call onRender: " + this.xtype);
24168         
24169        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24170        Roo.log(this.el);
24171        this.el.dom.style.marginBottom = '0';
24172        var _this = this;
24173        var editorcore = this.editorcore;
24174        var editor= this.editor;
24175        
24176        var children = [];
24177        var btn = function(id,cmd , toggle, handler, html){
24178        
24179             var  event = toggle ? 'toggle' : 'click';
24180        
24181             var a = {
24182                 size : 'sm',
24183                 xtype: 'Button',
24184                 xns: Roo.bootstrap,
24185                 //glyphicon : id,
24186                 fa: id,
24187                 cmd : id || cmd,
24188                 enableToggle:toggle !== false,
24189                 html : html || '',
24190                 pressed : toggle ? false : null,
24191                 listeners : {}
24192             };
24193             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24194                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24195             };
24196             children.push(a);
24197             return a;
24198        }
24199        
24200     //    var cb_box = function...
24201         
24202         var style = {
24203                 xtype: 'Button',
24204                 size : 'sm',
24205                 xns: Roo.bootstrap,
24206                 fa : 'font',
24207                 //html : 'submit'
24208                 menu : {
24209                     xtype: 'Menu',
24210                     xns: Roo.bootstrap,
24211                     items:  []
24212                 }
24213         };
24214         Roo.each(this.formats, function(f) {
24215             style.menu.items.push({
24216                 xtype :'MenuItem',
24217                 xns: Roo.bootstrap,
24218                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24219                 tagname : f,
24220                 listeners : {
24221                     click : function()
24222                     {
24223                         editorcore.insertTag(this.tagname);
24224                         editor.focus();
24225                     }
24226                 }
24227                 
24228             });
24229         });
24230         children.push(style);   
24231         
24232         btn('bold',false,true);
24233         btn('italic',false,true);
24234         btn('align-left', 'justifyleft',true);
24235         btn('align-center', 'justifycenter',true);
24236         btn('align-right' , 'justifyright',true);
24237         btn('link', false, false, function(btn) {
24238             //Roo.log("create link?");
24239             var url = prompt(this.createLinkText, this.defaultLinkValue);
24240             if(url && url != 'http:/'+'/'){
24241                 this.editorcore.relayCmd('createlink', url);
24242             }
24243         }),
24244         btn('list','insertunorderedlist',true);
24245         btn('pencil', false,true, function(btn){
24246                 Roo.log(this);
24247                 this.toggleSourceEdit(btn.pressed);
24248         });
24249         
24250         if (this.editor.btns.length > 0) {
24251             for (var i = 0; i<this.editor.btns.length; i++) {
24252                 children.push(this.editor.btns[i]);
24253             }
24254         }
24255         
24256         /*
24257         var cog = {
24258                 xtype: 'Button',
24259                 size : 'sm',
24260                 xns: Roo.bootstrap,
24261                 glyphicon : 'cog',
24262                 //html : 'submit'
24263                 menu : {
24264                     xtype: 'Menu',
24265                     xns: Roo.bootstrap,
24266                     items:  []
24267                 }
24268         };
24269         
24270         cog.menu.items.push({
24271             xtype :'MenuItem',
24272             xns: Roo.bootstrap,
24273             html : Clean styles,
24274             tagname : f,
24275             listeners : {
24276                 click : function()
24277                 {
24278                     editorcore.insertTag(this.tagname);
24279                     editor.focus();
24280                 }
24281             }
24282             
24283         });
24284        */
24285         
24286          
24287        this.xtype = 'NavSimplebar';
24288         
24289         for(var i=0;i< children.length;i++) {
24290             
24291             this.buttons.add(this.addxtypeChild(children[i]));
24292             
24293         }
24294         
24295         editor.on('editorevent', this.updateToolbar, this);
24296     },
24297     onBtnClick : function(id)
24298     {
24299        this.editorcore.relayCmd(id);
24300        this.editorcore.focus();
24301     },
24302     
24303     /**
24304      * Protected method that will not generally be called directly. It triggers
24305      * a toolbar update by reading the markup state of the current selection in the editor.
24306      */
24307     updateToolbar: function(){
24308
24309         if(!this.editorcore.activated){
24310             this.editor.onFirstFocus(); // is this neeed?
24311             return;
24312         }
24313
24314         var btns = this.buttons; 
24315         var doc = this.editorcore.doc;
24316         btns.get('bold').setActive(doc.queryCommandState('bold'));
24317         btns.get('italic').setActive(doc.queryCommandState('italic'));
24318         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24319         
24320         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24321         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24322         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24323         
24324         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24325         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24326          /*
24327         
24328         var ans = this.editorcore.getAllAncestors();
24329         if (this.formatCombo) {
24330             
24331             
24332             var store = this.formatCombo.store;
24333             this.formatCombo.setValue("");
24334             for (var i =0; i < ans.length;i++) {
24335                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24336                     // select it..
24337                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24338                     break;
24339                 }
24340             }
24341         }
24342         
24343         
24344         
24345         // hides menus... - so this cant be on a menu...
24346         Roo.bootstrap.MenuMgr.hideAll();
24347         */
24348         Roo.bootstrap.MenuMgr.hideAll();
24349         //this.editorsyncValue();
24350     },
24351     onFirstFocus: function() {
24352         this.buttons.each(function(item){
24353            item.enable();
24354         });
24355     },
24356     toggleSourceEdit : function(sourceEditMode){
24357         
24358           
24359         if(sourceEditMode){
24360             Roo.log("disabling buttons");
24361            this.buttons.each( function(item){
24362                 if(item.cmd != 'pencil'){
24363                     item.disable();
24364                 }
24365             });
24366           
24367         }else{
24368             Roo.log("enabling buttons");
24369             if(this.editorcore.initialized){
24370                 this.buttons.each( function(item){
24371                     item.enable();
24372                 });
24373             }
24374             
24375         }
24376         Roo.log("calling toggole on editor");
24377         // tell the editor that it's been pressed..
24378         this.editor.toggleSourceEdit(sourceEditMode);
24379        
24380     }
24381 });
24382
24383
24384
24385
24386
24387 /**
24388  * @class Roo.bootstrap.Table.AbstractSelectionModel
24389  * @extends Roo.util.Observable
24390  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24391  * implemented by descendant classes.  This class should not be directly instantiated.
24392  * @constructor
24393  */
24394 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24395     this.locked = false;
24396     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24397 };
24398
24399
24400 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24401     /** @ignore Called by the grid automatically. Do not call directly. */
24402     init : function(grid){
24403         this.grid = grid;
24404         this.initEvents();
24405     },
24406
24407     /**
24408      * Locks the selections.
24409      */
24410     lock : function(){
24411         this.locked = true;
24412     },
24413
24414     /**
24415      * Unlocks the selections.
24416      */
24417     unlock : function(){
24418         this.locked = false;
24419     },
24420
24421     /**
24422      * Returns true if the selections are locked.
24423      * @return {Boolean}
24424      */
24425     isLocked : function(){
24426         return this.locked;
24427     }
24428 });
24429 /**
24430  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24431  * @class Roo.bootstrap.Table.RowSelectionModel
24432  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24433  * It supports multiple selections and keyboard selection/navigation. 
24434  * @constructor
24435  * @param {Object} config
24436  */
24437
24438 Roo.bootstrap.Table.RowSelectionModel = function(config){
24439     Roo.apply(this, config);
24440     this.selections = new Roo.util.MixedCollection(false, function(o){
24441         return o.id;
24442     });
24443
24444     this.last = false;
24445     this.lastActive = false;
24446
24447     this.addEvents({
24448         /**
24449              * @event selectionchange
24450              * Fires when the selection changes
24451              * @param {SelectionModel} this
24452              */
24453             "selectionchange" : true,
24454         /**
24455              * @event afterselectionchange
24456              * Fires after the selection changes (eg. by key press or clicking)
24457              * @param {SelectionModel} this
24458              */
24459             "afterselectionchange" : true,
24460         /**
24461              * @event beforerowselect
24462              * Fires when a row is selected being selected, return false to cancel.
24463              * @param {SelectionModel} this
24464              * @param {Number} rowIndex The selected index
24465              * @param {Boolean} keepExisting False if other selections will be cleared
24466              */
24467             "beforerowselect" : true,
24468         /**
24469              * @event rowselect
24470              * Fires when a row is selected.
24471              * @param {SelectionModel} this
24472              * @param {Number} rowIndex The selected index
24473              * @param {Roo.data.Record} r The record
24474              */
24475             "rowselect" : true,
24476         /**
24477              * @event rowdeselect
24478              * Fires when a row is deselected.
24479              * @param {SelectionModel} this
24480              * @param {Number} rowIndex The selected index
24481              */
24482         "rowdeselect" : true
24483     });
24484     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24485     this.locked = false;
24486  };
24487
24488 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24489     /**
24490      * @cfg {Boolean} singleSelect
24491      * True to allow selection of only one row at a time (defaults to false)
24492      */
24493     singleSelect : false,
24494
24495     // private
24496     initEvents : function()
24497     {
24498
24499         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24500         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24501         //}else{ // allow click to work like normal
24502          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24503         //}
24504         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24505         this.grid.on("rowclick", this.handleMouseDown, this);
24506         
24507         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24508             "up" : function(e){
24509                 if(!e.shiftKey){
24510                     this.selectPrevious(e.shiftKey);
24511                 }else if(this.last !== false && this.lastActive !== false){
24512                     var last = this.last;
24513                     this.selectRange(this.last,  this.lastActive-1);
24514                     this.grid.getView().focusRow(this.lastActive);
24515                     if(last !== false){
24516                         this.last = last;
24517                     }
24518                 }else{
24519                     this.selectFirstRow();
24520                 }
24521                 this.fireEvent("afterselectionchange", this);
24522             },
24523             "down" : function(e){
24524                 if(!e.shiftKey){
24525                     this.selectNext(e.shiftKey);
24526                 }else if(this.last !== false && this.lastActive !== false){
24527                     var last = this.last;
24528                     this.selectRange(this.last,  this.lastActive+1);
24529                     this.grid.getView().focusRow(this.lastActive);
24530                     if(last !== false){
24531                         this.last = last;
24532                     }
24533                 }else{
24534                     this.selectFirstRow();
24535                 }
24536                 this.fireEvent("afterselectionchange", this);
24537             },
24538             scope: this
24539         });
24540         this.grid.store.on('load', function(){
24541             this.selections.clear();
24542         },this);
24543         /*
24544         var view = this.grid.view;
24545         view.on("refresh", this.onRefresh, this);
24546         view.on("rowupdated", this.onRowUpdated, this);
24547         view.on("rowremoved", this.onRemove, this);
24548         */
24549     },
24550
24551     // private
24552     onRefresh : function()
24553     {
24554         var ds = this.grid.store, i, v = this.grid.view;
24555         var s = this.selections;
24556         s.each(function(r){
24557             if((i = ds.indexOfId(r.id)) != -1){
24558                 v.onRowSelect(i);
24559             }else{
24560                 s.remove(r);
24561             }
24562         });
24563     },
24564
24565     // private
24566     onRemove : function(v, index, r){
24567         this.selections.remove(r);
24568     },
24569
24570     // private
24571     onRowUpdated : function(v, index, r){
24572         if(this.isSelected(r)){
24573             v.onRowSelect(index);
24574         }
24575     },
24576
24577     /**
24578      * Select records.
24579      * @param {Array} records The records to select
24580      * @param {Boolean} keepExisting (optional) True to keep existing selections
24581      */
24582     selectRecords : function(records, keepExisting)
24583     {
24584         if(!keepExisting){
24585             this.clearSelections();
24586         }
24587             var ds = this.grid.store;
24588         for(var i = 0, len = records.length; i < len; i++){
24589             this.selectRow(ds.indexOf(records[i]), true);
24590         }
24591     },
24592
24593     /**
24594      * Gets the number of selected rows.
24595      * @return {Number}
24596      */
24597     getCount : function(){
24598         return this.selections.length;
24599     },
24600
24601     /**
24602      * Selects the first row in the grid.
24603      */
24604     selectFirstRow : function(){
24605         this.selectRow(0);
24606     },
24607
24608     /**
24609      * Select the last row.
24610      * @param {Boolean} keepExisting (optional) True to keep existing selections
24611      */
24612     selectLastRow : function(keepExisting){
24613         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24614         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24615     },
24616
24617     /**
24618      * Selects the row immediately following the last selected row.
24619      * @param {Boolean} keepExisting (optional) True to keep existing selections
24620      */
24621     selectNext : function(keepExisting)
24622     {
24623             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24624             this.selectRow(this.last+1, keepExisting);
24625             this.grid.getView().focusRow(this.last);
24626         }
24627     },
24628
24629     /**
24630      * Selects the row that precedes the last selected row.
24631      * @param {Boolean} keepExisting (optional) True to keep existing selections
24632      */
24633     selectPrevious : function(keepExisting){
24634         if(this.last){
24635             this.selectRow(this.last-1, keepExisting);
24636             this.grid.getView().focusRow(this.last);
24637         }
24638     },
24639
24640     /**
24641      * Returns the selected records
24642      * @return {Array} Array of selected records
24643      */
24644     getSelections : function(){
24645         return [].concat(this.selections.items);
24646     },
24647
24648     /**
24649      * Returns the first selected record.
24650      * @return {Record}
24651      */
24652     getSelected : function(){
24653         return this.selections.itemAt(0);
24654     },
24655
24656
24657     /**
24658      * Clears all selections.
24659      */
24660     clearSelections : function(fast)
24661     {
24662         if(this.locked) {
24663             return;
24664         }
24665         if(fast !== true){
24666                 var ds = this.grid.store;
24667             var s = this.selections;
24668             s.each(function(r){
24669                 this.deselectRow(ds.indexOfId(r.id));
24670             }, this);
24671             s.clear();
24672         }else{
24673             this.selections.clear();
24674         }
24675         this.last = false;
24676     },
24677
24678
24679     /**
24680      * Selects all rows.
24681      */
24682     selectAll : function(){
24683         if(this.locked) {
24684             return;
24685         }
24686         this.selections.clear();
24687         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24688             this.selectRow(i, true);
24689         }
24690     },
24691
24692     /**
24693      * Returns True if there is a selection.
24694      * @return {Boolean}
24695      */
24696     hasSelection : function(){
24697         return this.selections.length > 0;
24698     },
24699
24700     /**
24701      * Returns True if the specified row is selected.
24702      * @param {Number/Record} record The record or index of the record to check
24703      * @return {Boolean}
24704      */
24705     isSelected : function(index){
24706             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24707         return (r && this.selections.key(r.id) ? true : false);
24708     },
24709
24710     /**
24711      * Returns True if the specified record id is selected.
24712      * @param {String} id The id of record to check
24713      * @return {Boolean}
24714      */
24715     isIdSelected : function(id){
24716         return (this.selections.key(id) ? true : false);
24717     },
24718
24719
24720     // private
24721     handleMouseDBClick : function(e, t){
24722         
24723     },
24724     // private
24725     handleMouseDown : function(e, t)
24726     {
24727             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24728         if(this.isLocked() || rowIndex < 0 ){
24729             return;
24730         };
24731         if(e.shiftKey && this.last !== false){
24732             var last = this.last;
24733             this.selectRange(last, rowIndex, e.ctrlKey);
24734             this.last = last; // reset the last
24735             t.focus();
24736     
24737         }else{
24738             var isSelected = this.isSelected(rowIndex);
24739             //Roo.log("select row:" + rowIndex);
24740             if(isSelected){
24741                 this.deselectRow(rowIndex);
24742             } else {
24743                         this.selectRow(rowIndex, true);
24744             }
24745     
24746             /*
24747                 if(e.button !== 0 && isSelected){
24748                 alert('rowIndex 2: ' + rowIndex);
24749                     view.focusRow(rowIndex);
24750                 }else if(e.ctrlKey && isSelected){
24751                     this.deselectRow(rowIndex);
24752                 }else if(!isSelected){
24753                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24754                     view.focusRow(rowIndex);
24755                 }
24756             */
24757         }
24758         this.fireEvent("afterselectionchange", this);
24759     },
24760     // private
24761     handleDragableRowClick :  function(grid, rowIndex, e) 
24762     {
24763         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24764             this.selectRow(rowIndex, false);
24765             grid.view.focusRow(rowIndex);
24766              this.fireEvent("afterselectionchange", this);
24767         }
24768     },
24769     
24770     /**
24771      * Selects multiple rows.
24772      * @param {Array} rows Array of the indexes of the row to select
24773      * @param {Boolean} keepExisting (optional) True to keep existing selections
24774      */
24775     selectRows : function(rows, keepExisting){
24776         if(!keepExisting){
24777             this.clearSelections();
24778         }
24779         for(var i = 0, len = rows.length; i < len; i++){
24780             this.selectRow(rows[i], true);
24781         }
24782     },
24783
24784     /**
24785      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24786      * @param {Number} startRow The index of the first row in the range
24787      * @param {Number} endRow The index of the last row in the range
24788      * @param {Boolean} keepExisting (optional) True to retain existing selections
24789      */
24790     selectRange : function(startRow, endRow, keepExisting){
24791         if(this.locked) {
24792             return;
24793         }
24794         if(!keepExisting){
24795             this.clearSelections();
24796         }
24797         if(startRow <= endRow){
24798             for(var i = startRow; i <= endRow; i++){
24799                 this.selectRow(i, true);
24800             }
24801         }else{
24802             for(var i = startRow; i >= endRow; i--){
24803                 this.selectRow(i, true);
24804             }
24805         }
24806     },
24807
24808     /**
24809      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24810      * @param {Number} startRow The index of the first row in the range
24811      * @param {Number} endRow The index of the last row in the range
24812      */
24813     deselectRange : function(startRow, endRow, preventViewNotify){
24814         if(this.locked) {
24815             return;
24816         }
24817         for(var i = startRow; i <= endRow; i++){
24818             this.deselectRow(i, preventViewNotify);
24819         }
24820     },
24821
24822     /**
24823      * Selects a row.
24824      * @param {Number} row The index of the row to select
24825      * @param {Boolean} keepExisting (optional) True to keep existing selections
24826      */
24827     selectRow : function(index, keepExisting, preventViewNotify)
24828     {
24829             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24830             return;
24831         }
24832         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24833             if(!keepExisting || this.singleSelect){
24834                 this.clearSelections();
24835             }
24836             
24837             var r = this.grid.store.getAt(index);
24838             //console.log('selectRow - record id :' + r.id);
24839             
24840             this.selections.add(r);
24841             this.last = this.lastActive = index;
24842             if(!preventViewNotify){
24843                 var proxy = new Roo.Element(
24844                                 this.grid.getRowDom(index)
24845                 );
24846                 proxy.addClass('bg-info info');
24847             }
24848             this.fireEvent("rowselect", this, index, r);
24849             this.fireEvent("selectionchange", this);
24850         }
24851     },
24852
24853     /**
24854      * Deselects a row.
24855      * @param {Number} row The index of the row to deselect
24856      */
24857     deselectRow : function(index, preventViewNotify)
24858     {
24859         if(this.locked) {
24860             return;
24861         }
24862         if(this.last == index){
24863             this.last = false;
24864         }
24865         if(this.lastActive == index){
24866             this.lastActive = false;
24867         }
24868         
24869         var r = this.grid.store.getAt(index);
24870         if (!r) {
24871             return;
24872         }
24873         
24874         this.selections.remove(r);
24875         //.console.log('deselectRow - record id :' + r.id);
24876         if(!preventViewNotify){
24877         
24878             var proxy = new Roo.Element(
24879                 this.grid.getRowDom(index)
24880             );
24881             proxy.removeClass('bg-info info');
24882         }
24883         this.fireEvent("rowdeselect", this, index);
24884         this.fireEvent("selectionchange", this);
24885     },
24886
24887     // private
24888     restoreLast : function(){
24889         if(this._last){
24890             this.last = this._last;
24891         }
24892     },
24893
24894     // private
24895     acceptsNav : function(row, col, cm){
24896         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24897     },
24898
24899     // private
24900     onEditorKey : function(field, e){
24901         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24902         if(k == e.TAB){
24903             e.stopEvent();
24904             ed.completeEdit();
24905             if(e.shiftKey){
24906                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24907             }else{
24908                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24909             }
24910         }else if(k == e.ENTER && !e.ctrlKey){
24911             e.stopEvent();
24912             ed.completeEdit();
24913             if(e.shiftKey){
24914                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24915             }else{
24916                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24917             }
24918         }else if(k == e.ESC){
24919             ed.cancelEdit();
24920         }
24921         if(newCell){
24922             g.startEditing(newCell[0], newCell[1]);
24923         }
24924     }
24925 });
24926 /*
24927  * Based on:
24928  * Ext JS Library 1.1.1
24929  * Copyright(c) 2006-2007, Ext JS, LLC.
24930  *
24931  * Originally Released Under LGPL - original licence link has changed is not relivant.
24932  *
24933  * Fork - LGPL
24934  * <script type="text/javascript">
24935  */
24936  
24937 /**
24938  * @class Roo.bootstrap.PagingToolbar
24939  * @extends Roo.bootstrap.NavSimplebar
24940  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24941  * @constructor
24942  * Create a new PagingToolbar
24943  * @param {Object} config The config object
24944  * @param {Roo.data.Store} store
24945  */
24946 Roo.bootstrap.PagingToolbar = function(config)
24947 {
24948     // old args format still supported... - xtype is prefered..
24949         // created from xtype...
24950     
24951     this.ds = config.dataSource;
24952     
24953     if (config.store && !this.ds) {
24954         this.store= Roo.factory(config.store, Roo.data);
24955         this.ds = this.store;
24956         this.ds.xmodule = this.xmodule || false;
24957     }
24958     
24959     this.toolbarItems = [];
24960     if (config.items) {
24961         this.toolbarItems = config.items;
24962     }
24963     
24964     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24965     
24966     this.cursor = 0;
24967     
24968     if (this.ds) { 
24969         this.bind(this.ds);
24970     }
24971     
24972     if (Roo.bootstrap.version == 4) {
24973         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24974     } else {
24975         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24976     }
24977     
24978 };
24979
24980 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24981     /**
24982      * @cfg {Roo.data.Store} dataSource
24983      * The underlying data store providing the paged data
24984      */
24985     /**
24986      * @cfg {String/HTMLElement/Element} container
24987      * container The id or element that will contain the toolbar
24988      */
24989     /**
24990      * @cfg {Boolean} displayInfo
24991      * True to display the displayMsg (defaults to false)
24992      */
24993     /**
24994      * @cfg {Number} pageSize
24995      * The number of records to display per page (defaults to 20)
24996      */
24997     pageSize: 20,
24998     /**
24999      * @cfg {String} displayMsg
25000      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25001      */
25002     displayMsg : 'Displaying {0} - {1} of {2}',
25003     /**
25004      * @cfg {String} emptyMsg
25005      * The message to display when no records are found (defaults to "No data to display")
25006      */
25007     emptyMsg : 'No data to display',
25008     /**
25009      * Customizable piece of the default paging text (defaults to "Page")
25010      * @type String
25011      */
25012     beforePageText : "Page",
25013     /**
25014      * Customizable piece of the default paging text (defaults to "of %0")
25015      * @type String
25016      */
25017     afterPageText : "of {0}",
25018     /**
25019      * Customizable piece of the default paging text (defaults to "First Page")
25020      * @type String
25021      */
25022     firstText : "First Page",
25023     /**
25024      * Customizable piece of the default paging text (defaults to "Previous Page")
25025      * @type String
25026      */
25027     prevText : "Previous Page",
25028     /**
25029      * Customizable piece of the default paging text (defaults to "Next Page")
25030      * @type String
25031      */
25032     nextText : "Next Page",
25033     /**
25034      * Customizable piece of the default paging text (defaults to "Last Page")
25035      * @type String
25036      */
25037     lastText : "Last Page",
25038     /**
25039      * Customizable piece of the default paging text (defaults to "Refresh")
25040      * @type String
25041      */
25042     refreshText : "Refresh",
25043
25044     buttons : false,
25045     // private
25046     onRender : function(ct, position) 
25047     {
25048         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25049         this.navgroup.parentId = this.id;
25050         this.navgroup.onRender(this.el, null);
25051         // add the buttons to the navgroup
25052         
25053         if(this.displayInfo){
25054             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25055             this.displayEl = this.el.select('.x-paging-info', true).first();
25056 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25057 //            this.displayEl = navel.el.select('span',true).first();
25058         }
25059         
25060         var _this = this;
25061         
25062         if(this.buttons){
25063             Roo.each(_this.buttons, function(e){ // this might need to use render????
25064                Roo.factory(e).render(_this.el);
25065             });
25066         }
25067             
25068         Roo.each(_this.toolbarItems, function(e) {
25069             _this.navgroup.addItem(e);
25070         });
25071         
25072         
25073         this.first = this.navgroup.addItem({
25074             tooltip: this.firstText,
25075             cls: "prev btn-outline-secondary",
25076             html : ' <i class="fa fa-step-backward"></i>',
25077             disabled: true,
25078             preventDefault: true,
25079             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25080         });
25081         
25082         this.prev =  this.navgroup.addItem({
25083             tooltip: this.prevText,
25084             cls: "prev btn-outline-secondary",
25085             html : ' <i class="fa fa-backward"></i>',
25086             disabled: true,
25087             preventDefault: true,
25088             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25089         });
25090     //this.addSeparator();
25091         
25092         
25093         var field = this.navgroup.addItem( {
25094             tagtype : 'span',
25095             cls : 'x-paging-position  btn-outline-secondary',
25096              disabled: true,
25097             html : this.beforePageText  +
25098                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25099                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25100          } ); //?? escaped?
25101         
25102         this.field = field.el.select('input', true).first();
25103         this.field.on("keydown", this.onPagingKeydown, this);
25104         this.field.on("focus", function(){this.dom.select();});
25105     
25106     
25107         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25108         //this.field.setHeight(18);
25109         //this.addSeparator();
25110         this.next = this.navgroup.addItem({
25111             tooltip: this.nextText,
25112             cls: "next btn-outline-secondary",
25113             html : ' <i class="fa fa-forward"></i>',
25114             disabled: true,
25115             preventDefault: true,
25116             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25117         });
25118         this.last = this.navgroup.addItem({
25119             tooltip: this.lastText,
25120             html : ' <i class="fa fa-step-forward"></i>',
25121             cls: "next btn-outline-secondary",
25122             disabled: true,
25123             preventDefault: true,
25124             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25125         });
25126     //this.addSeparator();
25127         this.loading = this.navgroup.addItem({
25128             tooltip: this.refreshText,
25129             cls: "btn-outline-secondary",
25130             html : ' <i class="fa fa-refresh"></i>',
25131             preventDefault: true,
25132             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25133         });
25134         
25135     },
25136
25137     // private
25138     updateInfo : function(){
25139         if(this.displayEl){
25140             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25141             var msg = count == 0 ?
25142                 this.emptyMsg :
25143                 String.format(
25144                     this.displayMsg,
25145                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25146                 );
25147             this.displayEl.update(msg);
25148         }
25149     },
25150
25151     // private
25152     onLoad : function(ds, r, o)
25153     {
25154         this.cursor = o.params.start ? o.params.start : 0;
25155         
25156         var d = this.getPageData(),
25157             ap = d.activePage,
25158             ps = d.pages;
25159         
25160         
25161         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25162         this.field.dom.value = ap;
25163         this.first.setDisabled(ap == 1);
25164         this.prev.setDisabled(ap == 1);
25165         this.next.setDisabled(ap == ps);
25166         this.last.setDisabled(ap == ps);
25167         this.loading.enable();
25168         this.updateInfo();
25169     },
25170
25171     // private
25172     getPageData : function(){
25173         var total = this.ds.getTotalCount();
25174         return {
25175             total : total,
25176             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25177             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25178         };
25179     },
25180
25181     // private
25182     onLoadError : function(){
25183         this.loading.enable();
25184     },
25185
25186     // private
25187     onPagingKeydown : function(e){
25188         var k = e.getKey();
25189         var d = this.getPageData();
25190         if(k == e.RETURN){
25191             var v = this.field.dom.value, pageNum;
25192             if(!v || isNaN(pageNum = parseInt(v, 10))){
25193                 this.field.dom.value = d.activePage;
25194                 return;
25195             }
25196             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25197             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25198             e.stopEvent();
25199         }
25200         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))
25201         {
25202           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25203           this.field.dom.value = pageNum;
25204           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25205           e.stopEvent();
25206         }
25207         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25208         {
25209           var v = this.field.dom.value, pageNum; 
25210           var increment = (e.shiftKey) ? 10 : 1;
25211           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25212                 increment *= -1;
25213           }
25214           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25215             this.field.dom.value = d.activePage;
25216             return;
25217           }
25218           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25219           {
25220             this.field.dom.value = parseInt(v, 10) + increment;
25221             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25222             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25223           }
25224           e.stopEvent();
25225         }
25226     },
25227
25228     // private
25229     beforeLoad : function(){
25230         if(this.loading){
25231             this.loading.disable();
25232         }
25233     },
25234
25235     // private
25236     onClick : function(which){
25237         
25238         var ds = this.ds;
25239         if (!ds) {
25240             return;
25241         }
25242         
25243         switch(which){
25244             case "first":
25245                 ds.load({params:{start: 0, limit: this.pageSize}});
25246             break;
25247             case "prev":
25248                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25249             break;
25250             case "next":
25251                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25252             break;
25253             case "last":
25254                 var total = ds.getTotalCount();
25255                 var extra = total % this.pageSize;
25256                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25257                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25258             break;
25259             case "refresh":
25260                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25261             break;
25262         }
25263     },
25264
25265     /**
25266      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25267      * @param {Roo.data.Store} store The data store to unbind
25268      */
25269     unbind : function(ds){
25270         ds.un("beforeload", this.beforeLoad, this);
25271         ds.un("load", this.onLoad, this);
25272         ds.un("loadexception", this.onLoadError, this);
25273         ds.un("remove", this.updateInfo, this);
25274         ds.un("add", this.updateInfo, this);
25275         this.ds = undefined;
25276     },
25277
25278     /**
25279      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25280      * @param {Roo.data.Store} store The data store to bind
25281      */
25282     bind : function(ds){
25283         ds.on("beforeload", this.beforeLoad, this);
25284         ds.on("load", this.onLoad, this);
25285         ds.on("loadexception", this.onLoadError, this);
25286         ds.on("remove", this.updateInfo, this);
25287         ds.on("add", this.updateInfo, this);
25288         this.ds = ds;
25289     }
25290 });/*
25291  * - LGPL
25292  *
25293  * element
25294  * 
25295  */
25296
25297 /**
25298  * @class Roo.bootstrap.MessageBar
25299  * @extends Roo.bootstrap.Component
25300  * Bootstrap MessageBar class
25301  * @cfg {String} html contents of the MessageBar
25302  * @cfg {String} weight (info | success | warning | danger) default info
25303  * @cfg {String} beforeClass insert the bar before the given class
25304  * @cfg {Boolean} closable (true | false) default false
25305  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25306  * 
25307  * @constructor
25308  * Create a new Element
25309  * @param {Object} config The config object
25310  */
25311
25312 Roo.bootstrap.MessageBar = function(config){
25313     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25314 };
25315
25316 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25317     
25318     html: '',
25319     weight: 'info',
25320     closable: false,
25321     fixed: false,
25322     beforeClass: 'bootstrap-sticky-wrap',
25323     
25324     getAutoCreate : function(){
25325         
25326         var cfg = {
25327             tag: 'div',
25328             cls: 'alert alert-dismissable alert-' + this.weight,
25329             cn: [
25330                 {
25331                     tag: 'span',
25332                     cls: 'message',
25333                     html: this.html || ''
25334                 }
25335             ]
25336         };
25337         
25338         if(this.fixed){
25339             cfg.cls += ' alert-messages-fixed';
25340         }
25341         
25342         if(this.closable){
25343             cfg.cn.push({
25344                 tag: 'button',
25345                 cls: 'close',
25346                 html: 'x'
25347             });
25348         }
25349         
25350         return cfg;
25351     },
25352     
25353     onRender : function(ct, position)
25354     {
25355         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25356         
25357         if(!this.el){
25358             var cfg = Roo.apply({},  this.getAutoCreate());
25359             cfg.id = Roo.id();
25360             
25361             if (this.cls) {
25362                 cfg.cls += ' ' + this.cls;
25363             }
25364             if (this.style) {
25365                 cfg.style = this.style;
25366             }
25367             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25368             
25369             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25370         }
25371         
25372         this.el.select('>button.close').on('click', this.hide, this);
25373         
25374     },
25375     
25376     show : function()
25377     {
25378         if (!this.rendered) {
25379             this.render();
25380         }
25381         
25382         this.el.show();
25383         
25384         this.fireEvent('show', this);
25385         
25386     },
25387     
25388     hide : function()
25389     {
25390         if (!this.rendered) {
25391             this.render();
25392         }
25393         
25394         this.el.hide();
25395         
25396         this.fireEvent('hide', this);
25397     },
25398     
25399     update : function()
25400     {
25401 //        var e = this.el.dom.firstChild;
25402 //        
25403 //        if(this.closable){
25404 //            e = e.nextSibling;
25405 //        }
25406 //        
25407 //        e.data = this.html || '';
25408
25409         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25410     }
25411    
25412 });
25413
25414  
25415
25416      /*
25417  * - LGPL
25418  *
25419  * Graph
25420  * 
25421  */
25422
25423
25424 /**
25425  * @class Roo.bootstrap.Graph
25426  * @extends Roo.bootstrap.Component
25427  * Bootstrap Graph class
25428 > Prameters
25429  -sm {number} sm 4
25430  -md {number} md 5
25431  @cfg {String} graphtype  bar | vbar | pie
25432  @cfg {number} g_x coodinator | centre x (pie)
25433  @cfg {number} g_y coodinator | centre y (pie)
25434  @cfg {number} g_r radius (pie)
25435  @cfg {number} g_height height of the chart (respected by all elements in the set)
25436  @cfg {number} g_width width of the chart (respected by all elements in the set)
25437  @cfg {Object} title The title of the chart
25438     
25439  -{Array}  values
25440  -opts (object) options for the chart 
25441      o {
25442      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25443      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25444      o vgutter (number)
25445      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.
25446      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25447      o to
25448      o stretch (boolean)
25449      o }
25450  -opts (object) options for the pie
25451      o{
25452      o cut
25453      o startAngle (number)
25454      o endAngle (number)
25455      } 
25456  *
25457  * @constructor
25458  * Create a new Input
25459  * @param {Object} config The config object
25460  */
25461
25462 Roo.bootstrap.Graph = function(config){
25463     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25464     
25465     this.addEvents({
25466         // img events
25467         /**
25468          * @event click
25469          * The img click event for the img.
25470          * @param {Roo.EventObject} e
25471          */
25472         "click" : true
25473     });
25474 };
25475
25476 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25477     
25478     sm: 4,
25479     md: 5,
25480     graphtype: 'bar',
25481     g_height: 250,
25482     g_width: 400,
25483     g_x: 50,
25484     g_y: 50,
25485     g_r: 30,
25486     opts:{
25487         //g_colors: this.colors,
25488         g_type: 'soft',
25489         g_gutter: '20%'
25490
25491     },
25492     title : false,
25493
25494     getAutoCreate : function(){
25495         
25496         var cfg = {
25497             tag: 'div',
25498             html : null
25499         };
25500         
25501         
25502         return  cfg;
25503     },
25504
25505     onRender : function(ct,position){
25506         
25507         
25508         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25509         
25510         if (typeof(Raphael) == 'undefined') {
25511             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25512             return;
25513         }
25514         
25515         this.raphael = Raphael(this.el.dom);
25516         
25517                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25518                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25519                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25520                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25521                 /*
25522                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25523                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25524                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25525                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25526                 
25527                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25528                 r.barchart(330, 10, 300, 220, data1);
25529                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25530                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25531                 */
25532                 
25533                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25534                 // r.barchart(30, 30, 560, 250,  xdata, {
25535                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25536                 //     axis : "0 0 1 1",
25537                 //     axisxlabels :  xdata
25538                 //     //yvalues : cols,
25539                    
25540                 // });
25541 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25542 //        
25543 //        this.load(null,xdata,{
25544 //                axis : "0 0 1 1",
25545 //                axisxlabels :  xdata
25546 //                });
25547
25548     },
25549
25550     load : function(graphtype,xdata,opts)
25551     {
25552         this.raphael.clear();
25553         if(!graphtype) {
25554             graphtype = this.graphtype;
25555         }
25556         if(!opts){
25557             opts = this.opts;
25558         }
25559         var r = this.raphael,
25560             fin = function () {
25561                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25562             },
25563             fout = function () {
25564                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25565             },
25566             pfin = function() {
25567                 this.sector.stop();
25568                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25569
25570                 if (this.label) {
25571                     this.label[0].stop();
25572                     this.label[0].attr({ r: 7.5 });
25573                     this.label[1].attr({ "font-weight": 800 });
25574                 }
25575             },
25576             pfout = function() {
25577                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25578
25579                 if (this.label) {
25580                     this.label[0].animate({ r: 5 }, 500, "bounce");
25581                     this.label[1].attr({ "font-weight": 400 });
25582                 }
25583             };
25584
25585         switch(graphtype){
25586             case 'bar':
25587                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25588                 break;
25589             case 'hbar':
25590                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25591                 break;
25592             case 'pie':
25593 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25594 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25595 //            
25596                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25597                 
25598                 break;
25599
25600         }
25601         
25602         if(this.title){
25603             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25604         }
25605         
25606     },
25607     
25608     setTitle: function(o)
25609     {
25610         this.title = o;
25611     },
25612     
25613     initEvents: function() {
25614         
25615         if(!this.href){
25616             this.el.on('click', this.onClick, this);
25617         }
25618     },
25619     
25620     onClick : function(e)
25621     {
25622         Roo.log('img onclick');
25623         this.fireEvent('click', this, e);
25624     }
25625    
25626 });
25627
25628  
25629 /*
25630  * - LGPL
25631  *
25632  * numberBox
25633  * 
25634  */
25635 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25636
25637 /**
25638  * @class Roo.bootstrap.dash.NumberBox
25639  * @extends Roo.bootstrap.Component
25640  * Bootstrap NumberBox class
25641  * @cfg {String} headline Box headline
25642  * @cfg {String} content Box content
25643  * @cfg {String} icon Box icon
25644  * @cfg {String} footer Footer text
25645  * @cfg {String} fhref Footer href
25646  * 
25647  * @constructor
25648  * Create a new NumberBox
25649  * @param {Object} config The config object
25650  */
25651
25652
25653 Roo.bootstrap.dash.NumberBox = function(config){
25654     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25655     
25656 };
25657
25658 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25659     
25660     headline : '',
25661     content : '',
25662     icon : '',
25663     footer : '',
25664     fhref : '',
25665     ficon : '',
25666     
25667     getAutoCreate : function(){
25668         
25669         var cfg = {
25670             tag : 'div',
25671             cls : 'small-box ',
25672             cn : [
25673                 {
25674                     tag : 'div',
25675                     cls : 'inner',
25676                     cn :[
25677                         {
25678                             tag : 'h3',
25679                             cls : 'roo-headline',
25680                             html : this.headline
25681                         },
25682                         {
25683                             tag : 'p',
25684                             cls : 'roo-content',
25685                             html : this.content
25686                         }
25687                     ]
25688                 }
25689             ]
25690         };
25691         
25692         if(this.icon){
25693             cfg.cn.push({
25694                 tag : 'div',
25695                 cls : 'icon',
25696                 cn :[
25697                     {
25698                         tag : 'i',
25699                         cls : 'ion ' + this.icon
25700                     }
25701                 ]
25702             });
25703         }
25704         
25705         if(this.footer){
25706             var footer = {
25707                 tag : 'a',
25708                 cls : 'small-box-footer',
25709                 href : this.fhref || '#',
25710                 html : this.footer
25711             };
25712             
25713             cfg.cn.push(footer);
25714             
25715         }
25716         
25717         return  cfg;
25718     },
25719
25720     onRender : function(ct,position){
25721         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25722
25723
25724        
25725                 
25726     },
25727
25728     setHeadline: function (value)
25729     {
25730         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25731     },
25732     
25733     setFooter: function (value, href)
25734     {
25735         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25736         
25737         if(href){
25738             this.el.select('a.small-box-footer',true).first().attr('href', href);
25739         }
25740         
25741     },
25742
25743     setContent: function (value)
25744     {
25745         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25746     },
25747
25748     initEvents: function() 
25749     {   
25750         
25751     }
25752     
25753 });
25754
25755  
25756 /*
25757  * - LGPL
25758  *
25759  * TabBox
25760  * 
25761  */
25762 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25763
25764 /**
25765  * @class Roo.bootstrap.dash.TabBox
25766  * @extends Roo.bootstrap.Component
25767  * Bootstrap TabBox class
25768  * @cfg {String} title Title of the TabBox
25769  * @cfg {String} icon Icon of the TabBox
25770  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25771  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25772  * 
25773  * @constructor
25774  * Create a new TabBox
25775  * @param {Object} config The config object
25776  */
25777
25778
25779 Roo.bootstrap.dash.TabBox = function(config){
25780     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25781     this.addEvents({
25782         // raw events
25783         /**
25784          * @event addpane
25785          * When a pane is added
25786          * @param {Roo.bootstrap.dash.TabPane} pane
25787          */
25788         "addpane" : true,
25789         /**
25790          * @event activatepane
25791          * When a pane is activated
25792          * @param {Roo.bootstrap.dash.TabPane} pane
25793          */
25794         "activatepane" : true
25795         
25796          
25797     });
25798     
25799     this.panes = [];
25800 };
25801
25802 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25803
25804     title : '',
25805     icon : false,
25806     showtabs : true,
25807     tabScrollable : false,
25808     
25809     getChildContainer : function()
25810     {
25811         return this.el.select('.tab-content', true).first();
25812     },
25813     
25814     getAutoCreate : function(){
25815         
25816         var header = {
25817             tag: 'li',
25818             cls: 'pull-left header',
25819             html: this.title,
25820             cn : []
25821         };
25822         
25823         if(this.icon){
25824             header.cn.push({
25825                 tag: 'i',
25826                 cls: 'fa ' + this.icon
25827             });
25828         }
25829         
25830         var h = {
25831             tag: 'ul',
25832             cls: 'nav nav-tabs pull-right',
25833             cn: [
25834                 header
25835             ]
25836         };
25837         
25838         if(this.tabScrollable){
25839             h = {
25840                 tag: 'div',
25841                 cls: 'tab-header',
25842                 cn: [
25843                     {
25844                         tag: 'ul',
25845                         cls: 'nav nav-tabs pull-right',
25846                         cn: [
25847                             header
25848                         ]
25849                     }
25850                 ]
25851             };
25852         }
25853         
25854         var cfg = {
25855             tag: 'div',
25856             cls: 'nav-tabs-custom',
25857             cn: [
25858                 h,
25859                 {
25860                     tag: 'div',
25861                     cls: 'tab-content no-padding',
25862                     cn: []
25863                 }
25864             ]
25865         };
25866
25867         return  cfg;
25868     },
25869     initEvents : function()
25870     {
25871         //Roo.log('add add pane handler');
25872         this.on('addpane', this.onAddPane, this);
25873     },
25874      /**
25875      * Updates the box title
25876      * @param {String} html to set the title to.
25877      */
25878     setTitle : function(value)
25879     {
25880         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25881     },
25882     onAddPane : function(pane)
25883     {
25884         this.panes.push(pane);
25885         //Roo.log('addpane');
25886         //Roo.log(pane);
25887         // tabs are rendere left to right..
25888         if(!this.showtabs){
25889             return;
25890         }
25891         
25892         var ctr = this.el.select('.nav-tabs', true).first();
25893          
25894          
25895         var existing = ctr.select('.nav-tab',true);
25896         var qty = existing.getCount();;
25897         
25898         
25899         var tab = ctr.createChild({
25900             tag : 'li',
25901             cls : 'nav-tab' + (qty ? '' : ' active'),
25902             cn : [
25903                 {
25904                     tag : 'a',
25905                     href:'#',
25906                     html : pane.title
25907                 }
25908             ]
25909         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25910         pane.tab = tab;
25911         
25912         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25913         if (!qty) {
25914             pane.el.addClass('active');
25915         }
25916         
25917                 
25918     },
25919     onTabClick : function(ev,un,ob,pane)
25920     {
25921         //Roo.log('tab - prev default');
25922         ev.preventDefault();
25923         
25924         
25925         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25926         pane.tab.addClass('active');
25927         //Roo.log(pane.title);
25928         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25929         // technically we should have a deactivate event.. but maybe add later.
25930         // and it should not de-activate the selected tab...
25931         this.fireEvent('activatepane', pane);
25932         pane.el.addClass('active');
25933         pane.fireEvent('activate');
25934         
25935         
25936     },
25937     
25938     getActivePane : function()
25939     {
25940         var r = false;
25941         Roo.each(this.panes, function(p) {
25942             if(p.el.hasClass('active')){
25943                 r = p;
25944                 return false;
25945             }
25946             
25947             return;
25948         });
25949         
25950         return r;
25951     }
25952     
25953     
25954 });
25955
25956  
25957 /*
25958  * - LGPL
25959  *
25960  * Tab pane
25961  * 
25962  */
25963 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25964 /**
25965  * @class Roo.bootstrap.TabPane
25966  * @extends Roo.bootstrap.Component
25967  * Bootstrap TabPane class
25968  * @cfg {Boolean} active (false | true) Default false
25969  * @cfg {String} title title of panel
25970
25971  * 
25972  * @constructor
25973  * Create a new TabPane
25974  * @param {Object} config The config object
25975  */
25976
25977 Roo.bootstrap.dash.TabPane = function(config){
25978     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25979     
25980     this.addEvents({
25981         // raw events
25982         /**
25983          * @event activate
25984          * When a pane is activated
25985          * @param {Roo.bootstrap.dash.TabPane} pane
25986          */
25987         "activate" : true
25988          
25989     });
25990 };
25991
25992 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25993     
25994     active : false,
25995     title : '',
25996     
25997     // the tabBox that this is attached to.
25998     tab : false,
25999      
26000     getAutoCreate : function() 
26001     {
26002         var cfg = {
26003             tag: 'div',
26004             cls: 'tab-pane'
26005         };
26006         
26007         if(this.active){
26008             cfg.cls += ' active';
26009         }
26010         
26011         return cfg;
26012     },
26013     initEvents  : function()
26014     {
26015         //Roo.log('trigger add pane handler');
26016         this.parent().fireEvent('addpane', this)
26017     },
26018     
26019      /**
26020      * Updates the tab title 
26021      * @param {String} html to set the title to.
26022      */
26023     setTitle: function(str)
26024     {
26025         if (!this.tab) {
26026             return;
26027         }
26028         this.title = str;
26029         this.tab.select('a', true).first().dom.innerHTML = str;
26030         
26031     }
26032     
26033     
26034     
26035 });
26036
26037  
26038
26039
26040  /*
26041  * - LGPL
26042  *
26043  * menu
26044  * 
26045  */
26046 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26047
26048 /**
26049  * @class Roo.bootstrap.menu.Menu
26050  * @extends Roo.bootstrap.Component
26051  * Bootstrap Menu class - container for Menu
26052  * @cfg {String} html Text of the menu
26053  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26054  * @cfg {String} icon Font awesome icon
26055  * @cfg {String} pos Menu align to (top | bottom) default bottom
26056  * 
26057  * 
26058  * @constructor
26059  * Create a new Menu
26060  * @param {Object} config The config object
26061  */
26062
26063
26064 Roo.bootstrap.menu.Menu = function(config){
26065     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26066     
26067     this.addEvents({
26068         /**
26069          * @event beforeshow
26070          * Fires before this menu is displayed
26071          * @param {Roo.bootstrap.menu.Menu} this
26072          */
26073         beforeshow : true,
26074         /**
26075          * @event beforehide
26076          * Fires before this menu is hidden
26077          * @param {Roo.bootstrap.menu.Menu} this
26078          */
26079         beforehide : true,
26080         /**
26081          * @event show
26082          * Fires after this menu is displayed
26083          * @param {Roo.bootstrap.menu.Menu} this
26084          */
26085         show : true,
26086         /**
26087          * @event hide
26088          * Fires after this menu is hidden
26089          * @param {Roo.bootstrap.menu.Menu} this
26090          */
26091         hide : true,
26092         /**
26093          * @event click
26094          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26095          * @param {Roo.bootstrap.menu.Menu} this
26096          * @param {Roo.EventObject} e
26097          */
26098         click : true
26099     });
26100     
26101 };
26102
26103 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26104     
26105     submenu : false,
26106     html : '',
26107     weight : 'default',
26108     icon : false,
26109     pos : 'bottom',
26110     
26111     
26112     getChildContainer : function() {
26113         if(this.isSubMenu){
26114             return this.el;
26115         }
26116         
26117         return this.el.select('ul.dropdown-menu', true).first();  
26118     },
26119     
26120     getAutoCreate : function()
26121     {
26122         var text = [
26123             {
26124                 tag : 'span',
26125                 cls : 'roo-menu-text',
26126                 html : this.html
26127             }
26128         ];
26129         
26130         if(this.icon){
26131             text.unshift({
26132                 tag : 'i',
26133                 cls : 'fa ' + this.icon
26134             })
26135         }
26136         
26137         
26138         var cfg = {
26139             tag : 'div',
26140             cls : 'btn-group',
26141             cn : [
26142                 {
26143                     tag : 'button',
26144                     cls : 'dropdown-button btn btn-' + this.weight,
26145                     cn : text
26146                 },
26147                 {
26148                     tag : 'button',
26149                     cls : 'dropdown-toggle btn btn-' + this.weight,
26150                     cn : [
26151                         {
26152                             tag : 'span',
26153                             cls : 'caret'
26154                         }
26155                     ]
26156                 },
26157                 {
26158                     tag : 'ul',
26159                     cls : 'dropdown-menu'
26160                 }
26161             ]
26162             
26163         };
26164         
26165         if(this.pos == 'top'){
26166             cfg.cls += ' dropup';
26167         }
26168         
26169         if(this.isSubMenu){
26170             cfg = {
26171                 tag : 'ul',
26172                 cls : 'dropdown-menu'
26173             }
26174         }
26175         
26176         return cfg;
26177     },
26178     
26179     onRender : function(ct, position)
26180     {
26181         this.isSubMenu = ct.hasClass('dropdown-submenu');
26182         
26183         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26184     },
26185     
26186     initEvents : function() 
26187     {
26188         if(this.isSubMenu){
26189             return;
26190         }
26191         
26192         this.hidden = true;
26193         
26194         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26195         this.triggerEl.on('click', this.onTriggerPress, this);
26196         
26197         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26198         this.buttonEl.on('click', this.onClick, this);
26199         
26200     },
26201     
26202     list : function()
26203     {
26204         if(this.isSubMenu){
26205             return this.el;
26206         }
26207         
26208         return this.el.select('ul.dropdown-menu', true).first();
26209     },
26210     
26211     onClick : function(e)
26212     {
26213         this.fireEvent("click", this, e);
26214     },
26215     
26216     onTriggerPress  : function(e)
26217     {   
26218         if (this.isVisible()) {
26219             this.hide();
26220         } else {
26221             this.show();
26222         }
26223     },
26224     
26225     isVisible : function(){
26226         return !this.hidden;
26227     },
26228     
26229     show : function()
26230     {
26231         this.fireEvent("beforeshow", this);
26232         
26233         this.hidden = false;
26234         this.el.addClass('open');
26235         
26236         Roo.get(document).on("mouseup", this.onMouseUp, this);
26237         
26238         this.fireEvent("show", this);
26239         
26240         
26241     },
26242     
26243     hide : function()
26244     {
26245         this.fireEvent("beforehide", this);
26246         
26247         this.hidden = true;
26248         this.el.removeClass('open');
26249         
26250         Roo.get(document).un("mouseup", this.onMouseUp);
26251         
26252         this.fireEvent("hide", this);
26253     },
26254     
26255     onMouseUp : function()
26256     {
26257         this.hide();
26258     }
26259     
26260 });
26261
26262  
26263  /*
26264  * - LGPL
26265  *
26266  * menu item
26267  * 
26268  */
26269 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26270
26271 /**
26272  * @class Roo.bootstrap.menu.Item
26273  * @extends Roo.bootstrap.Component
26274  * Bootstrap MenuItem class
26275  * @cfg {Boolean} submenu (true | false) default false
26276  * @cfg {String} html text of the item
26277  * @cfg {String} href the link
26278  * @cfg {Boolean} disable (true | false) default false
26279  * @cfg {Boolean} preventDefault (true | false) default true
26280  * @cfg {String} icon Font awesome icon
26281  * @cfg {String} pos Submenu align to (left | right) default right 
26282  * 
26283  * 
26284  * @constructor
26285  * Create a new Item
26286  * @param {Object} config The config object
26287  */
26288
26289
26290 Roo.bootstrap.menu.Item = function(config){
26291     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26292     this.addEvents({
26293         /**
26294          * @event mouseover
26295          * Fires when the mouse is hovering over this menu
26296          * @param {Roo.bootstrap.menu.Item} this
26297          * @param {Roo.EventObject} e
26298          */
26299         mouseover : true,
26300         /**
26301          * @event mouseout
26302          * Fires when the mouse exits this menu
26303          * @param {Roo.bootstrap.menu.Item} this
26304          * @param {Roo.EventObject} e
26305          */
26306         mouseout : true,
26307         // raw events
26308         /**
26309          * @event click
26310          * The raw click event for the entire grid.
26311          * @param {Roo.EventObject} e
26312          */
26313         click : true
26314     });
26315 };
26316
26317 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26318     
26319     submenu : false,
26320     href : '',
26321     html : '',
26322     preventDefault: true,
26323     disable : false,
26324     icon : false,
26325     pos : 'right',
26326     
26327     getAutoCreate : function()
26328     {
26329         var text = [
26330             {
26331                 tag : 'span',
26332                 cls : 'roo-menu-item-text',
26333                 html : this.html
26334             }
26335         ];
26336         
26337         if(this.icon){
26338             text.unshift({
26339                 tag : 'i',
26340                 cls : 'fa ' + this.icon
26341             })
26342         }
26343         
26344         var cfg = {
26345             tag : 'li',
26346             cn : [
26347                 {
26348                     tag : 'a',
26349                     href : this.href || '#',
26350                     cn : text
26351                 }
26352             ]
26353         };
26354         
26355         if(this.disable){
26356             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26357         }
26358         
26359         if(this.submenu){
26360             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26361             
26362             if(this.pos == 'left'){
26363                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26364             }
26365         }
26366         
26367         return cfg;
26368     },
26369     
26370     initEvents : function() 
26371     {
26372         this.el.on('mouseover', this.onMouseOver, this);
26373         this.el.on('mouseout', this.onMouseOut, this);
26374         
26375         this.el.select('a', true).first().on('click', this.onClick, this);
26376         
26377     },
26378     
26379     onClick : function(e)
26380     {
26381         if(this.preventDefault){
26382             e.preventDefault();
26383         }
26384         
26385         this.fireEvent("click", this, e);
26386     },
26387     
26388     onMouseOver : function(e)
26389     {
26390         if(this.submenu && this.pos == 'left'){
26391             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26392         }
26393         
26394         this.fireEvent("mouseover", this, e);
26395     },
26396     
26397     onMouseOut : function(e)
26398     {
26399         this.fireEvent("mouseout", this, e);
26400     }
26401 });
26402
26403  
26404
26405  /*
26406  * - LGPL
26407  *
26408  * menu separator
26409  * 
26410  */
26411 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26412
26413 /**
26414  * @class Roo.bootstrap.menu.Separator
26415  * @extends Roo.bootstrap.Component
26416  * Bootstrap Separator class
26417  * 
26418  * @constructor
26419  * Create a new Separator
26420  * @param {Object} config The config object
26421  */
26422
26423
26424 Roo.bootstrap.menu.Separator = function(config){
26425     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26426 };
26427
26428 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26429     
26430     getAutoCreate : function(){
26431         var cfg = {
26432             tag : 'li',
26433             cls: 'divider'
26434         };
26435         
26436         return cfg;
26437     }
26438    
26439 });
26440
26441  
26442
26443  /*
26444  * - LGPL
26445  *
26446  * Tooltip
26447  * 
26448  */
26449
26450 /**
26451  * @class Roo.bootstrap.Tooltip
26452  * Bootstrap Tooltip class
26453  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26454  * to determine which dom element triggers the tooltip.
26455  * 
26456  * It needs to add support for additional attributes like tooltip-position
26457  * 
26458  * @constructor
26459  * Create a new Toolti
26460  * @param {Object} config The config object
26461  */
26462
26463 Roo.bootstrap.Tooltip = function(config){
26464     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26465     
26466     this.alignment = Roo.bootstrap.Tooltip.alignment;
26467     
26468     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26469         this.alignment = config.alignment;
26470     }
26471     
26472 };
26473
26474 Roo.apply(Roo.bootstrap.Tooltip, {
26475     /**
26476      * @function init initialize tooltip monitoring.
26477      * @static
26478      */
26479     currentEl : false,
26480     currentTip : false,
26481     currentRegion : false,
26482     
26483     //  init : delay?
26484     
26485     init : function()
26486     {
26487         Roo.get(document).on('mouseover', this.enter ,this);
26488         Roo.get(document).on('mouseout', this.leave, this);
26489          
26490         
26491         this.currentTip = new Roo.bootstrap.Tooltip();
26492     },
26493     
26494     enter : function(ev)
26495     {
26496         var dom = ev.getTarget();
26497         
26498         //Roo.log(['enter',dom]);
26499         var el = Roo.fly(dom);
26500         if (this.currentEl) {
26501             //Roo.log(dom);
26502             //Roo.log(this.currentEl);
26503             //Roo.log(this.currentEl.contains(dom));
26504             if (this.currentEl == el) {
26505                 return;
26506             }
26507             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26508                 return;
26509             }
26510
26511         }
26512         
26513         if (this.currentTip.el) {
26514             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26515         }    
26516         //Roo.log(ev);
26517         
26518         if(!el || el.dom == document){
26519             return;
26520         }
26521         
26522         var bindEl = el;
26523         
26524         // you can not look for children, as if el is the body.. then everythign is the child..
26525         if (!el.attr('tooltip')) { //
26526             if (!el.select("[tooltip]").elements.length) {
26527                 return;
26528             }
26529             // is the mouse over this child...?
26530             bindEl = el.select("[tooltip]").first();
26531             var xy = ev.getXY();
26532             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26533                 //Roo.log("not in region.");
26534                 return;
26535             }
26536             //Roo.log("child element over..");
26537             
26538         }
26539         this.currentEl = bindEl;
26540         this.currentTip.bind(bindEl);
26541         this.currentRegion = Roo.lib.Region.getRegion(dom);
26542         this.currentTip.enter();
26543         
26544     },
26545     leave : function(ev)
26546     {
26547         var dom = ev.getTarget();
26548         //Roo.log(['leave',dom]);
26549         if (!this.currentEl) {
26550             return;
26551         }
26552         
26553         
26554         if (dom != this.currentEl.dom) {
26555             return;
26556         }
26557         var xy = ev.getXY();
26558         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26559             return;
26560         }
26561         // only activate leave if mouse cursor is outside... bounding box..
26562         
26563         
26564         
26565         
26566         if (this.currentTip) {
26567             this.currentTip.leave();
26568         }
26569         //Roo.log('clear currentEl');
26570         this.currentEl = false;
26571         
26572         
26573     },
26574     alignment : {
26575         'left' : ['r-l', [-2,0], 'right'],
26576         'right' : ['l-r', [2,0], 'left'],
26577         'bottom' : ['t-b', [0,2], 'top'],
26578         'top' : [ 'b-t', [0,-2], 'bottom']
26579     }
26580     
26581 });
26582
26583
26584 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26585     
26586     
26587     bindEl : false,
26588     
26589     delay : null, // can be { show : 300 , hide: 500}
26590     
26591     timeout : null,
26592     
26593     hoverState : null, //???
26594     
26595     placement : 'bottom', 
26596     
26597     alignment : false,
26598     
26599     getAutoCreate : function(){
26600     
26601         var cfg = {
26602            cls : 'tooltip',
26603            role : 'tooltip',
26604            cn : [
26605                 {
26606                     cls : 'tooltip-arrow'
26607                 },
26608                 {
26609                     cls : 'tooltip-inner'
26610                 }
26611            ]
26612         };
26613         
26614         return cfg;
26615     },
26616     bind : function(el)
26617     {
26618         this.bindEl = el;
26619     },
26620       
26621     
26622     enter : function () {
26623        
26624         if (this.timeout != null) {
26625             clearTimeout(this.timeout);
26626         }
26627         
26628         this.hoverState = 'in';
26629          //Roo.log("enter - show");
26630         if (!this.delay || !this.delay.show) {
26631             this.show();
26632             return;
26633         }
26634         var _t = this;
26635         this.timeout = setTimeout(function () {
26636             if (_t.hoverState == 'in') {
26637                 _t.show();
26638             }
26639         }, this.delay.show);
26640     },
26641     leave : function()
26642     {
26643         clearTimeout(this.timeout);
26644     
26645         this.hoverState = 'out';
26646          if (!this.delay || !this.delay.hide) {
26647             this.hide();
26648             return;
26649         }
26650        
26651         var _t = this;
26652         this.timeout = setTimeout(function () {
26653             //Roo.log("leave - timeout");
26654             
26655             if (_t.hoverState == 'out') {
26656                 _t.hide();
26657                 Roo.bootstrap.Tooltip.currentEl = false;
26658             }
26659         }, delay);
26660     },
26661     
26662     show : function (msg)
26663     {
26664         if (!this.el) {
26665             this.render(document.body);
26666         }
26667         // set content.
26668         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26669         
26670         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26671         
26672         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26673         
26674         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26675         
26676         var placement = typeof this.placement == 'function' ?
26677             this.placement.call(this, this.el, on_el) :
26678             this.placement;
26679             
26680         var autoToken = /\s?auto?\s?/i;
26681         var autoPlace = autoToken.test(placement);
26682         if (autoPlace) {
26683             placement = placement.replace(autoToken, '') || 'top';
26684         }
26685         
26686         //this.el.detach()
26687         //this.el.setXY([0,0]);
26688         this.el.show();
26689         //this.el.dom.style.display='block';
26690         
26691         //this.el.appendTo(on_el);
26692         
26693         var p = this.getPosition();
26694         var box = this.el.getBox();
26695         
26696         if (autoPlace) {
26697             // fixme..
26698         }
26699         
26700         var align = this.alignment[placement];
26701         
26702         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26703         
26704         if(placement == 'top' || placement == 'bottom'){
26705             if(xy[0] < 0){
26706                 placement = 'right';
26707             }
26708             
26709             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26710                 placement = 'left';
26711             }
26712             
26713             var scroll = Roo.select('body', true).first().getScroll();
26714             
26715             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26716                 placement = 'top';
26717             }
26718             
26719             align = this.alignment[placement];
26720         }
26721         
26722         this.el.alignTo(this.bindEl, align[0],align[1]);
26723         //var arrow = this.el.select('.arrow',true).first();
26724         //arrow.set(align[2], 
26725         
26726         this.el.addClass(placement);
26727         
26728         this.el.addClass('in fade');
26729         
26730         this.hoverState = null;
26731         
26732         if (this.el.hasClass('fade')) {
26733             // fade it?
26734         }
26735         
26736     },
26737     hide : function()
26738     {
26739          
26740         if (!this.el) {
26741             return;
26742         }
26743         //this.el.setXY([0,0]);
26744         this.el.removeClass('in');
26745         //this.el.hide();
26746         
26747     }
26748     
26749 });
26750  
26751
26752  /*
26753  * - LGPL
26754  *
26755  * Location Picker
26756  * 
26757  */
26758
26759 /**
26760  * @class Roo.bootstrap.LocationPicker
26761  * @extends Roo.bootstrap.Component
26762  * Bootstrap LocationPicker class
26763  * @cfg {Number} latitude Position when init default 0
26764  * @cfg {Number} longitude Position when init default 0
26765  * @cfg {Number} zoom default 15
26766  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26767  * @cfg {Boolean} mapTypeControl default false
26768  * @cfg {Boolean} disableDoubleClickZoom default false
26769  * @cfg {Boolean} scrollwheel default true
26770  * @cfg {Boolean} streetViewControl default false
26771  * @cfg {Number} radius default 0
26772  * @cfg {String} locationName
26773  * @cfg {Boolean} draggable default true
26774  * @cfg {Boolean} enableAutocomplete default false
26775  * @cfg {Boolean} enableReverseGeocode default true
26776  * @cfg {String} markerTitle
26777  * 
26778  * @constructor
26779  * Create a new LocationPicker
26780  * @param {Object} config The config object
26781  */
26782
26783
26784 Roo.bootstrap.LocationPicker = function(config){
26785     
26786     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26787     
26788     this.addEvents({
26789         /**
26790          * @event initial
26791          * Fires when the picker initialized.
26792          * @param {Roo.bootstrap.LocationPicker} this
26793          * @param {Google Location} location
26794          */
26795         initial : true,
26796         /**
26797          * @event positionchanged
26798          * Fires when the picker position changed.
26799          * @param {Roo.bootstrap.LocationPicker} this
26800          * @param {Google Location} location
26801          */
26802         positionchanged : true,
26803         /**
26804          * @event resize
26805          * Fires when the map resize.
26806          * @param {Roo.bootstrap.LocationPicker} this
26807          */
26808         resize : true,
26809         /**
26810          * @event show
26811          * Fires when the map show.
26812          * @param {Roo.bootstrap.LocationPicker} this
26813          */
26814         show : true,
26815         /**
26816          * @event hide
26817          * Fires when the map hide.
26818          * @param {Roo.bootstrap.LocationPicker} this
26819          */
26820         hide : true,
26821         /**
26822          * @event mapClick
26823          * Fires when click the map.
26824          * @param {Roo.bootstrap.LocationPicker} this
26825          * @param {Map event} e
26826          */
26827         mapClick : true,
26828         /**
26829          * @event mapRightClick
26830          * Fires when right click the map.
26831          * @param {Roo.bootstrap.LocationPicker} this
26832          * @param {Map event} e
26833          */
26834         mapRightClick : true,
26835         /**
26836          * @event markerClick
26837          * Fires when click the marker.
26838          * @param {Roo.bootstrap.LocationPicker} this
26839          * @param {Map event} e
26840          */
26841         markerClick : true,
26842         /**
26843          * @event markerRightClick
26844          * Fires when right click the marker.
26845          * @param {Roo.bootstrap.LocationPicker} this
26846          * @param {Map event} e
26847          */
26848         markerRightClick : true,
26849         /**
26850          * @event OverlayViewDraw
26851          * Fires when OverlayView Draw
26852          * @param {Roo.bootstrap.LocationPicker} this
26853          */
26854         OverlayViewDraw : true,
26855         /**
26856          * @event OverlayViewOnAdd
26857          * Fires when OverlayView Draw
26858          * @param {Roo.bootstrap.LocationPicker} this
26859          */
26860         OverlayViewOnAdd : true,
26861         /**
26862          * @event OverlayViewOnRemove
26863          * Fires when OverlayView Draw
26864          * @param {Roo.bootstrap.LocationPicker} this
26865          */
26866         OverlayViewOnRemove : true,
26867         /**
26868          * @event OverlayViewShow
26869          * Fires when OverlayView Draw
26870          * @param {Roo.bootstrap.LocationPicker} this
26871          * @param {Pixel} cpx
26872          */
26873         OverlayViewShow : true,
26874         /**
26875          * @event OverlayViewHide
26876          * Fires when OverlayView Draw
26877          * @param {Roo.bootstrap.LocationPicker} this
26878          */
26879         OverlayViewHide : true,
26880         /**
26881          * @event loadexception
26882          * Fires when load google lib failed.
26883          * @param {Roo.bootstrap.LocationPicker} this
26884          */
26885         loadexception : true
26886     });
26887         
26888 };
26889
26890 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26891     
26892     gMapContext: false,
26893     
26894     latitude: 0,
26895     longitude: 0,
26896     zoom: 15,
26897     mapTypeId: false,
26898     mapTypeControl: false,
26899     disableDoubleClickZoom: false,
26900     scrollwheel: true,
26901     streetViewControl: false,
26902     radius: 0,
26903     locationName: '',
26904     draggable: true,
26905     enableAutocomplete: false,
26906     enableReverseGeocode: true,
26907     markerTitle: '',
26908     
26909     getAutoCreate: function()
26910     {
26911
26912         var cfg = {
26913             tag: 'div',
26914             cls: 'roo-location-picker'
26915         };
26916         
26917         return cfg
26918     },
26919     
26920     initEvents: function(ct, position)
26921     {       
26922         if(!this.el.getWidth() || this.isApplied()){
26923             return;
26924         }
26925         
26926         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26927         
26928         this.initial();
26929     },
26930     
26931     initial: function()
26932     {
26933         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26934             this.fireEvent('loadexception', this);
26935             return;
26936         }
26937         
26938         if(!this.mapTypeId){
26939             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26940         }
26941         
26942         this.gMapContext = this.GMapContext();
26943         
26944         this.initOverlayView();
26945         
26946         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26947         
26948         var _this = this;
26949                 
26950         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26951             _this.setPosition(_this.gMapContext.marker.position);
26952         });
26953         
26954         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26955             _this.fireEvent('mapClick', this, event);
26956             
26957         });
26958
26959         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26960             _this.fireEvent('mapRightClick', this, event);
26961             
26962         });
26963         
26964         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26965             _this.fireEvent('markerClick', this, event);
26966             
26967         });
26968
26969         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26970             _this.fireEvent('markerRightClick', this, event);
26971             
26972         });
26973         
26974         this.setPosition(this.gMapContext.location);
26975         
26976         this.fireEvent('initial', this, this.gMapContext.location);
26977     },
26978     
26979     initOverlayView: function()
26980     {
26981         var _this = this;
26982         
26983         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26984             
26985             draw: function()
26986             {
26987                 _this.fireEvent('OverlayViewDraw', _this);
26988             },
26989             
26990             onAdd: function()
26991             {
26992                 _this.fireEvent('OverlayViewOnAdd', _this);
26993             },
26994             
26995             onRemove: function()
26996             {
26997                 _this.fireEvent('OverlayViewOnRemove', _this);
26998             },
26999             
27000             show: function(cpx)
27001             {
27002                 _this.fireEvent('OverlayViewShow', _this, cpx);
27003             },
27004             
27005             hide: function()
27006             {
27007                 _this.fireEvent('OverlayViewHide', _this);
27008             }
27009             
27010         });
27011     },
27012     
27013     fromLatLngToContainerPixel: function(event)
27014     {
27015         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27016     },
27017     
27018     isApplied: function() 
27019     {
27020         return this.getGmapContext() == false ? false : true;
27021     },
27022     
27023     getGmapContext: function() 
27024     {
27025         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27026     },
27027     
27028     GMapContext: function() 
27029     {
27030         var position = new google.maps.LatLng(this.latitude, this.longitude);
27031         
27032         var _map = new google.maps.Map(this.el.dom, {
27033             center: position,
27034             zoom: this.zoom,
27035             mapTypeId: this.mapTypeId,
27036             mapTypeControl: this.mapTypeControl,
27037             disableDoubleClickZoom: this.disableDoubleClickZoom,
27038             scrollwheel: this.scrollwheel,
27039             streetViewControl: this.streetViewControl,
27040             locationName: this.locationName,
27041             draggable: this.draggable,
27042             enableAutocomplete: this.enableAutocomplete,
27043             enableReverseGeocode: this.enableReverseGeocode
27044         });
27045         
27046         var _marker = new google.maps.Marker({
27047             position: position,
27048             map: _map,
27049             title: this.markerTitle,
27050             draggable: this.draggable
27051         });
27052         
27053         return {
27054             map: _map,
27055             marker: _marker,
27056             circle: null,
27057             location: position,
27058             radius: this.radius,
27059             locationName: this.locationName,
27060             addressComponents: {
27061                 formatted_address: null,
27062                 addressLine1: null,
27063                 addressLine2: null,
27064                 streetName: null,
27065                 streetNumber: null,
27066                 city: null,
27067                 district: null,
27068                 state: null,
27069                 stateOrProvince: null
27070             },
27071             settings: this,
27072             domContainer: this.el.dom,
27073             geodecoder: new google.maps.Geocoder()
27074         };
27075     },
27076     
27077     drawCircle: function(center, radius, options) 
27078     {
27079         if (this.gMapContext.circle != null) {
27080             this.gMapContext.circle.setMap(null);
27081         }
27082         if (radius > 0) {
27083             radius *= 1;
27084             options = Roo.apply({}, options, {
27085                 strokeColor: "#0000FF",
27086                 strokeOpacity: .35,
27087                 strokeWeight: 2,
27088                 fillColor: "#0000FF",
27089                 fillOpacity: .2
27090             });
27091             
27092             options.map = this.gMapContext.map;
27093             options.radius = radius;
27094             options.center = center;
27095             this.gMapContext.circle = new google.maps.Circle(options);
27096             return this.gMapContext.circle;
27097         }
27098         
27099         return null;
27100     },
27101     
27102     setPosition: function(location) 
27103     {
27104         this.gMapContext.location = location;
27105         this.gMapContext.marker.setPosition(location);
27106         this.gMapContext.map.panTo(location);
27107         this.drawCircle(location, this.gMapContext.radius, {});
27108         
27109         var _this = this;
27110         
27111         if (this.gMapContext.settings.enableReverseGeocode) {
27112             this.gMapContext.geodecoder.geocode({
27113                 latLng: this.gMapContext.location
27114             }, function(results, status) {
27115                 
27116                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27117                     _this.gMapContext.locationName = results[0].formatted_address;
27118                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27119                     
27120                     _this.fireEvent('positionchanged', this, location);
27121                 }
27122             });
27123             
27124             return;
27125         }
27126         
27127         this.fireEvent('positionchanged', this, location);
27128     },
27129     
27130     resize: function()
27131     {
27132         google.maps.event.trigger(this.gMapContext.map, "resize");
27133         
27134         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27135         
27136         this.fireEvent('resize', this);
27137     },
27138     
27139     setPositionByLatLng: function(latitude, longitude)
27140     {
27141         this.setPosition(new google.maps.LatLng(latitude, longitude));
27142     },
27143     
27144     getCurrentPosition: function() 
27145     {
27146         return {
27147             latitude: this.gMapContext.location.lat(),
27148             longitude: this.gMapContext.location.lng()
27149         };
27150     },
27151     
27152     getAddressName: function() 
27153     {
27154         return this.gMapContext.locationName;
27155     },
27156     
27157     getAddressComponents: function() 
27158     {
27159         return this.gMapContext.addressComponents;
27160     },
27161     
27162     address_component_from_google_geocode: function(address_components) 
27163     {
27164         var result = {};
27165         
27166         for (var i = 0; i < address_components.length; i++) {
27167             var component = address_components[i];
27168             if (component.types.indexOf("postal_code") >= 0) {
27169                 result.postalCode = component.short_name;
27170             } else if (component.types.indexOf("street_number") >= 0) {
27171                 result.streetNumber = component.short_name;
27172             } else if (component.types.indexOf("route") >= 0) {
27173                 result.streetName = component.short_name;
27174             } else if (component.types.indexOf("neighborhood") >= 0) {
27175                 result.city = component.short_name;
27176             } else if (component.types.indexOf("locality") >= 0) {
27177                 result.city = component.short_name;
27178             } else if (component.types.indexOf("sublocality") >= 0) {
27179                 result.district = component.short_name;
27180             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27181                 result.stateOrProvince = component.short_name;
27182             } else if (component.types.indexOf("country") >= 0) {
27183                 result.country = component.short_name;
27184             }
27185         }
27186         
27187         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27188         result.addressLine2 = "";
27189         return result;
27190     },
27191     
27192     setZoomLevel: function(zoom)
27193     {
27194         this.gMapContext.map.setZoom(zoom);
27195     },
27196     
27197     show: function()
27198     {
27199         if(!this.el){
27200             return;
27201         }
27202         
27203         this.el.show();
27204         
27205         this.resize();
27206         
27207         this.fireEvent('show', this);
27208     },
27209     
27210     hide: function()
27211     {
27212         if(!this.el){
27213             return;
27214         }
27215         
27216         this.el.hide();
27217         
27218         this.fireEvent('hide', this);
27219     }
27220     
27221 });
27222
27223 Roo.apply(Roo.bootstrap.LocationPicker, {
27224     
27225     OverlayView : function(map, options)
27226     {
27227         options = options || {};
27228         
27229         this.setMap(map);
27230     }
27231     
27232     
27233 });/**
27234  * @class Roo.bootstrap.Alert
27235  * @extends Roo.bootstrap.Component
27236  * Bootstrap Alert class - shows an alert area box
27237  * eg
27238  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27239   Enter a valid email address
27240 </div>
27241  * @licence LGPL
27242  * @cfg {String} title The title of alert
27243  * @cfg {String} html The content of alert
27244  * @cfg {String} weight (  success | info | warning | danger )
27245  * @cfg {String} faicon font-awesomeicon
27246  * 
27247  * @constructor
27248  * Create a new alert
27249  * @param {Object} config The config object
27250  */
27251
27252
27253 Roo.bootstrap.Alert = function(config){
27254     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27255     
27256 };
27257
27258 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27259     
27260     title: '',
27261     html: '',
27262     weight: false,
27263     faicon: false,
27264     
27265     getAutoCreate : function()
27266     {
27267         
27268         var cfg = {
27269             tag : 'div',
27270             cls : 'alert',
27271             cn : [
27272                 {
27273                     tag : 'i',
27274                     cls : 'roo-alert-icon'
27275                     
27276                 },
27277                 {
27278                     tag : 'b',
27279                     cls : 'roo-alert-title',
27280                     html : this.title
27281                 },
27282                 {
27283                     tag : 'span',
27284                     cls : 'roo-alert-text',
27285                     html : this.html
27286                 }
27287             ]
27288         };
27289         
27290         if(this.faicon){
27291             cfg.cn[0].cls += ' fa ' + this.faicon;
27292         }
27293         
27294         if(this.weight){
27295             cfg.cls += ' alert-' + this.weight;
27296         }
27297         
27298         return cfg;
27299     },
27300     
27301     initEvents: function() 
27302     {
27303         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27304     },
27305     
27306     setTitle : function(str)
27307     {
27308         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27309     },
27310     
27311     setText : function(str)
27312     {
27313         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27314     },
27315     
27316     setWeight : function(weight)
27317     {
27318         if(this.weight){
27319             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27320         }
27321         
27322         this.weight = weight;
27323         
27324         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27325     },
27326     
27327     setIcon : function(icon)
27328     {
27329         if(this.faicon){
27330             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27331         }
27332         
27333         this.faicon = icon;
27334         
27335         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27336     },
27337     
27338     hide: function() 
27339     {
27340         this.el.hide();   
27341     },
27342     
27343     show: function() 
27344     {  
27345         this.el.show();   
27346     }
27347     
27348 });
27349
27350  
27351 /*
27352 * Licence: LGPL
27353 */
27354
27355 /**
27356  * @class Roo.bootstrap.UploadCropbox
27357  * @extends Roo.bootstrap.Component
27358  * Bootstrap UploadCropbox class
27359  * @cfg {String} emptyText show when image has been loaded
27360  * @cfg {String} rotateNotify show when image too small to rotate
27361  * @cfg {Number} errorTimeout default 3000
27362  * @cfg {Number} minWidth default 300
27363  * @cfg {Number} minHeight default 300
27364  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27365  * @cfg {Boolean} isDocument (true|false) default false
27366  * @cfg {String} url action url
27367  * @cfg {String} paramName default 'imageUpload'
27368  * @cfg {String} method default POST
27369  * @cfg {Boolean} loadMask (true|false) default true
27370  * @cfg {Boolean} loadingText default 'Loading...'
27371  * 
27372  * @constructor
27373  * Create a new UploadCropbox
27374  * @param {Object} config The config object
27375  */
27376
27377 Roo.bootstrap.UploadCropbox = function(config){
27378     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27379     
27380     this.addEvents({
27381         /**
27382          * @event beforeselectfile
27383          * Fire before select file
27384          * @param {Roo.bootstrap.UploadCropbox} this
27385          */
27386         "beforeselectfile" : true,
27387         /**
27388          * @event initial
27389          * Fire after initEvent
27390          * @param {Roo.bootstrap.UploadCropbox} this
27391          */
27392         "initial" : true,
27393         /**
27394          * @event crop
27395          * Fire after initEvent
27396          * @param {Roo.bootstrap.UploadCropbox} this
27397          * @param {String} data
27398          */
27399         "crop" : true,
27400         /**
27401          * @event prepare
27402          * Fire when preparing the file data
27403          * @param {Roo.bootstrap.UploadCropbox} this
27404          * @param {Object} file
27405          */
27406         "prepare" : true,
27407         /**
27408          * @event exception
27409          * Fire when get exception
27410          * @param {Roo.bootstrap.UploadCropbox} this
27411          * @param {XMLHttpRequest} xhr
27412          */
27413         "exception" : true,
27414         /**
27415          * @event beforeloadcanvas
27416          * Fire before load the canvas
27417          * @param {Roo.bootstrap.UploadCropbox} this
27418          * @param {String} src
27419          */
27420         "beforeloadcanvas" : true,
27421         /**
27422          * @event trash
27423          * Fire when trash image
27424          * @param {Roo.bootstrap.UploadCropbox} this
27425          */
27426         "trash" : true,
27427         /**
27428          * @event download
27429          * Fire when download the image
27430          * @param {Roo.bootstrap.UploadCropbox} this
27431          */
27432         "download" : true,
27433         /**
27434          * @event footerbuttonclick
27435          * Fire when footerbuttonclick
27436          * @param {Roo.bootstrap.UploadCropbox} this
27437          * @param {String} type
27438          */
27439         "footerbuttonclick" : true,
27440         /**
27441          * @event resize
27442          * Fire when resize
27443          * @param {Roo.bootstrap.UploadCropbox} this
27444          */
27445         "resize" : true,
27446         /**
27447          * @event rotate
27448          * Fire when rotate the image
27449          * @param {Roo.bootstrap.UploadCropbox} this
27450          * @param {String} pos
27451          */
27452         "rotate" : true,
27453         /**
27454          * @event inspect
27455          * Fire when inspect the file
27456          * @param {Roo.bootstrap.UploadCropbox} this
27457          * @param {Object} file
27458          */
27459         "inspect" : true,
27460         /**
27461          * @event upload
27462          * Fire when xhr upload the file
27463          * @param {Roo.bootstrap.UploadCropbox} this
27464          * @param {Object} data
27465          */
27466         "upload" : true,
27467         /**
27468          * @event arrange
27469          * Fire when arrange the file data
27470          * @param {Roo.bootstrap.UploadCropbox} this
27471          * @param {Object} formData
27472          */
27473         "arrange" : true
27474     });
27475     
27476     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27477 };
27478
27479 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27480     
27481     emptyText : 'Click to upload image',
27482     rotateNotify : 'Image is too small to rotate',
27483     errorTimeout : 3000,
27484     scale : 0,
27485     baseScale : 1,
27486     rotate : 0,
27487     dragable : false,
27488     pinching : false,
27489     mouseX : 0,
27490     mouseY : 0,
27491     cropData : false,
27492     minWidth : 300,
27493     minHeight : 300,
27494     file : false,
27495     exif : {},
27496     baseRotate : 1,
27497     cropType : 'image/jpeg',
27498     buttons : false,
27499     canvasLoaded : false,
27500     isDocument : false,
27501     method : 'POST',
27502     paramName : 'imageUpload',
27503     loadMask : true,
27504     loadingText : 'Loading...',
27505     maskEl : false,
27506     
27507     getAutoCreate : function()
27508     {
27509         var cfg = {
27510             tag : 'div',
27511             cls : 'roo-upload-cropbox',
27512             cn : [
27513                 {
27514                     tag : 'input',
27515                     cls : 'roo-upload-cropbox-selector',
27516                     type : 'file'
27517                 },
27518                 {
27519                     tag : 'div',
27520                     cls : 'roo-upload-cropbox-body',
27521                     style : 'cursor:pointer',
27522                     cn : [
27523                         {
27524                             tag : 'div',
27525                             cls : 'roo-upload-cropbox-preview'
27526                         },
27527                         {
27528                             tag : 'div',
27529                             cls : 'roo-upload-cropbox-thumb'
27530                         },
27531                         {
27532                             tag : 'div',
27533                             cls : 'roo-upload-cropbox-empty-notify',
27534                             html : this.emptyText
27535                         },
27536                         {
27537                             tag : 'div',
27538                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27539                             html : this.rotateNotify
27540                         }
27541                     ]
27542                 },
27543                 {
27544                     tag : 'div',
27545                     cls : 'roo-upload-cropbox-footer',
27546                     cn : {
27547                         tag : 'div',
27548                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27549                         cn : []
27550                     }
27551                 }
27552             ]
27553         };
27554         
27555         return cfg;
27556     },
27557     
27558     onRender : function(ct, position)
27559     {
27560         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27561         
27562         if (this.buttons.length) {
27563             
27564             Roo.each(this.buttons, function(bb) {
27565                 
27566                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27567                 
27568                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27569                 
27570             }, this);
27571         }
27572         
27573         if(this.loadMask){
27574             this.maskEl = this.el;
27575         }
27576     },
27577     
27578     initEvents : function()
27579     {
27580         this.urlAPI = (window.createObjectURL && window) || 
27581                                 (window.URL && URL.revokeObjectURL && URL) || 
27582                                 (window.webkitURL && webkitURL);
27583                         
27584         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27585         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27586         
27587         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27588         this.selectorEl.hide();
27589         
27590         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27591         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27592         
27593         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27594         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27595         this.thumbEl.hide();
27596         
27597         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27598         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27599         
27600         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27601         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27602         this.errorEl.hide();
27603         
27604         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27605         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27606         this.footerEl.hide();
27607         
27608         this.setThumbBoxSize();
27609         
27610         this.bind();
27611         
27612         this.resize();
27613         
27614         this.fireEvent('initial', this);
27615     },
27616
27617     bind : function()
27618     {
27619         var _this = this;
27620         
27621         window.addEventListener("resize", function() { _this.resize(); } );
27622         
27623         this.bodyEl.on('click', this.beforeSelectFile, this);
27624         
27625         if(Roo.isTouch){
27626             this.bodyEl.on('touchstart', this.onTouchStart, this);
27627             this.bodyEl.on('touchmove', this.onTouchMove, this);
27628             this.bodyEl.on('touchend', this.onTouchEnd, this);
27629         }
27630         
27631         if(!Roo.isTouch){
27632             this.bodyEl.on('mousedown', this.onMouseDown, this);
27633             this.bodyEl.on('mousemove', this.onMouseMove, this);
27634             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27635             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27636             Roo.get(document).on('mouseup', this.onMouseUp, this);
27637         }
27638         
27639         this.selectorEl.on('change', this.onFileSelected, this);
27640     },
27641     
27642     reset : function()
27643     {    
27644         this.scale = 0;
27645         this.baseScale = 1;
27646         this.rotate = 0;
27647         this.baseRotate = 1;
27648         this.dragable = false;
27649         this.pinching = false;
27650         this.mouseX = 0;
27651         this.mouseY = 0;
27652         this.cropData = false;
27653         this.notifyEl.dom.innerHTML = this.emptyText;
27654         
27655         this.selectorEl.dom.value = '';
27656         
27657     },
27658     
27659     resize : function()
27660     {
27661         if(this.fireEvent('resize', this) != false){
27662             this.setThumbBoxPosition();
27663             this.setCanvasPosition();
27664         }
27665     },
27666     
27667     onFooterButtonClick : function(e, el, o, type)
27668     {
27669         switch (type) {
27670             case 'rotate-left' :
27671                 this.onRotateLeft(e);
27672                 break;
27673             case 'rotate-right' :
27674                 this.onRotateRight(e);
27675                 break;
27676             case 'picture' :
27677                 this.beforeSelectFile(e);
27678                 break;
27679             case 'trash' :
27680                 this.trash(e);
27681                 break;
27682             case 'crop' :
27683                 this.crop(e);
27684                 break;
27685             case 'download' :
27686                 this.download(e);
27687                 break;
27688             default :
27689                 break;
27690         }
27691         
27692         this.fireEvent('footerbuttonclick', this, type);
27693     },
27694     
27695     beforeSelectFile : function(e)
27696     {
27697         e.preventDefault();
27698         
27699         if(this.fireEvent('beforeselectfile', this) != false){
27700             this.selectorEl.dom.click();
27701         }
27702     },
27703     
27704     onFileSelected : function(e)
27705     {
27706         e.preventDefault();
27707         
27708         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27709             return;
27710         }
27711         
27712         var file = this.selectorEl.dom.files[0];
27713         
27714         if(this.fireEvent('inspect', this, file) != false){
27715             this.prepare(file);
27716         }
27717         
27718     },
27719     
27720     trash : function(e)
27721     {
27722         this.fireEvent('trash', this);
27723     },
27724     
27725     download : function(e)
27726     {
27727         this.fireEvent('download', this);
27728     },
27729     
27730     loadCanvas : function(src)
27731     {   
27732         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27733             
27734             this.reset();
27735             
27736             this.imageEl = document.createElement('img');
27737             
27738             var _this = this;
27739             
27740             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27741             
27742             this.imageEl.src = src;
27743         }
27744     },
27745     
27746     onLoadCanvas : function()
27747     {   
27748         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27749         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27750         
27751         this.bodyEl.un('click', this.beforeSelectFile, this);
27752         
27753         this.notifyEl.hide();
27754         this.thumbEl.show();
27755         this.footerEl.show();
27756         
27757         this.baseRotateLevel();
27758         
27759         if(this.isDocument){
27760             this.setThumbBoxSize();
27761         }
27762         
27763         this.setThumbBoxPosition();
27764         
27765         this.baseScaleLevel();
27766         
27767         this.draw();
27768         
27769         this.resize();
27770         
27771         this.canvasLoaded = true;
27772         
27773         if(this.loadMask){
27774             this.maskEl.unmask();
27775         }
27776         
27777     },
27778     
27779     setCanvasPosition : function()
27780     {   
27781         if(!this.canvasEl){
27782             return;
27783         }
27784         
27785         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27786         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27787         
27788         this.previewEl.setLeft(pw);
27789         this.previewEl.setTop(ph);
27790         
27791     },
27792     
27793     onMouseDown : function(e)
27794     {   
27795         e.stopEvent();
27796         
27797         this.dragable = true;
27798         this.pinching = false;
27799         
27800         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27801             this.dragable = false;
27802             return;
27803         }
27804         
27805         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27806         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27807         
27808     },
27809     
27810     onMouseMove : function(e)
27811     {   
27812         e.stopEvent();
27813         
27814         if(!this.canvasLoaded){
27815             return;
27816         }
27817         
27818         if (!this.dragable){
27819             return;
27820         }
27821         
27822         var minX = Math.ceil(this.thumbEl.getLeft(true));
27823         var minY = Math.ceil(this.thumbEl.getTop(true));
27824         
27825         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27826         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27827         
27828         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27829         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27830         
27831         x = x - this.mouseX;
27832         y = y - this.mouseY;
27833         
27834         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27835         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27836         
27837         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27838         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27839         
27840         this.previewEl.setLeft(bgX);
27841         this.previewEl.setTop(bgY);
27842         
27843         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27844         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27845     },
27846     
27847     onMouseUp : function(e)
27848     {   
27849         e.stopEvent();
27850         
27851         this.dragable = false;
27852     },
27853     
27854     onMouseWheel : function(e)
27855     {   
27856         e.stopEvent();
27857         
27858         this.startScale = this.scale;
27859         
27860         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27861         
27862         if(!this.zoomable()){
27863             this.scale = this.startScale;
27864             return;
27865         }
27866         
27867         this.draw();
27868         
27869         return;
27870     },
27871     
27872     zoomable : function()
27873     {
27874         var minScale = this.thumbEl.getWidth() / this.minWidth;
27875         
27876         if(this.minWidth < this.minHeight){
27877             minScale = this.thumbEl.getHeight() / this.minHeight;
27878         }
27879         
27880         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27881         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27882         
27883         if(
27884                 this.isDocument &&
27885                 (this.rotate == 0 || this.rotate == 180) && 
27886                 (
27887                     width > this.imageEl.OriginWidth || 
27888                     height > this.imageEl.OriginHeight ||
27889                     (width < this.minWidth && height < this.minHeight)
27890                 )
27891         ){
27892             return false;
27893         }
27894         
27895         if(
27896                 this.isDocument &&
27897                 (this.rotate == 90 || this.rotate == 270) && 
27898                 (
27899                     width > this.imageEl.OriginWidth || 
27900                     height > this.imageEl.OriginHeight ||
27901                     (width < this.minHeight && height < this.minWidth)
27902                 )
27903         ){
27904             return false;
27905         }
27906         
27907         if(
27908                 !this.isDocument &&
27909                 (this.rotate == 0 || this.rotate == 180) && 
27910                 (
27911                     width < this.minWidth || 
27912                     width > this.imageEl.OriginWidth || 
27913                     height < this.minHeight || 
27914                     height > this.imageEl.OriginHeight
27915                 )
27916         ){
27917             return false;
27918         }
27919         
27920         if(
27921                 !this.isDocument &&
27922                 (this.rotate == 90 || this.rotate == 270) && 
27923                 (
27924                     width < this.minHeight || 
27925                     width > this.imageEl.OriginWidth || 
27926                     height < this.minWidth || 
27927                     height > this.imageEl.OriginHeight
27928                 )
27929         ){
27930             return false;
27931         }
27932         
27933         return true;
27934         
27935     },
27936     
27937     onRotateLeft : function(e)
27938     {   
27939         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27940             
27941             var minScale = this.thumbEl.getWidth() / this.minWidth;
27942             
27943             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27944             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27945             
27946             this.startScale = this.scale;
27947             
27948             while (this.getScaleLevel() < minScale){
27949             
27950                 this.scale = this.scale + 1;
27951                 
27952                 if(!this.zoomable()){
27953                     break;
27954                 }
27955                 
27956                 if(
27957                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27958                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27959                 ){
27960                     continue;
27961                 }
27962                 
27963                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27964
27965                 this.draw();
27966                 
27967                 return;
27968             }
27969             
27970             this.scale = this.startScale;
27971             
27972             this.onRotateFail();
27973             
27974             return false;
27975         }
27976         
27977         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27978
27979         if(this.isDocument){
27980             this.setThumbBoxSize();
27981             this.setThumbBoxPosition();
27982             this.setCanvasPosition();
27983         }
27984         
27985         this.draw();
27986         
27987         this.fireEvent('rotate', this, 'left');
27988         
27989     },
27990     
27991     onRotateRight : function(e)
27992     {
27993         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27994             
27995             var minScale = this.thumbEl.getWidth() / this.minWidth;
27996         
27997             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27998             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27999             
28000             this.startScale = this.scale;
28001             
28002             while (this.getScaleLevel() < minScale){
28003             
28004                 this.scale = this.scale + 1;
28005                 
28006                 if(!this.zoomable()){
28007                     break;
28008                 }
28009                 
28010                 if(
28011                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28012                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28013                 ){
28014                     continue;
28015                 }
28016                 
28017                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28018
28019                 this.draw();
28020                 
28021                 return;
28022             }
28023             
28024             this.scale = this.startScale;
28025             
28026             this.onRotateFail();
28027             
28028             return false;
28029         }
28030         
28031         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28032
28033         if(this.isDocument){
28034             this.setThumbBoxSize();
28035             this.setThumbBoxPosition();
28036             this.setCanvasPosition();
28037         }
28038         
28039         this.draw();
28040         
28041         this.fireEvent('rotate', this, 'right');
28042     },
28043     
28044     onRotateFail : function()
28045     {
28046         this.errorEl.show(true);
28047         
28048         var _this = this;
28049         
28050         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28051     },
28052     
28053     draw : function()
28054     {
28055         this.previewEl.dom.innerHTML = '';
28056         
28057         var canvasEl = document.createElement("canvas");
28058         
28059         var contextEl = canvasEl.getContext("2d");
28060         
28061         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28062         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28063         var center = this.imageEl.OriginWidth / 2;
28064         
28065         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28066             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28067             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28068             center = this.imageEl.OriginHeight / 2;
28069         }
28070         
28071         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28072         
28073         contextEl.translate(center, center);
28074         contextEl.rotate(this.rotate * Math.PI / 180);
28075
28076         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28077         
28078         this.canvasEl = document.createElement("canvas");
28079         
28080         this.contextEl = this.canvasEl.getContext("2d");
28081         
28082         switch (this.rotate) {
28083             case 0 :
28084                 
28085                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28086                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28087                 
28088                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28089                 
28090                 break;
28091             case 90 : 
28092                 
28093                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28094                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28095                 
28096                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28097                     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);
28098                     break;
28099                 }
28100                 
28101                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28102                 
28103                 break;
28104             case 180 :
28105                 
28106                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28107                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28108                 
28109                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28110                     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);
28111                     break;
28112                 }
28113                 
28114                 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);
28115                 
28116                 break;
28117             case 270 :
28118                 
28119                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28120                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28121         
28122                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28123                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28124                     break;
28125                 }
28126                 
28127                 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);
28128                 
28129                 break;
28130             default : 
28131                 break;
28132         }
28133         
28134         this.previewEl.appendChild(this.canvasEl);
28135         
28136         this.setCanvasPosition();
28137     },
28138     
28139     crop : function()
28140     {
28141         if(!this.canvasLoaded){
28142             return;
28143         }
28144         
28145         var imageCanvas = document.createElement("canvas");
28146         
28147         var imageContext = imageCanvas.getContext("2d");
28148         
28149         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28150         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28151         
28152         var center = imageCanvas.width / 2;
28153         
28154         imageContext.translate(center, center);
28155         
28156         imageContext.rotate(this.rotate * Math.PI / 180);
28157         
28158         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28159         
28160         var canvas = document.createElement("canvas");
28161         
28162         var context = canvas.getContext("2d");
28163                 
28164         canvas.width = this.minWidth;
28165         canvas.height = this.minHeight;
28166
28167         switch (this.rotate) {
28168             case 0 :
28169                 
28170                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28171                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28172                 
28173                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28174                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28175                 
28176                 var targetWidth = this.minWidth - 2 * x;
28177                 var targetHeight = this.minHeight - 2 * y;
28178                 
28179                 var scale = 1;
28180                 
28181                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28182                     scale = targetWidth / width;
28183                 }
28184                 
28185                 if(x > 0 && y == 0){
28186                     scale = targetHeight / height;
28187                 }
28188                 
28189                 if(x > 0 && y > 0){
28190                     scale = targetWidth / width;
28191                     
28192                     if(width < height){
28193                         scale = targetHeight / height;
28194                     }
28195                 }
28196                 
28197                 context.scale(scale, scale);
28198                 
28199                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28200                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28201
28202                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28203                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28204
28205                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28206                 
28207                 break;
28208             case 90 : 
28209                 
28210                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28211                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28212                 
28213                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28214                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28215                 
28216                 var targetWidth = this.minWidth - 2 * x;
28217                 var targetHeight = this.minHeight - 2 * y;
28218                 
28219                 var scale = 1;
28220                 
28221                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28222                     scale = targetWidth / width;
28223                 }
28224                 
28225                 if(x > 0 && y == 0){
28226                     scale = targetHeight / height;
28227                 }
28228                 
28229                 if(x > 0 && y > 0){
28230                     scale = targetWidth / width;
28231                     
28232                     if(width < height){
28233                         scale = targetHeight / height;
28234                     }
28235                 }
28236                 
28237                 context.scale(scale, scale);
28238                 
28239                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28240                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28241
28242                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28243                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28244                 
28245                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28246                 
28247                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28248                 
28249                 break;
28250             case 180 :
28251                 
28252                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28253                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28254                 
28255                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28256                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28257                 
28258                 var targetWidth = this.minWidth - 2 * x;
28259                 var targetHeight = this.minHeight - 2 * y;
28260                 
28261                 var scale = 1;
28262                 
28263                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28264                     scale = targetWidth / width;
28265                 }
28266                 
28267                 if(x > 0 && y == 0){
28268                     scale = targetHeight / height;
28269                 }
28270                 
28271                 if(x > 0 && y > 0){
28272                     scale = targetWidth / width;
28273                     
28274                     if(width < height){
28275                         scale = targetHeight / height;
28276                     }
28277                 }
28278                 
28279                 context.scale(scale, scale);
28280                 
28281                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28282                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28283
28284                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28285                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28286
28287                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28288                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28289                 
28290                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28291                 
28292                 break;
28293             case 270 :
28294                 
28295                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28296                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28297                 
28298                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28299                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28300                 
28301                 var targetWidth = this.minWidth - 2 * x;
28302                 var targetHeight = this.minHeight - 2 * y;
28303                 
28304                 var scale = 1;
28305                 
28306                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28307                     scale = targetWidth / width;
28308                 }
28309                 
28310                 if(x > 0 && y == 0){
28311                     scale = targetHeight / height;
28312                 }
28313                 
28314                 if(x > 0 && y > 0){
28315                     scale = targetWidth / width;
28316                     
28317                     if(width < height){
28318                         scale = targetHeight / height;
28319                     }
28320                 }
28321                 
28322                 context.scale(scale, scale);
28323                 
28324                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28325                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28326
28327                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28328                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28329                 
28330                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28331                 
28332                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28333                 
28334                 break;
28335             default : 
28336                 break;
28337         }
28338         
28339         this.cropData = canvas.toDataURL(this.cropType);
28340         
28341         if(this.fireEvent('crop', this, this.cropData) !== false){
28342             this.process(this.file, this.cropData);
28343         }
28344         
28345         return;
28346         
28347     },
28348     
28349     setThumbBoxSize : function()
28350     {
28351         var width, height;
28352         
28353         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28354             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28355             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28356             
28357             this.minWidth = width;
28358             this.minHeight = height;
28359             
28360             if(this.rotate == 90 || this.rotate == 270){
28361                 this.minWidth = height;
28362                 this.minHeight = width;
28363             }
28364         }
28365         
28366         height = 300;
28367         width = Math.ceil(this.minWidth * height / this.minHeight);
28368         
28369         if(this.minWidth > this.minHeight){
28370             width = 300;
28371             height = Math.ceil(this.minHeight * width / this.minWidth);
28372         }
28373         
28374         this.thumbEl.setStyle({
28375             width : width + 'px',
28376             height : height + 'px'
28377         });
28378
28379         return;
28380             
28381     },
28382     
28383     setThumbBoxPosition : function()
28384     {
28385         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28386         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28387         
28388         this.thumbEl.setLeft(x);
28389         this.thumbEl.setTop(y);
28390         
28391     },
28392     
28393     baseRotateLevel : function()
28394     {
28395         this.baseRotate = 1;
28396         
28397         if(
28398                 typeof(this.exif) != 'undefined' &&
28399                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28400                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28401         ){
28402             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28403         }
28404         
28405         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28406         
28407     },
28408     
28409     baseScaleLevel : function()
28410     {
28411         var width, height;
28412         
28413         if(this.isDocument){
28414             
28415             if(this.baseRotate == 6 || this.baseRotate == 8){
28416             
28417                 height = this.thumbEl.getHeight();
28418                 this.baseScale = height / this.imageEl.OriginWidth;
28419
28420                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28421                     width = this.thumbEl.getWidth();
28422                     this.baseScale = width / this.imageEl.OriginHeight;
28423                 }
28424
28425                 return;
28426             }
28427
28428             height = this.thumbEl.getHeight();
28429             this.baseScale = height / this.imageEl.OriginHeight;
28430
28431             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28432                 width = this.thumbEl.getWidth();
28433                 this.baseScale = width / this.imageEl.OriginWidth;
28434             }
28435
28436             return;
28437         }
28438         
28439         if(this.baseRotate == 6 || this.baseRotate == 8){
28440             
28441             width = this.thumbEl.getHeight();
28442             this.baseScale = width / this.imageEl.OriginHeight;
28443             
28444             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28445                 height = this.thumbEl.getWidth();
28446                 this.baseScale = height / this.imageEl.OriginHeight;
28447             }
28448             
28449             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28450                 height = this.thumbEl.getWidth();
28451                 this.baseScale = height / this.imageEl.OriginHeight;
28452                 
28453                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28454                     width = this.thumbEl.getHeight();
28455                     this.baseScale = width / this.imageEl.OriginWidth;
28456                 }
28457             }
28458             
28459             return;
28460         }
28461         
28462         width = this.thumbEl.getWidth();
28463         this.baseScale = width / this.imageEl.OriginWidth;
28464         
28465         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28466             height = this.thumbEl.getHeight();
28467             this.baseScale = height / this.imageEl.OriginHeight;
28468         }
28469         
28470         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28471             
28472             height = this.thumbEl.getHeight();
28473             this.baseScale = height / this.imageEl.OriginHeight;
28474             
28475             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28476                 width = this.thumbEl.getWidth();
28477                 this.baseScale = width / this.imageEl.OriginWidth;
28478             }
28479             
28480         }
28481         
28482         return;
28483     },
28484     
28485     getScaleLevel : function()
28486     {
28487         return this.baseScale * Math.pow(1.1, this.scale);
28488     },
28489     
28490     onTouchStart : function(e)
28491     {
28492         if(!this.canvasLoaded){
28493             this.beforeSelectFile(e);
28494             return;
28495         }
28496         
28497         var touches = e.browserEvent.touches;
28498         
28499         if(!touches){
28500             return;
28501         }
28502         
28503         if(touches.length == 1){
28504             this.onMouseDown(e);
28505             return;
28506         }
28507         
28508         if(touches.length != 2){
28509             return;
28510         }
28511         
28512         var coords = [];
28513         
28514         for(var i = 0, finger; finger = touches[i]; i++){
28515             coords.push(finger.pageX, finger.pageY);
28516         }
28517         
28518         var x = Math.pow(coords[0] - coords[2], 2);
28519         var y = Math.pow(coords[1] - coords[3], 2);
28520         
28521         this.startDistance = Math.sqrt(x + y);
28522         
28523         this.startScale = this.scale;
28524         
28525         this.pinching = true;
28526         this.dragable = false;
28527         
28528     },
28529     
28530     onTouchMove : function(e)
28531     {
28532         if(!this.pinching && !this.dragable){
28533             return;
28534         }
28535         
28536         var touches = e.browserEvent.touches;
28537         
28538         if(!touches){
28539             return;
28540         }
28541         
28542         if(this.dragable){
28543             this.onMouseMove(e);
28544             return;
28545         }
28546         
28547         var coords = [];
28548         
28549         for(var i = 0, finger; finger = touches[i]; i++){
28550             coords.push(finger.pageX, finger.pageY);
28551         }
28552         
28553         var x = Math.pow(coords[0] - coords[2], 2);
28554         var y = Math.pow(coords[1] - coords[3], 2);
28555         
28556         this.endDistance = Math.sqrt(x + y);
28557         
28558         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28559         
28560         if(!this.zoomable()){
28561             this.scale = this.startScale;
28562             return;
28563         }
28564         
28565         this.draw();
28566         
28567     },
28568     
28569     onTouchEnd : function(e)
28570     {
28571         this.pinching = false;
28572         this.dragable = false;
28573         
28574     },
28575     
28576     process : function(file, crop)
28577     {
28578         if(this.loadMask){
28579             this.maskEl.mask(this.loadingText);
28580         }
28581         
28582         this.xhr = new XMLHttpRequest();
28583         
28584         file.xhr = this.xhr;
28585
28586         this.xhr.open(this.method, this.url, true);
28587         
28588         var headers = {
28589             "Accept": "application/json",
28590             "Cache-Control": "no-cache",
28591             "X-Requested-With": "XMLHttpRequest"
28592         };
28593         
28594         for (var headerName in headers) {
28595             var headerValue = headers[headerName];
28596             if (headerValue) {
28597                 this.xhr.setRequestHeader(headerName, headerValue);
28598             }
28599         }
28600         
28601         var _this = this;
28602         
28603         this.xhr.onload = function()
28604         {
28605             _this.xhrOnLoad(_this.xhr);
28606         }
28607         
28608         this.xhr.onerror = function()
28609         {
28610             _this.xhrOnError(_this.xhr);
28611         }
28612         
28613         var formData = new FormData();
28614
28615         formData.append('returnHTML', 'NO');
28616         
28617         if(crop){
28618             formData.append('crop', crop);
28619         }
28620         
28621         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28622             formData.append(this.paramName, file, file.name);
28623         }
28624         
28625         if(typeof(file.filename) != 'undefined'){
28626             formData.append('filename', file.filename);
28627         }
28628         
28629         if(typeof(file.mimetype) != 'undefined'){
28630             formData.append('mimetype', file.mimetype);
28631         }
28632         
28633         if(this.fireEvent('arrange', this, formData) != false){
28634             this.xhr.send(formData);
28635         };
28636     },
28637     
28638     xhrOnLoad : function(xhr)
28639     {
28640         if(this.loadMask){
28641             this.maskEl.unmask();
28642         }
28643         
28644         if (xhr.readyState !== 4) {
28645             this.fireEvent('exception', this, xhr);
28646             return;
28647         }
28648
28649         var response = Roo.decode(xhr.responseText);
28650         
28651         if(!response.success){
28652             this.fireEvent('exception', this, xhr);
28653             return;
28654         }
28655         
28656         var response = Roo.decode(xhr.responseText);
28657         
28658         this.fireEvent('upload', this, response);
28659         
28660     },
28661     
28662     xhrOnError : function()
28663     {
28664         if(this.loadMask){
28665             this.maskEl.unmask();
28666         }
28667         
28668         Roo.log('xhr on error');
28669         
28670         var response = Roo.decode(xhr.responseText);
28671           
28672         Roo.log(response);
28673         
28674     },
28675     
28676     prepare : function(file)
28677     {   
28678         if(this.loadMask){
28679             this.maskEl.mask(this.loadingText);
28680         }
28681         
28682         this.file = false;
28683         this.exif = {};
28684         
28685         if(typeof(file) === 'string'){
28686             this.loadCanvas(file);
28687             return;
28688         }
28689         
28690         if(!file || !this.urlAPI){
28691             return;
28692         }
28693         
28694         this.file = file;
28695         this.cropType = file.type;
28696         
28697         var _this = this;
28698         
28699         if(this.fireEvent('prepare', this, this.file) != false){
28700             
28701             var reader = new FileReader();
28702             
28703             reader.onload = function (e) {
28704                 if (e.target.error) {
28705                     Roo.log(e.target.error);
28706                     return;
28707                 }
28708                 
28709                 var buffer = e.target.result,
28710                     dataView = new DataView(buffer),
28711                     offset = 2,
28712                     maxOffset = dataView.byteLength - 4,
28713                     markerBytes,
28714                     markerLength;
28715                 
28716                 if (dataView.getUint16(0) === 0xffd8) {
28717                     while (offset < maxOffset) {
28718                         markerBytes = dataView.getUint16(offset);
28719                         
28720                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28721                             markerLength = dataView.getUint16(offset + 2) + 2;
28722                             if (offset + markerLength > dataView.byteLength) {
28723                                 Roo.log('Invalid meta data: Invalid segment size.');
28724                                 break;
28725                             }
28726                             
28727                             if(markerBytes == 0xffe1){
28728                                 _this.parseExifData(
28729                                     dataView,
28730                                     offset,
28731                                     markerLength
28732                                 );
28733                             }
28734                             
28735                             offset += markerLength;
28736                             
28737                             continue;
28738                         }
28739                         
28740                         break;
28741                     }
28742                     
28743                 }
28744                 
28745                 var url = _this.urlAPI.createObjectURL(_this.file);
28746                 
28747                 _this.loadCanvas(url);
28748                 
28749                 return;
28750             }
28751             
28752             reader.readAsArrayBuffer(this.file);
28753             
28754         }
28755         
28756     },
28757     
28758     parseExifData : function(dataView, offset, length)
28759     {
28760         var tiffOffset = offset + 10,
28761             littleEndian,
28762             dirOffset;
28763     
28764         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28765             // No Exif data, might be XMP data instead
28766             return;
28767         }
28768         
28769         // Check for the ASCII code for "Exif" (0x45786966):
28770         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28771             // No Exif data, might be XMP data instead
28772             return;
28773         }
28774         if (tiffOffset + 8 > dataView.byteLength) {
28775             Roo.log('Invalid Exif data: Invalid segment size.');
28776             return;
28777         }
28778         // Check for the two null bytes:
28779         if (dataView.getUint16(offset + 8) !== 0x0000) {
28780             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28781             return;
28782         }
28783         // Check the byte alignment:
28784         switch (dataView.getUint16(tiffOffset)) {
28785         case 0x4949:
28786             littleEndian = true;
28787             break;
28788         case 0x4D4D:
28789             littleEndian = false;
28790             break;
28791         default:
28792             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28793             return;
28794         }
28795         // Check for the TIFF tag marker (0x002A):
28796         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28797             Roo.log('Invalid Exif data: Missing TIFF marker.');
28798             return;
28799         }
28800         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28801         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28802         
28803         this.parseExifTags(
28804             dataView,
28805             tiffOffset,
28806             tiffOffset + dirOffset,
28807             littleEndian
28808         );
28809     },
28810     
28811     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28812     {
28813         var tagsNumber,
28814             dirEndOffset,
28815             i;
28816         if (dirOffset + 6 > dataView.byteLength) {
28817             Roo.log('Invalid Exif data: Invalid directory offset.');
28818             return;
28819         }
28820         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28821         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28822         if (dirEndOffset + 4 > dataView.byteLength) {
28823             Roo.log('Invalid Exif data: Invalid directory size.');
28824             return;
28825         }
28826         for (i = 0; i < tagsNumber; i += 1) {
28827             this.parseExifTag(
28828                 dataView,
28829                 tiffOffset,
28830                 dirOffset + 2 + 12 * i, // tag offset
28831                 littleEndian
28832             );
28833         }
28834         // Return the offset to the next directory:
28835         return dataView.getUint32(dirEndOffset, littleEndian);
28836     },
28837     
28838     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28839     {
28840         var tag = dataView.getUint16(offset, littleEndian);
28841         
28842         this.exif[tag] = this.getExifValue(
28843             dataView,
28844             tiffOffset,
28845             offset,
28846             dataView.getUint16(offset + 2, littleEndian), // tag type
28847             dataView.getUint32(offset + 4, littleEndian), // tag length
28848             littleEndian
28849         );
28850     },
28851     
28852     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28853     {
28854         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28855             tagSize,
28856             dataOffset,
28857             values,
28858             i,
28859             str,
28860             c;
28861     
28862         if (!tagType) {
28863             Roo.log('Invalid Exif data: Invalid tag type.');
28864             return;
28865         }
28866         
28867         tagSize = tagType.size * length;
28868         // Determine if the value is contained in the dataOffset bytes,
28869         // or if the value at the dataOffset is a pointer to the actual data:
28870         dataOffset = tagSize > 4 ?
28871                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28872         if (dataOffset + tagSize > dataView.byteLength) {
28873             Roo.log('Invalid Exif data: Invalid data offset.');
28874             return;
28875         }
28876         if (length === 1) {
28877             return tagType.getValue(dataView, dataOffset, littleEndian);
28878         }
28879         values = [];
28880         for (i = 0; i < length; i += 1) {
28881             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28882         }
28883         
28884         if (tagType.ascii) {
28885             str = '';
28886             // Concatenate the chars:
28887             for (i = 0; i < values.length; i += 1) {
28888                 c = values[i];
28889                 // Ignore the terminating NULL byte(s):
28890                 if (c === '\u0000') {
28891                     break;
28892                 }
28893                 str += c;
28894             }
28895             return str;
28896         }
28897         return values;
28898     }
28899     
28900 });
28901
28902 Roo.apply(Roo.bootstrap.UploadCropbox, {
28903     tags : {
28904         'Orientation': 0x0112
28905     },
28906     
28907     Orientation: {
28908             1: 0, //'top-left',
28909 //            2: 'top-right',
28910             3: 180, //'bottom-right',
28911 //            4: 'bottom-left',
28912 //            5: 'left-top',
28913             6: 90, //'right-top',
28914 //            7: 'right-bottom',
28915             8: 270 //'left-bottom'
28916     },
28917     
28918     exifTagTypes : {
28919         // byte, 8-bit unsigned int:
28920         1: {
28921             getValue: function (dataView, dataOffset) {
28922                 return dataView.getUint8(dataOffset);
28923             },
28924             size: 1
28925         },
28926         // ascii, 8-bit byte:
28927         2: {
28928             getValue: function (dataView, dataOffset) {
28929                 return String.fromCharCode(dataView.getUint8(dataOffset));
28930             },
28931             size: 1,
28932             ascii: true
28933         },
28934         // short, 16 bit int:
28935         3: {
28936             getValue: function (dataView, dataOffset, littleEndian) {
28937                 return dataView.getUint16(dataOffset, littleEndian);
28938             },
28939             size: 2
28940         },
28941         // long, 32 bit int:
28942         4: {
28943             getValue: function (dataView, dataOffset, littleEndian) {
28944                 return dataView.getUint32(dataOffset, littleEndian);
28945             },
28946             size: 4
28947         },
28948         // rational = two long values, first is numerator, second is denominator:
28949         5: {
28950             getValue: function (dataView, dataOffset, littleEndian) {
28951                 return dataView.getUint32(dataOffset, littleEndian) /
28952                     dataView.getUint32(dataOffset + 4, littleEndian);
28953             },
28954             size: 8
28955         },
28956         // slong, 32 bit signed int:
28957         9: {
28958             getValue: function (dataView, dataOffset, littleEndian) {
28959                 return dataView.getInt32(dataOffset, littleEndian);
28960             },
28961             size: 4
28962         },
28963         // srational, two slongs, first is numerator, second is denominator:
28964         10: {
28965             getValue: function (dataView, dataOffset, littleEndian) {
28966                 return dataView.getInt32(dataOffset, littleEndian) /
28967                     dataView.getInt32(dataOffset + 4, littleEndian);
28968             },
28969             size: 8
28970         }
28971     },
28972     
28973     footer : {
28974         STANDARD : [
28975             {
28976                 tag : 'div',
28977                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28978                 action : 'rotate-left',
28979                 cn : [
28980                     {
28981                         tag : 'button',
28982                         cls : 'btn btn-default',
28983                         html : '<i class="fa fa-undo"></i>'
28984                     }
28985                 ]
28986             },
28987             {
28988                 tag : 'div',
28989                 cls : 'btn-group roo-upload-cropbox-picture',
28990                 action : 'picture',
28991                 cn : [
28992                     {
28993                         tag : 'button',
28994                         cls : 'btn btn-default',
28995                         html : '<i class="fa fa-picture-o"></i>'
28996                     }
28997                 ]
28998             },
28999             {
29000                 tag : 'div',
29001                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29002                 action : 'rotate-right',
29003                 cn : [
29004                     {
29005                         tag : 'button',
29006                         cls : 'btn btn-default',
29007                         html : '<i class="fa fa-repeat"></i>'
29008                     }
29009                 ]
29010             }
29011         ],
29012         DOCUMENT : [
29013             {
29014                 tag : 'div',
29015                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29016                 action : 'rotate-left',
29017                 cn : [
29018                     {
29019                         tag : 'button',
29020                         cls : 'btn btn-default',
29021                         html : '<i class="fa fa-undo"></i>'
29022                     }
29023                 ]
29024             },
29025             {
29026                 tag : 'div',
29027                 cls : 'btn-group roo-upload-cropbox-download',
29028                 action : 'download',
29029                 cn : [
29030                     {
29031                         tag : 'button',
29032                         cls : 'btn btn-default',
29033                         html : '<i class="fa fa-download"></i>'
29034                     }
29035                 ]
29036             },
29037             {
29038                 tag : 'div',
29039                 cls : 'btn-group roo-upload-cropbox-crop',
29040                 action : 'crop',
29041                 cn : [
29042                     {
29043                         tag : 'button',
29044                         cls : 'btn btn-default',
29045                         html : '<i class="fa fa-crop"></i>'
29046                     }
29047                 ]
29048             },
29049             {
29050                 tag : 'div',
29051                 cls : 'btn-group roo-upload-cropbox-trash',
29052                 action : 'trash',
29053                 cn : [
29054                     {
29055                         tag : 'button',
29056                         cls : 'btn btn-default',
29057                         html : '<i class="fa fa-trash"></i>'
29058                     }
29059                 ]
29060             },
29061             {
29062                 tag : 'div',
29063                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29064                 action : 'rotate-right',
29065                 cn : [
29066                     {
29067                         tag : 'button',
29068                         cls : 'btn btn-default',
29069                         html : '<i class="fa fa-repeat"></i>'
29070                     }
29071                 ]
29072             }
29073         ],
29074         ROTATOR : [
29075             {
29076                 tag : 'div',
29077                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29078                 action : 'rotate-left',
29079                 cn : [
29080                     {
29081                         tag : 'button',
29082                         cls : 'btn btn-default',
29083                         html : '<i class="fa fa-undo"></i>'
29084                     }
29085                 ]
29086             },
29087             {
29088                 tag : 'div',
29089                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29090                 action : 'rotate-right',
29091                 cn : [
29092                     {
29093                         tag : 'button',
29094                         cls : 'btn btn-default',
29095                         html : '<i class="fa fa-repeat"></i>'
29096                     }
29097                 ]
29098             }
29099         ]
29100     }
29101 });
29102
29103 /*
29104 * Licence: LGPL
29105 */
29106
29107 /**
29108  * @class Roo.bootstrap.DocumentManager
29109  * @extends Roo.bootstrap.Component
29110  * Bootstrap DocumentManager class
29111  * @cfg {String} paramName default 'imageUpload'
29112  * @cfg {String} toolTipName default 'filename'
29113  * @cfg {String} method default POST
29114  * @cfg {String} url action url
29115  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29116  * @cfg {Boolean} multiple multiple upload default true
29117  * @cfg {Number} thumbSize default 300
29118  * @cfg {String} fieldLabel
29119  * @cfg {Number} labelWidth default 4
29120  * @cfg {String} labelAlign (left|top) default left
29121  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29122 * @cfg {Number} labellg set the width of label (1-12)
29123  * @cfg {Number} labelmd set the width of label (1-12)
29124  * @cfg {Number} labelsm set the width of label (1-12)
29125  * @cfg {Number} labelxs set the width of label (1-12)
29126  * 
29127  * @constructor
29128  * Create a new DocumentManager
29129  * @param {Object} config The config object
29130  */
29131
29132 Roo.bootstrap.DocumentManager = function(config){
29133     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29134     
29135     this.files = [];
29136     this.delegates = [];
29137     
29138     this.addEvents({
29139         /**
29140          * @event initial
29141          * Fire when initial the DocumentManager
29142          * @param {Roo.bootstrap.DocumentManager} this
29143          */
29144         "initial" : true,
29145         /**
29146          * @event inspect
29147          * inspect selected file
29148          * @param {Roo.bootstrap.DocumentManager} this
29149          * @param {File} file
29150          */
29151         "inspect" : true,
29152         /**
29153          * @event exception
29154          * Fire when xhr load exception
29155          * @param {Roo.bootstrap.DocumentManager} this
29156          * @param {XMLHttpRequest} xhr
29157          */
29158         "exception" : true,
29159         /**
29160          * @event afterupload
29161          * Fire when xhr load exception
29162          * @param {Roo.bootstrap.DocumentManager} this
29163          * @param {XMLHttpRequest} xhr
29164          */
29165         "afterupload" : true,
29166         /**
29167          * @event prepare
29168          * prepare the form data
29169          * @param {Roo.bootstrap.DocumentManager} this
29170          * @param {Object} formData
29171          */
29172         "prepare" : true,
29173         /**
29174          * @event remove
29175          * Fire when remove the file
29176          * @param {Roo.bootstrap.DocumentManager} this
29177          * @param {Object} file
29178          */
29179         "remove" : true,
29180         /**
29181          * @event refresh
29182          * Fire after refresh the file
29183          * @param {Roo.bootstrap.DocumentManager} this
29184          */
29185         "refresh" : true,
29186         /**
29187          * @event click
29188          * Fire after click the image
29189          * @param {Roo.bootstrap.DocumentManager} this
29190          * @param {Object} file
29191          */
29192         "click" : true,
29193         /**
29194          * @event edit
29195          * Fire when upload a image and editable set to true
29196          * @param {Roo.bootstrap.DocumentManager} this
29197          * @param {Object} file
29198          */
29199         "edit" : true,
29200         /**
29201          * @event beforeselectfile
29202          * Fire before select file
29203          * @param {Roo.bootstrap.DocumentManager} this
29204          */
29205         "beforeselectfile" : true,
29206         /**
29207          * @event process
29208          * Fire before process file
29209          * @param {Roo.bootstrap.DocumentManager} this
29210          * @param {Object} file
29211          */
29212         "process" : true,
29213         /**
29214          * @event previewrendered
29215          * Fire when preview rendered
29216          * @param {Roo.bootstrap.DocumentManager} this
29217          * @param {Object} file
29218          */
29219         "previewrendered" : true,
29220         /**
29221          */
29222         "previewResize" : true
29223         
29224     });
29225 };
29226
29227 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29228     
29229     boxes : 0,
29230     inputName : '',
29231     thumbSize : 300,
29232     multiple : true,
29233     files : false,
29234     method : 'POST',
29235     url : '',
29236     paramName : 'imageUpload',
29237     toolTipName : 'filename',
29238     fieldLabel : '',
29239     labelWidth : 4,
29240     labelAlign : 'left',
29241     editable : true,
29242     delegates : false,
29243     xhr : false, 
29244     
29245     labellg : 0,
29246     labelmd : 0,
29247     labelsm : 0,
29248     labelxs : 0,
29249     
29250     getAutoCreate : function()
29251     {   
29252         var managerWidget = {
29253             tag : 'div',
29254             cls : 'roo-document-manager',
29255             cn : [
29256                 {
29257                     tag : 'input',
29258                     cls : 'roo-document-manager-selector',
29259                     type : 'file'
29260                 },
29261                 {
29262                     tag : 'div',
29263                     cls : 'roo-document-manager-uploader',
29264                     cn : [
29265                         {
29266                             tag : 'div',
29267                             cls : 'roo-document-manager-upload-btn',
29268                             html : '<i class="fa fa-plus"></i>'
29269                         }
29270                     ]
29271                     
29272                 }
29273             ]
29274         };
29275         
29276         var content = [
29277             {
29278                 tag : 'div',
29279                 cls : 'column col-md-12',
29280                 cn : managerWidget
29281             }
29282         ];
29283         
29284         if(this.fieldLabel.length){
29285             
29286             content = [
29287                 {
29288                     tag : 'div',
29289                     cls : 'column col-md-12',
29290                     html : this.fieldLabel
29291                 },
29292                 {
29293                     tag : 'div',
29294                     cls : 'column col-md-12',
29295                     cn : managerWidget
29296                 }
29297             ];
29298
29299             if(this.labelAlign == 'left'){
29300                 content = [
29301                     {
29302                         tag : 'div',
29303                         cls : 'column',
29304                         html : this.fieldLabel
29305                     },
29306                     {
29307                         tag : 'div',
29308                         cls : 'column',
29309                         cn : managerWidget
29310                     }
29311                 ];
29312                 
29313                 if(this.labelWidth > 12){
29314                     content[0].style = "width: " + this.labelWidth + 'px';
29315                 }
29316
29317                 if(this.labelWidth < 13 && this.labelmd == 0){
29318                     this.labelmd = this.labelWidth;
29319                 }
29320
29321                 if(this.labellg > 0){
29322                     content[0].cls += ' col-lg-' + this.labellg;
29323                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29324                 }
29325
29326                 if(this.labelmd > 0){
29327                     content[0].cls += ' col-md-' + this.labelmd;
29328                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29329                 }
29330
29331                 if(this.labelsm > 0){
29332                     content[0].cls += ' col-sm-' + this.labelsm;
29333                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29334                 }
29335
29336                 if(this.labelxs > 0){
29337                     content[0].cls += ' col-xs-' + this.labelxs;
29338                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29339                 }
29340                 
29341             }
29342         }
29343         
29344         var cfg = {
29345             tag : 'div',
29346             cls : 'row clearfix',
29347             cn : content
29348         };
29349         
29350         return cfg;
29351         
29352     },
29353     
29354     initEvents : function()
29355     {
29356         this.managerEl = this.el.select('.roo-document-manager', true).first();
29357         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29358         
29359         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29360         this.selectorEl.hide();
29361         
29362         if(this.multiple){
29363             this.selectorEl.attr('multiple', 'multiple');
29364         }
29365         
29366         this.selectorEl.on('change', this.onFileSelected, this);
29367         
29368         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29369         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29370         
29371         this.uploader.on('click', this.onUploaderClick, this);
29372         
29373         this.renderProgressDialog();
29374         
29375         var _this = this;
29376         
29377         window.addEventListener("resize", function() { _this.refresh(); } );
29378         
29379         this.fireEvent('initial', this);
29380     },
29381     
29382     renderProgressDialog : function()
29383     {
29384         var _this = this;
29385         
29386         this.progressDialog = new Roo.bootstrap.Modal({
29387             cls : 'roo-document-manager-progress-dialog',
29388             allow_close : false,
29389             animate : false,
29390             title : '',
29391             buttons : [
29392                 {
29393                     name  :'cancel',
29394                     weight : 'danger',
29395                     html : 'Cancel'
29396                 }
29397             ], 
29398             listeners : { 
29399                 btnclick : function() {
29400                     _this.uploadCancel();
29401                     this.hide();
29402                 }
29403             }
29404         });
29405          
29406         this.progressDialog.render(Roo.get(document.body));
29407          
29408         this.progress = new Roo.bootstrap.Progress({
29409             cls : 'roo-document-manager-progress',
29410             active : true,
29411             striped : true
29412         });
29413         
29414         this.progress.render(this.progressDialog.getChildContainer());
29415         
29416         this.progressBar = new Roo.bootstrap.ProgressBar({
29417             cls : 'roo-document-manager-progress-bar',
29418             aria_valuenow : 0,
29419             aria_valuemin : 0,
29420             aria_valuemax : 12,
29421             panel : 'success'
29422         });
29423         
29424         this.progressBar.render(this.progress.getChildContainer());
29425     },
29426     
29427     onUploaderClick : function(e)
29428     {
29429         e.preventDefault();
29430      
29431         if(this.fireEvent('beforeselectfile', this) != false){
29432             this.selectorEl.dom.click();
29433         }
29434         
29435     },
29436     
29437     onFileSelected : function(e)
29438     {
29439         e.preventDefault();
29440         
29441         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29442             return;
29443         }
29444         
29445         Roo.each(this.selectorEl.dom.files, function(file){
29446             if(this.fireEvent('inspect', this, file) != false){
29447                 this.files.push(file);
29448             }
29449         }, this);
29450         
29451         this.queue();
29452         
29453     },
29454     
29455     queue : function()
29456     {
29457         this.selectorEl.dom.value = '';
29458         
29459         if(!this.files || !this.files.length){
29460             return;
29461         }
29462         
29463         if(this.boxes > 0 && this.files.length > this.boxes){
29464             this.files = this.files.slice(0, this.boxes);
29465         }
29466         
29467         this.uploader.show();
29468         
29469         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29470             this.uploader.hide();
29471         }
29472         
29473         var _this = this;
29474         
29475         var files = [];
29476         
29477         var docs = [];
29478         
29479         Roo.each(this.files, function(file){
29480             
29481             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29482                 var f = this.renderPreview(file);
29483                 files.push(f);
29484                 return;
29485             }
29486             
29487             if(file.type.indexOf('image') != -1){
29488                 this.delegates.push(
29489                     (function(){
29490                         _this.process(file);
29491                     }).createDelegate(this)
29492                 );
29493         
29494                 return;
29495             }
29496             
29497             docs.push(
29498                 (function(){
29499                     _this.process(file);
29500                 }).createDelegate(this)
29501             );
29502             
29503         }, this);
29504         
29505         this.files = files;
29506         
29507         this.delegates = this.delegates.concat(docs);
29508         
29509         if(!this.delegates.length){
29510             this.refresh();
29511             return;
29512         }
29513         
29514         this.progressBar.aria_valuemax = this.delegates.length;
29515         
29516         this.arrange();
29517         
29518         return;
29519     },
29520     
29521     arrange : function()
29522     {
29523         if(!this.delegates.length){
29524             this.progressDialog.hide();
29525             this.refresh();
29526             return;
29527         }
29528         
29529         var delegate = this.delegates.shift();
29530         
29531         this.progressDialog.show();
29532         
29533         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29534         
29535         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29536         
29537         delegate();
29538     },
29539     
29540     refresh : function()
29541     {
29542         this.uploader.show();
29543         
29544         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29545             this.uploader.hide();
29546         }
29547         
29548         Roo.isTouch ? this.closable(false) : this.closable(true);
29549         
29550         this.fireEvent('refresh', this);
29551     },
29552     
29553     onRemove : function(e, el, o)
29554     {
29555         e.preventDefault();
29556         
29557         this.fireEvent('remove', this, o);
29558         
29559     },
29560     
29561     remove : function(o)
29562     {
29563         var files = [];
29564         
29565         Roo.each(this.files, function(file){
29566             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29567                 files.push(file);
29568                 return;
29569             }
29570
29571             o.target.remove();
29572
29573         }, this);
29574         
29575         this.files = files;
29576         
29577         this.refresh();
29578     },
29579     
29580     clear : function()
29581     {
29582         Roo.each(this.files, function(file){
29583             if(!file.target){
29584                 return;
29585             }
29586             
29587             file.target.remove();
29588
29589         }, this);
29590         
29591         this.files = [];
29592         
29593         this.refresh();
29594     },
29595     
29596     onClick : function(e, el, o)
29597     {
29598         e.preventDefault();
29599         
29600         this.fireEvent('click', this, o);
29601         
29602     },
29603     
29604     closable : function(closable)
29605     {
29606         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29607             
29608             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29609             
29610             if(closable){
29611                 el.show();
29612                 return;
29613             }
29614             
29615             el.hide();
29616             
29617         }, this);
29618     },
29619     
29620     xhrOnLoad : function(xhr)
29621     {
29622         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29623             el.remove();
29624         }, this);
29625         
29626         if (xhr.readyState !== 4) {
29627             this.arrange();
29628             this.fireEvent('exception', this, xhr);
29629             return;
29630         }
29631
29632         var response = Roo.decode(xhr.responseText);
29633         
29634         if(!response.success){
29635             this.arrange();
29636             this.fireEvent('exception', this, xhr);
29637             return;
29638         }
29639         
29640         var file = this.renderPreview(response.data);
29641         
29642         this.files.push(file);
29643         
29644         this.arrange();
29645         
29646         this.fireEvent('afterupload', this, xhr);
29647         
29648     },
29649     
29650     xhrOnError : function(xhr)
29651     {
29652         Roo.log('xhr on error');
29653         
29654         var response = Roo.decode(xhr.responseText);
29655           
29656         Roo.log(response);
29657         
29658         this.arrange();
29659     },
29660     
29661     process : function(file)
29662     {
29663         if(this.fireEvent('process', this, file) !== false){
29664             if(this.editable && file.type.indexOf('image') != -1){
29665                 this.fireEvent('edit', this, file);
29666                 return;
29667             }
29668
29669             this.uploadStart(file, false);
29670
29671             return;
29672         }
29673         
29674     },
29675     
29676     uploadStart : function(file, crop)
29677     {
29678         this.xhr = new XMLHttpRequest();
29679         
29680         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29681             this.arrange();
29682             return;
29683         }
29684         
29685         file.xhr = this.xhr;
29686             
29687         this.managerEl.createChild({
29688             tag : 'div',
29689             cls : 'roo-document-manager-loading',
29690             cn : [
29691                 {
29692                     tag : 'div',
29693                     tooltip : file.name,
29694                     cls : 'roo-document-manager-thumb',
29695                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29696                 }
29697             ]
29698
29699         });
29700
29701         this.xhr.open(this.method, this.url, true);
29702         
29703         var headers = {
29704             "Accept": "application/json",
29705             "Cache-Control": "no-cache",
29706             "X-Requested-With": "XMLHttpRequest"
29707         };
29708         
29709         for (var headerName in headers) {
29710             var headerValue = headers[headerName];
29711             if (headerValue) {
29712                 this.xhr.setRequestHeader(headerName, headerValue);
29713             }
29714         }
29715         
29716         var _this = this;
29717         
29718         this.xhr.onload = function()
29719         {
29720             _this.xhrOnLoad(_this.xhr);
29721         }
29722         
29723         this.xhr.onerror = function()
29724         {
29725             _this.xhrOnError(_this.xhr);
29726         }
29727         
29728         var formData = new FormData();
29729
29730         formData.append('returnHTML', 'NO');
29731         
29732         if(crop){
29733             formData.append('crop', crop);
29734         }
29735         
29736         formData.append(this.paramName, file, file.name);
29737         
29738         var options = {
29739             file : file, 
29740             manually : false
29741         };
29742         
29743         if(this.fireEvent('prepare', this, formData, options) != false){
29744             
29745             if(options.manually){
29746                 return;
29747             }
29748             
29749             this.xhr.send(formData);
29750             return;
29751         };
29752         
29753         this.uploadCancel();
29754     },
29755     
29756     uploadCancel : function()
29757     {
29758         if (this.xhr) {
29759             this.xhr.abort();
29760         }
29761         
29762         this.delegates = [];
29763         
29764         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29765             el.remove();
29766         }, this);
29767         
29768         this.arrange();
29769     },
29770     
29771     renderPreview : function(file)
29772     {
29773         if(typeof(file.target) != 'undefined' && file.target){
29774             return file;
29775         }
29776         
29777         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29778         
29779         var previewEl = this.managerEl.createChild({
29780             tag : 'div',
29781             cls : 'roo-document-manager-preview',
29782             cn : [
29783                 {
29784                     tag : 'div',
29785                     tooltip : file[this.toolTipName],
29786                     cls : 'roo-document-manager-thumb',
29787                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29788                 },
29789                 {
29790                     tag : 'button',
29791                     cls : 'close',
29792                     html : '<i class="fa fa-times-circle"></i>'
29793                 }
29794             ]
29795         });
29796
29797         var close = previewEl.select('button.close', true).first();
29798
29799         close.on('click', this.onRemove, this, file);
29800
29801         file.target = previewEl;
29802
29803         var image = previewEl.select('img', true).first();
29804         
29805         var _this = this;
29806         
29807         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29808         
29809         image.on('click', this.onClick, this, file);
29810         
29811         this.fireEvent('previewrendered', this, file);
29812         
29813         return file;
29814         
29815     },
29816     
29817     onPreviewLoad : function(file, image)
29818     {
29819         if(typeof(file.target) == 'undefined' || !file.target){
29820             return;
29821         }
29822         
29823         var width = image.dom.naturalWidth || image.dom.width;
29824         var height = image.dom.naturalHeight || image.dom.height;
29825         
29826         if(!this.previewResize) {
29827             return;
29828         }
29829         
29830         if(width > height){
29831             file.target.addClass('wide');
29832             return;
29833         }
29834         
29835         file.target.addClass('tall');
29836         return;
29837         
29838     },
29839     
29840     uploadFromSource : function(file, crop)
29841     {
29842         this.xhr = new XMLHttpRequest();
29843         
29844         this.managerEl.createChild({
29845             tag : 'div',
29846             cls : 'roo-document-manager-loading',
29847             cn : [
29848                 {
29849                     tag : 'div',
29850                     tooltip : file.name,
29851                     cls : 'roo-document-manager-thumb',
29852                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29853                 }
29854             ]
29855
29856         });
29857
29858         this.xhr.open(this.method, this.url, true);
29859         
29860         var headers = {
29861             "Accept": "application/json",
29862             "Cache-Control": "no-cache",
29863             "X-Requested-With": "XMLHttpRequest"
29864         };
29865         
29866         for (var headerName in headers) {
29867             var headerValue = headers[headerName];
29868             if (headerValue) {
29869                 this.xhr.setRequestHeader(headerName, headerValue);
29870             }
29871         }
29872         
29873         var _this = this;
29874         
29875         this.xhr.onload = function()
29876         {
29877             _this.xhrOnLoad(_this.xhr);
29878         }
29879         
29880         this.xhr.onerror = function()
29881         {
29882             _this.xhrOnError(_this.xhr);
29883         }
29884         
29885         var formData = new FormData();
29886
29887         formData.append('returnHTML', 'NO');
29888         
29889         formData.append('crop', crop);
29890         
29891         if(typeof(file.filename) != 'undefined'){
29892             formData.append('filename', file.filename);
29893         }
29894         
29895         if(typeof(file.mimetype) != 'undefined'){
29896             formData.append('mimetype', file.mimetype);
29897         }
29898         
29899         Roo.log(formData);
29900         
29901         if(this.fireEvent('prepare', this, formData) != false){
29902             this.xhr.send(formData);
29903         };
29904     }
29905 });
29906
29907 /*
29908 * Licence: LGPL
29909 */
29910
29911 /**
29912  * @class Roo.bootstrap.DocumentViewer
29913  * @extends Roo.bootstrap.Component
29914  * Bootstrap DocumentViewer class
29915  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29916  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29917  * 
29918  * @constructor
29919  * Create a new DocumentViewer
29920  * @param {Object} config The config object
29921  */
29922
29923 Roo.bootstrap.DocumentViewer = function(config){
29924     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29925     
29926     this.addEvents({
29927         /**
29928          * @event initial
29929          * Fire after initEvent
29930          * @param {Roo.bootstrap.DocumentViewer} this
29931          */
29932         "initial" : true,
29933         /**
29934          * @event click
29935          * Fire after click
29936          * @param {Roo.bootstrap.DocumentViewer} this
29937          */
29938         "click" : true,
29939         /**
29940          * @event download
29941          * Fire after download button
29942          * @param {Roo.bootstrap.DocumentViewer} this
29943          */
29944         "download" : true,
29945         /**
29946          * @event trash
29947          * Fire after trash button
29948          * @param {Roo.bootstrap.DocumentViewer} this
29949          */
29950         "trash" : true
29951         
29952     });
29953 };
29954
29955 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29956     
29957     showDownload : true,
29958     
29959     showTrash : true,
29960     
29961     getAutoCreate : function()
29962     {
29963         var cfg = {
29964             tag : 'div',
29965             cls : 'roo-document-viewer',
29966             cn : [
29967                 {
29968                     tag : 'div',
29969                     cls : 'roo-document-viewer-body',
29970                     cn : [
29971                         {
29972                             tag : 'div',
29973                             cls : 'roo-document-viewer-thumb',
29974                             cn : [
29975                                 {
29976                                     tag : 'img',
29977                                     cls : 'roo-document-viewer-image'
29978                                 }
29979                             ]
29980                         }
29981                     ]
29982                 },
29983                 {
29984                     tag : 'div',
29985                     cls : 'roo-document-viewer-footer',
29986                     cn : {
29987                         tag : 'div',
29988                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29989                         cn : [
29990                             {
29991                                 tag : 'div',
29992                                 cls : 'btn-group roo-document-viewer-download',
29993                                 cn : [
29994                                     {
29995                                         tag : 'button',
29996                                         cls : 'btn btn-default',
29997                                         html : '<i class="fa fa-download"></i>'
29998                                     }
29999                                 ]
30000                             },
30001                             {
30002                                 tag : 'div',
30003                                 cls : 'btn-group roo-document-viewer-trash',
30004                                 cn : [
30005                                     {
30006                                         tag : 'button',
30007                                         cls : 'btn btn-default',
30008                                         html : '<i class="fa fa-trash"></i>'
30009                                     }
30010                                 ]
30011                             }
30012                         ]
30013                     }
30014                 }
30015             ]
30016         };
30017         
30018         return cfg;
30019     },
30020     
30021     initEvents : function()
30022     {
30023         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30024         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30025         
30026         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30027         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30028         
30029         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30030         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30031         
30032         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30033         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30034         
30035         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30036         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30037         
30038         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30039         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30040         
30041         this.bodyEl.on('click', this.onClick, this);
30042         this.downloadBtn.on('click', this.onDownload, this);
30043         this.trashBtn.on('click', this.onTrash, this);
30044         
30045         this.downloadBtn.hide();
30046         this.trashBtn.hide();
30047         
30048         if(this.showDownload){
30049             this.downloadBtn.show();
30050         }
30051         
30052         if(this.showTrash){
30053             this.trashBtn.show();
30054         }
30055         
30056         if(!this.showDownload && !this.showTrash) {
30057             this.footerEl.hide();
30058         }
30059         
30060     },
30061     
30062     initial : function()
30063     {
30064         this.fireEvent('initial', this);
30065         
30066     },
30067     
30068     onClick : function(e)
30069     {
30070         e.preventDefault();
30071         
30072         this.fireEvent('click', this);
30073     },
30074     
30075     onDownload : function(e)
30076     {
30077         e.preventDefault();
30078         
30079         this.fireEvent('download', this);
30080     },
30081     
30082     onTrash : function(e)
30083     {
30084         e.preventDefault();
30085         
30086         this.fireEvent('trash', this);
30087     }
30088     
30089 });
30090 /*
30091  * - LGPL
30092  *
30093  * nav progress bar
30094  * 
30095  */
30096
30097 /**
30098  * @class Roo.bootstrap.NavProgressBar
30099  * @extends Roo.bootstrap.Component
30100  * Bootstrap NavProgressBar class
30101  * 
30102  * @constructor
30103  * Create a new nav progress bar
30104  * @param {Object} config The config object
30105  */
30106
30107 Roo.bootstrap.NavProgressBar = function(config){
30108     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30109
30110     this.bullets = this.bullets || [];
30111    
30112 //    Roo.bootstrap.NavProgressBar.register(this);
30113      this.addEvents({
30114         /**
30115              * @event changed
30116              * Fires when the active item changes
30117              * @param {Roo.bootstrap.NavProgressBar} this
30118              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30119              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30120          */
30121         'changed': true
30122      });
30123     
30124 };
30125
30126 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30127     
30128     bullets : [],
30129     barItems : [],
30130     
30131     getAutoCreate : function()
30132     {
30133         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30134         
30135         cfg = {
30136             tag : 'div',
30137             cls : 'roo-navigation-bar-group',
30138             cn : [
30139                 {
30140                     tag : 'div',
30141                     cls : 'roo-navigation-top-bar'
30142                 },
30143                 {
30144                     tag : 'div',
30145                     cls : 'roo-navigation-bullets-bar',
30146                     cn : [
30147                         {
30148                             tag : 'ul',
30149                             cls : 'roo-navigation-bar'
30150                         }
30151                     ]
30152                 },
30153                 
30154                 {
30155                     tag : 'div',
30156                     cls : 'roo-navigation-bottom-bar'
30157                 }
30158             ]
30159             
30160         };
30161         
30162         return cfg;
30163         
30164     },
30165     
30166     initEvents: function() 
30167     {
30168         
30169     },
30170     
30171     onRender : function(ct, position) 
30172     {
30173         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30174         
30175         if(this.bullets.length){
30176             Roo.each(this.bullets, function(b){
30177                this.addItem(b);
30178             }, this);
30179         }
30180         
30181         this.format();
30182         
30183     },
30184     
30185     addItem : function(cfg)
30186     {
30187         var item = new Roo.bootstrap.NavProgressItem(cfg);
30188         
30189         item.parentId = this.id;
30190         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30191         
30192         if(cfg.html){
30193             var top = new Roo.bootstrap.Element({
30194                 tag : 'div',
30195                 cls : 'roo-navigation-bar-text'
30196             });
30197             
30198             var bottom = new Roo.bootstrap.Element({
30199                 tag : 'div',
30200                 cls : 'roo-navigation-bar-text'
30201             });
30202             
30203             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30204             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30205             
30206             var topText = new Roo.bootstrap.Element({
30207                 tag : 'span',
30208                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30209             });
30210             
30211             var bottomText = new Roo.bootstrap.Element({
30212                 tag : 'span',
30213                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30214             });
30215             
30216             topText.onRender(top.el, null);
30217             bottomText.onRender(bottom.el, null);
30218             
30219             item.topEl = top;
30220             item.bottomEl = bottom;
30221         }
30222         
30223         this.barItems.push(item);
30224         
30225         return item;
30226     },
30227     
30228     getActive : function()
30229     {
30230         var active = false;
30231         
30232         Roo.each(this.barItems, function(v){
30233             
30234             if (!v.isActive()) {
30235                 return;
30236             }
30237             
30238             active = v;
30239             return false;
30240             
30241         });
30242         
30243         return active;
30244     },
30245     
30246     setActiveItem : function(item)
30247     {
30248         var prev = false;
30249         
30250         Roo.each(this.barItems, function(v){
30251             if (v.rid == item.rid) {
30252                 return ;
30253             }
30254             
30255             if (v.isActive()) {
30256                 v.setActive(false);
30257                 prev = v;
30258             }
30259         });
30260
30261         item.setActive(true);
30262         
30263         this.fireEvent('changed', this, item, prev);
30264     },
30265     
30266     getBarItem: function(rid)
30267     {
30268         var ret = false;
30269         
30270         Roo.each(this.barItems, function(e) {
30271             if (e.rid != rid) {
30272                 return;
30273             }
30274             
30275             ret =  e;
30276             return false;
30277         });
30278         
30279         return ret;
30280     },
30281     
30282     indexOfItem : function(item)
30283     {
30284         var index = false;
30285         
30286         Roo.each(this.barItems, function(v, i){
30287             
30288             if (v.rid != item.rid) {
30289                 return;
30290             }
30291             
30292             index = i;
30293             return false
30294         });
30295         
30296         return index;
30297     },
30298     
30299     setActiveNext : function()
30300     {
30301         var i = this.indexOfItem(this.getActive());
30302         
30303         if (i > this.barItems.length) {
30304             return;
30305         }
30306         
30307         this.setActiveItem(this.barItems[i+1]);
30308     },
30309     
30310     setActivePrev : function()
30311     {
30312         var i = this.indexOfItem(this.getActive());
30313         
30314         if (i  < 1) {
30315             return;
30316         }
30317         
30318         this.setActiveItem(this.barItems[i-1]);
30319     },
30320     
30321     format : function()
30322     {
30323         if(!this.barItems.length){
30324             return;
30325         }
30326      
30327         var width = 100 / this.barItems.length;
30328         
30329         Roo.each(this.barItems, function(i){
30330             i.el.setStyle('width', width + '%');
30331             i.topEl.el.setStyle('width', width + '%');
30332             i.bottomEl.el.setStyle('width', width + '%');
30333         }, this);
30334         
30335     }
30336     
30337 });
30338 /*
30339  * - LGPL
30340  *
30341  * Nav Progress Item
30342  * 
30343  */
30344
30345 /**
30346  * @class Roo.bootstrap.NavProgressItem
30347  * @extends Roo.bootstrap.Component
30348  * Bootstrap NavProgressItem class
30349  * @cfg {String} rid the reference id
30350  * @cfg {Boolean} active (true|false) Is item active default false
30351  * @cfg {Boolean} disabled (true|false) Is item active default false
30352  * @cfg {String} html
30353  * @cfg {String} position (top|bottom) text position default bottom
30354  * @cfg {String} icon show icon instead of number
30355  * 
30356  * @constructor
30357  * Create a new NavProgressItem
30358  * @param {Object} config The config object
30359  */
30360 Roo.bootstrap.NavProgressItem = function(config){
30361     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30362     this.addEvents({
30363         // raw events
30364         /**
30365          * @event click
30366          * The raw click event for the entire grid.
30367          * @param {Roo.bootstrap.NavProgressItem} this
30368          * @param {Roo.EventObject} e
30369          */
30370         "click" : true
30371     });
30372    
30373 };
30374
30375 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30376     
30377     rid : '',
30378     active : false,
30379     disabled : false,
30380     html : '',
30381     position : 'bottom',
30382     icon : false,
30383     
30384     getAutoCreate : function()
30385     {
30386         var iconCls = 'roo-navigation-bar-item-icon';
30387         
30388         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30389         
30390         var cfg = {
30391             tag: 'li',
30392             cls: 'roo-navigation-bar-item',
30393             cn : [
30394                 {
30395                     tag : 'i',
30396                     cls : iconCls
30397                 }
30398             ]
30399         };
30400         
30401         if(this.active){
30402             cfg.cls += ' active';
30403         }
30404         if(this.disabled){
30405             cfg.cls += ' disabled';
30406         }
30407         
30408         return cfg;
30409     },
30410     
30411     disable : function()
30412     {
30413         this.setDisabled(true);
30414     },
30415     
30416     enable : function()
30417     {
30418         this.setDisabled(false);
30419     },
30420     
30421     initEvents: function() 
30422     {
30423         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30424         
30425         this.iconEl.on('click', this.onClick, this);
30426     },
30427     
30428     onClick : function(e)
30429     {
30430         e.preventDefault();
30431         
30432         if(this.disabled){
30433             return;
30434         }
30435         
30436         if(this.fireEvent('click', this, e) === false){
30437             return;
30438         };
30439         
30440         this.parent().setActiveItem(this);
30441     },
30442     
30443     isActive: function () 
30444     {
30445         return this.active;
30446     },
30447     
30448     setActive : function(state)
30449     {
30450         if(this.active == state){
30451             return;
30452         }
30453         
30454         this.active = state;
30455         
30456         if (state) {
30457             this.el.addClass('active');
30458             return;
30459         }
30460         
30461         this.el.removeClass('active');
30462         
30463         return;
30464     },
30465     
30466     setDisabled : function(state)
30467     {
30468         if(this.disabled == state){
30469             return;
30470         }
30471         
30472         this.disabled = state;
30473         
30474         if (state) {
30475             this.el.addClass('disabled');
30476             return;
30477         }
30478         
30479         this.el.removeClass('disabled');
30480     },
30481     
30482     tooltipEl : function()
30483     {
30484         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30485     }
30486 });
30487  
30488
30489  /*
30490  * - LGPL
30491  *
30492  * FieldLabel
30493  * 
30494  */
30495
30496 /**
30497  * @class Roo.bootstrap.FieldLabel
30498  * @extends Roo.bootstrap.Component
30499  * Bootstrap FieldLabel class
30500  * @cfg {String} html contents of the element
30501  * @cfg {String} tag tag of the element default label
30502  * @cfg {String} cls class of the element
30503  * @cfg {String} target label target 
30504  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30505  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30506  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30507  * @cfg {String} iconTooltip default "This field is required"
30508  * @cfg {String} indicatorpos (left|right) default left
30509  * 
30510  * @constructor
30511  * Create a new FieldLabel
30512  * @param {Object} config The config object
30513  */
30514
30515 Roo.bootstrap.FieldLabel = function(config){
30516     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30517     
30518     this.addEvents({
30519             /**
30520              * @event invalid
30521              * Fires after the field has been marked as invalid.
30522              * @param {Roo.form.FieldLabel} this
30523              * @param {String} msg The validation message
30524              */
30525             invalid : true,
30526             /**
30527              * @event valid
30528              * Fires after the field has been validated with no errors.
30529              * @param {Roo.form.FieldLabel} this
30530              */
30531             valid : true
30532         });
30533 };
30534
30535 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30536     
30537     tag: 'label',
30538     cls: '',
30539     html: '',
30540     target: '',
30541     allowBlank : true,
30542     invalidClass : 'has-warning',
30543     validClass : 'has-success',
30544     iconTooltip : 'This field is required',
30545     indicatorpos : 'left',
30546     
30547     getAutoCreate : function(){
30548         
30549         var cls = "";
30550         if (!this.allowBlank) {
30551             cls  = "visible";
30552         }
30553         
30554         var cfg = {
30555             tag : this.tag,
30556             cls : 'roo-bootstrap-field-label ' + this.cls,
30557             for : this.target,
30558             cn : [
30559                 {
30560                     tag : 'i',
30561                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30562                     tooltip : this.iconTooltip
30563                 },
30564                 {
30565                     tag : 'span',
30566                     html : this.html
30567                 }
30568             ] 
30569         };
30570         
30571         if(this.indicatorpos == 'right'){
30572             var cfg = {
30573                 tag : this.tag,
30574                 cls : 'roo-bootstrap-field-label ' + this.cls,
30575                 for : this.target,
30576                 cn : [
30577                     {
30578                         tag : 'span',
30579                         html : this.html
30580                     },
30581                     {
30582                         tag : 'i',
30583                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30584                         tooltip : this.iconTooltip
30585                     }
30586                 ] 
30587             };
30588         }
30589         
30590         return cfg;
30591     },
30592     
30593     initEvents: function() 
30594     {
30595         Roo.bootstrap.Element.superclass.initEvents.call(this);
30596         
30597         this.indicator = this.indicatorEl();
30598         
30599         if(this.indicator){
30600             this.indicator.removeClass('visible');
30601             this.indicator.addClass('invisible');
30602         }
30603         
30604         Roo.bootstrap.FieldLabel.register(this);
30605     },
30606     
30607     indicatorEl : function()
30608     {
30609         var indicator = this.el.select('i.roo-required-indicator',true).first();
30610         
30611         if(!indicator){
30612             return false;
30613         }
30614         
30615         return indicator;
30616         
30617     },
30618     
30619     /**
30620      * Mark this field as valid
30621      */
30622     markValid : function()
30623     {
30624         if(this.indicator){
30625             this.indicator.removeClass('visible');
30626             this.indicator.addClass('invisible');
30627         }
30628         if (Roo.bootstrap.version == 3) {
30629             this.el.removeClass(this.invalidClass);
30630             this.el.addClass(this.validClass);
30631         } else {
30632             this.el.removeClass('is-invalid');
30633             this.el.addClass('is-valid');
30634         }
30635         
30636         
30637         this.fireEvent('valid', this);
30638     },
30639     
30640     /**
30641      * Mark this field as invalid
30642      * @param {String} msg The validation message
30643      */
30644     markInvalid : function(msg)
30645     {
30646         if(this.indicator){
30647             this.indicator.removeClass('invisible');
30648             this.indicator.addClass('visible');
30649         }
30650           if (Roo.bootstrap.version == 3) {
30651             this.el.removeClass(this.validClass);
30652             this.el.addClass(this.invalidClass);
30653         } else {
30654             this.el.removeClass('is-valid');
30655             this.el.addClass('is-invalid');
30656         }
30657         
30658         
30659         this.fireEvent('invalid', this, msg);
30660     }
30661     
30662    
30663 });
30664
30665 Roo.apply(Roo.bootstrap.FieldLabel, {
30666     
30667     groups: {},
30668     
30669      /**
30670     * register a FieldLabel Group
30671     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30672     */
30673     register : function(label)
30674     {
30675         if(this.groups.hasOwnProperty(label.target)){
30676             return;
30677         }
30678      
30679         this.groups[label.target] = label;
30680         
30681     },
30682     /**
30683     * fetch a FieldLabel Group based on the target
30684     * @param {string} target
30685     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30686     */
30687     get: function(target) {
30688         if (typeof(this.groups[target]) == 'undefined') {
30689             return false;
30690         }
30691         
30692         return this.groups[target] ;
30693     }
30694 });
30695
30696  
30697
30698  /*
30699  * - LGPL
30700  *
30701  * page DateSplitField.
30702  * 
30703  */
30704
30705
30706 /**
30707  * @class Roo.bootstrap.DateSplitField
30708  * @extends Roo.bootstrap.Component
30709  * Bootstrap DateSplitField class
30710  * @cfg {string} fieldLabel - the label associated
30711  * @cfg {Number} labelWidth set the width of label (0-12)
30712  * @cfg {String} labelAlign (top|left)
30713  * @cfg {Boolean} dayAllowBlank (true|false) default false
30714  * @cfg {Boolean} monthAllowBlank (true|false) default false
30715  * @cfg {Boolean} yearAllowBlank (true|false) default false
30716  * @cfg {string} dayPlaceholder 
30717  * @cfg {string} monthPlaceholder
30718  * @cfg {string} yearPlaceholder
30719  * @cfg {string} dayFormat default 'd'
30720  * @cfg {string} monthFormat default 'm'
30721  * @cfg {string} yearFormat default 'Y'
30722  * @cfg {Number} labellg set the width of label (1-12)
30723  * @cfg {Number} labelmd set the width of label (1-12)
30724  * @cfg {Number} labelsm set the width of label (1-12)
30725  * @cfg {Number} labelxs set the width of label (1-12)
30726
30727  *     
30728  * @constructor
30729  * Create a new DateSplitField
30730  * @param {Object} config The config object
30731  */
30732
30733 Roo.bootstrap.DateSplitField = function(config){
30734     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30735     
30736     this.addEvents({
30737         // raw events
30738          /**
30739          * @event years
30740          * getting the data of years
30741          * @param {Roo.bootstrap.DateSplitField} this
30742          * @param {Object} years
30743          */
30744         "years" : true,
30745         /**
30746          * @event days
30747          * getting the data of days
30748          * @param {Roo.bootstrap.DateSplitField} this
30749          * @param {Object} days
30750          */
30751         "days" : true,
30752         /**
30753          * @event invalid
30754          * Fires after the field has been marked as invalid.
30755          * @param {Roo.form.Field} this
30756          * @param {String} msg The validation message
30757          */
30758         invalid : true,
30759        /**
30760          * @event valid
30761          * Fires after the field has been validated with no errors.
30762          * @param {Roo.form.Field} this
30763          */
30764         valid : true
30765     });
30766 };
30767
30768 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30769     
30770     fieldLabel : '',
30771     labelAlign : 'top',
30772     labelWidth : 3,
30773     dayAllowBlank : false,
30774     monthAllowBlank : false,
30775     yearAllowBlank : false,
30776     dayPlaceholder : '',
30777     monthPlaceholder : '',
30778     yearPlaceholder : '',
30779     dayFormat : 'd',
30780     monthFormat : 'm',
30781     yearFormat : 'Y',
30782     isFormField : true,
30783     labellg : 0,
30784     labelmd : 0,
30785     labelsm : 0,
30786     labelxs : 0,
30787     
30788     getAutoCreate : function()
30789     {
30790         var cfg = {
30791             tag : 'div',
30792             cls : 'row roo-date-split-field-group',
30793             cn : [
30794                 {
30795                     tag : 'input',
30796                     type : 'hidden',
30797                     cls : 'form-hidden-field roo-date-split-field-group-value',
30798                     name : this.name
30799                 }
30800             ]
30801         };
30802         
30803         var labelCls = 'col-md-12';
30804         var contentCls = 'col-md-4';
30805         
30806         if(this.fieldLabel){
30807             
30808             var label = {
30809                 tag : 'div',
30810                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30811                 cn : [
30812                     {
30813                         tag : 'label',
30814                         html : this.fieldLabel
30815                     }
30816                 ]
30817             };
30818             
30819             if(this.labelAlign == 'left'){
30820             
30821                 if(this.labelWidth > 12){
30822                     label.style = "width: " + this.labelWidth + 'px';
30823                 }
30824
30825                 if(this.labelWidth < 13 && this.labelmd == 0){
30826                     this.labelmd = this.labelWidth;
30827                 }
30828
30829                 if(this.labellg > 0){
30830                     labelCls = ' col-lg-' + this.labellg;
30831                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30832                 }
30833
30834                 if(this.labelmd > 0){
30835                     labelCls = ' col-md-' + this.labelmd;
30836                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30837                 }
30838
30839                 if(this.labelsm > 0){
30840                     labelCls = ' col-sm-' + this.labelsm;
30841                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30842                 }
30843
30844                 if(this.labelxs > 0){
30845                     labelCls = ' col-xs-' + this.labelxs;
30846                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30847                 }
30848             }
30849             
30850             label.cls += ' ' + labelCls;
30851             
30852             cfg.cn.push(label);
30853         }
30854         
30855         Roo.each(['day', 'month', 'year'], function(t){
30856             cfg.cn.push({
30857                 tag : 'div',
30858                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30859             });
30860         }, this);
30861         
30862         return cfg;
30863     },
30864     
30865     inputEl: function ()
30866     {
30867         return this.el.select('.roo-date-split-field-group-value', true).first();
30868     },
30869     
30870     onRender : function(ct, position) 
30871     {
30872         var _this = this;
30873         
30874         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30875         
30876         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30877         
30878         this.dayField = new Roo.bootstrap.ComboBox({
30879             allowBlank : this.dayAllowBlank,
30880             alwaysQuery : true,
30881             displayField : 'value',
30882             editable : false,
30883             fieldLabel : '',
30884             forceSelection : true,
30885             mode : 'local',
30886             placeholder : this.dayPlaceholder,
30887             selectOnFocus : true,
30888             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30889             triggerAction : 'all',
30890             typeAhead : true,
30891             valueField : 'value',
30892             store : new Roo.data.SimpleStore({
30893                 data : (function() {    
30894                     var days = [];
30895                     _this.fireEvent('days', _this, days);
30896                     return days;
30897                 })(),
30898                 fields : [ 'value' ]
30899             }),
30900             listeners : {
30901                 select : function (_self, record, index)
30902                 {
30903                     _this.setValue(_this.getValue());
30904                 }
30905             }
30906         });
30907
30908         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30909         
30910         this.monthField = new Roo.bootstrap.MonthField({
30911             after : '<i class=\"fa fa-calendar\"></i>',
30912             allowBlank : this.monthAllowBlank,
30913             placeholder : this.monthPlaceholder,
30914             readOnly : true,
30915             listeners : {
30916                 render : function (_self)
30917                 {
30918                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30919                         e.preventDefault();
30920                         _self.focus();
30921                     });
30922                 },
30923                 select : function (_self, oldvalue, newvalue)
30924                 {
30925                     _this.setValue(_this.getValue());
30926                 }
30927             }
30928         });
30929         
30930         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30931         
30932         this.yearField = new Roo.bootstrap.ComboBox({
30933             allowBlank : this.yearAllowBlank,
30934             alwaysQuery : true,
30935             displayField : 'value',
30936             editable : false,
30937             fieldLabel : '',
30938             forceSelection : true,
30939             mode : 'local',
30940             placeholder : this.yearPlaceholder,
30941             selectOnFocus : true,
30942             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30943             triggerAction : 'all',
30944             typeAhead : true,
30945             valueField : 'value',
30946             store : new Roo.data.SimpleStore({
30947                 data : (function() {
30948                     var years = [];
30949                     _this.fireEvent('years', _this, years);
30950                     return years;
30951                 })(),
30952                 fields : [ 'value' ]
30953             }),
30954             listeners : {
30955                 select : function (_self, record, index)
30956                 {
30957                     _this.setValue(_this.getValue());
30958                 }
30959             }
30960         });
30961
30962         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30963     },
30964     
30965     setValue : function(v, format)
30966     {
30967         this.inputEl.dom.value = v;
30968         
30969         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30970         
30971         var d = Date.parseDate(v, f);
30972         
30973         if(!d){
30974             this.validate();
30975             return;
30976         }
30977         
30978         this.setDay(d.format(this.dayFormat));
30979         this.setMonth(d.format(this.monthFormat));
30980         this.setYear(d.format(this.yearFormat));
30981         
30982         this.validate();
30983         
30984         return;
30985     },
30986     
30987     setDay : function(v)
30988     {
30989         this.dayField.setValue(v);
30990         this.inputEl.dom.value = this.getValue();
30991         this.validate();
30992         return;
30993     },
30994     
30995     setMonth : function(v)
30996     {
30997         this.monthField.setValue(v, true);
30998         this.inputEl.dom.value = this.getValue();
30999         this.validate();
31000         return;
31001     },
31002     
31003     setYear : function(v)
31004     {
31005         this.yearField.setValue(v);
31006         this.inputEl.dom.value = this.getValue();
31007         this.validate();
31008         return;
31009     },
31010     
31011     getDay : function()
31012     {
31013         return this.dayField.getValue();
31014     },
31015     
31016     getMonth : function()
31017     {
31018         return this.monthField.getValue();
31019     },
31020     
31021     getYear : function()
31022     {
31023         return this.yearField.getValue();
31024     },
31025     
31026     getValue : function()
31027     {
31028         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31029         
31030         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31031         
31032         return date;
31033     },
31034     
31035     reset : function()
31036     {
31037         this.setDay('');
31038         this.setMonth('');
31039         this.setYear('');
31040         this.inputEl.dom.value = '';
31041         this.validate();
31042         return;
31043     },
31044     
31045     validate : function()
31046     {
31047         var d = this.dayField.validate();
31048         var m = this.monthField.validate();
31049         var y = this.yearField.validate();
31050         
31051         var valid = true;
31052         
31053         if(
31054                 (!this.dayAllowBlank && !d) ||
31055                 (!this.monthAllowBlank && !m) ||
31056                 (!this.yearAllowBlank && !y)
31057         ){
31058             valid = false;
31059         }
31060         
31061         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31062             return valid;
31063         }
31064         
31065         if(valid){
31066             this.markValid();
31067             return valid;
31068         }
31069         
31070         this.markInvalid();
31071         
31072         return valid;
31073     },
31074     
31075     markValid : function()
31076     {
31077         
31078         var label = this.el.select('label', true).first();
31079         var icon = this.el.select('i.fa-star', true).first();
31080
31081         if(label && icon){
31082             icon.remove();
31083         }
31084         
31085         this.fireEvent('valid', this);
31086     },
31087     
31088      /**
31089      * Mark this field as invalid
31090      * @param {String} msg The validation message
31091      */
31092     markInvalid : function(msg)
31093     {
31094         
31095         var label = this.el.select('label', true).first();
31096         var icon = this.el.select('i.fa-star', true).first();
31097
31098         if(label && !icon){
31099             this.el.select('.roo-date-split-field-label', true).createChild({
31100                 tag : 'i',
31101                 cls : 'text-danger fa fa-lg fa-star',
31102                 tooltip : 'This field is required',
31103                 style : 'margin-right:5px;'
31104             }, label, true);
31105         }
31106         
31107         this.fireEvent('invalid', this, msg);
31108     },
31109     
31110     clearInvalid : function()
31111     {
31112         var label = this.el.select('label', true).first();
31113         var icon = this.el.select('i.fa-star', true).first();
31114
31115         if(label && icon){
31116             icon.remove();
31117         }
31118         
31119         this.fireEvent('valid', this);
31120     },
31121     
31122     getName: function()
31123     {
31124         return this.name;
31125     }
31126     
31127 });
31128
31129  /**
31130  *
31131  * This is based on 
31132  * http://masonry.desandro.com
31133  *
31134  * The idea is to render all the bricks based on vertical width...
31135  *
31136  * The original code extends 'outlayer' - we might need to use that....
31137  * 
31138  */
31139
31140
31141 /**
31142  * @class Roo.bootstrap.LayoutMasonry
31143  * @extends Roo.bootstrap.Component
31144  * Bootstrap Layout Masonry class
31145  * 
31146  * @constructor
31147  * Create a new Element
31148  * @param {Object} config The config object
31149  */
31150
31151 Roo.bootstrap.LayoutMasonry = function(config){
31152     
31153     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31154     
31155     this.bricks = [];
31156     
31157     Roo.bootstrap.LayoutMasonry.register(this);
31158     
31159     this.addEvents({
31160         // raw events
31161         /**
31162          * @event layout
31163          * Fire after layout the items
31164          * @param {Roo.bootstrap.LayoutMasonry} this
31165          * @param {Roo.EventObject} e
31166          */
31167         "layout" : true
31168     });
31169     
31170 };
31171
31172 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31173     
31174     /**
31175      * @cfg {Boolean} isLayoutInstant = no animation?
31176      */   
31177     isLayoutInstant : false, // needed?
31178    
31179     /**
31180      * @cfg {Number} boxWidth  width of the columns
31181      */   
31182     boxWidth : 450,
31183     
31184       /**
31185      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31186      */   
31187     boxHeight : 0,
31188     
31189     /**
31190      * @cfg {Number} padWidth padding below box..
31191      */   
31192     padWidth : 10, 
31193     
31194     /**
31195      * @cfg {Number} gutter gutter width..
31196      */   
31197     gutter : 10,
31198     
31199      /**
31200      * @cfg {Number} maxCols maximum number of columns
31201      */   
31202     
31203     maxCols: 0,
31204     
31205     /**
31206      * @cfg {Boolean} isAutoInitial defalut true
31207      */   
31208     isAutoInitial : true, 
31209     
31210     containerWidth: 0,
31211     
31212     /**
31213      * @cfg {Boolean} isHorizontal defalut false
31214      */   
31215     isHorizontal : false, 
31216
31217     currentSize : null,
31218     
31219     tag: 'div',
31220     
31221     cls: '',
31222     
31223     bricks: null, //CompositeElement
31224     
31225     cols : 1,
31226     
31227     _isLayoutInited : false,
31228     
31229 //    isAlternative : false, // only use for vertical layout...
31230     
31231     /**
31232      * @cfg {Number} alternativePadWidth padding below box..
31233      */   
31234     alternativePadWidth : 50,
31235     
31236     selectedBrick : [],
31237     
31238     getAutoCreate : function(){
31239         
31240         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31241         
31242         var cfg = {
31243             tag: this.tag,
31244             cls: 'blog-masonary-wrapper ' + this.cls,
31245             cn : {
31246                 cls : 'mas-boxes masonary'
31247             }
31248         };
31249         
31250         return cfg;
31251     },
31252     
31253     getChildContainer: function( )
31254     {
31255         if (this.boxesEl) {
31256             return this.boxesEl;
31257         }
31258         
31259         this.boxesEl = this.el.select('.mas-boxes').first();
31260         
31261         return this.boxesEl;
31262     },
31263     
31264     
31265     initEvents : function()
31266     {
31267         var _this = this;
31268         
31269         if(this.isAutoInitial){
31270             Roo.log('hook children rendered');
31271             this.on('childrenrendered', function() {
31272                 Roo.log('children rendered');
31273                 _this.initial();
31274             } ,this);
31275         }
31276     },
31277     
31278     initial : function()
31279     {
31280         this.selectedBrick = [];
31281         
31282         this.currentSize = this.el.getBox(true);
31283         
31284         Roo.EventManager.onWindowResize(this.resize, this); 
31285
31286         if(!this.isAutoInitial){
31287             this.layout();
31288             return;
31289         }
31290         
31291         this.layout();
31292         
31293         return;
31294         //this.layout.defer(500,this);
31295         
31296     },
31297     
31298     resize : function()
31299     {
31300         var cs = this.el.getBox(true);
31301         
31302         if (
31303                 this.currentSize.width == cs.width && 
31304                 this.currentSize.x == cs.x && 
31305                 this.currentSize.height == cs.height && 
31306                 this.currentSize.y == cs.y 
31307         ) {
31308             Roo.log("no change in with or X or Y");
31309             return;
31310         }
31311         
31312         this.currentSize = cs;
31313         
31314         this.layout();
31315         
31316     },
31317     
31318     layout : function()
31319     {   
31320         this._resetLayout();
31321         
31322         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31323         
31324         this.layoutItems( isInstant );
31325       
31326         this._isLayoutInited = true;
31327         
31328         this.fireEvent('layout', this);
31329         
31330     },
31331     
31332     _resetLayout : function()
31333     {
31334         if(this.isHorizontal){
31335             this.horizontalMeasureColumns();
31336             return;
31337         }
31338         
31339         this.verticalMeasureColumns();
31340         
31341     },
31342     
31343     verticalMeasureColumns : function()
31344     {
31345         this.getContainerWidth();
31346         
31347 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31348 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31349 //            return;
31350 //        }
31351         
31352         var boxWidth = this.boxWidth + this.padWidth;
31353         
31354         if(this.containerWidth < this.boxWidth){
31355             boxWidth = this.containerWidth
31356         }
31357         
31358         var containerWidth = this.containerWidth;
31359         
31360         var cols = Math.floor(containerWidth / boxWidth);
31361         
31362         this.cols = Math.max( cols, 1 );
31363         
31364         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31365         
31366         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31367         
31368         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31369         
31370         this.colWidth = boxWidth + avail - this.padWidth;
31371         
31372         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31373         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31374     },
31375     
31376     horizontalMeasureColumns : function()
31377     {
31378         this.getContainerWidth();
31379         
31380         var boxWidth = this.boxWidth;
31381         
31382         if(this.containerWidth < boxWidth){
31383             boxWidth = this.containerWidth;
31384         }
31385         
31386         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31387         
31388         this.el.setHeight(boxWidth);
31389         
31390     },
31391     
31392     getContainerWidth : function()
31393     {
31394         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31395     },
31396     
31397     layoutItems : function( isInstant )
31398     {
31399         Roo.log(this.bricks);
31400         
31401         var items = Roo.apply([], this.bricks);
31402         
31403         if(this.isHorizontal){
31404             this._horizontalLayoutItems( items , isInstant );
31405             return;
31406         }
31407         
31408 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31409 //            this._verticalAlternativeLayoutItems( items , isInstant );
31410 //            return;
31411 //        }
31412         
31413         this._verticalLayoutItems( items , isInstant );
31414         
31415     },
31416     
31417     _verticalLayoutItems : function ( items , isInstant)
31418     {
31419         if ( !items || !items.length ) {
31420             return;
31421         }
31422         
31423         var standard = [
31424             ['xs', 'xs', 'xs', 'tall'],
31425             ['xs', 'xs', 'tall'],
31426             ['xs', 'xs', 'sm'],
31427             ['xs', 'xs', 'xs'],
31428             ['xs', 'tall'],
31429             ['xs', 'sm'],
31430             ['xs', 'xs'],
31431             ['xs'],
31432             
31433             ['sm', 'xs', 'xs'],
31434             ['sm', 'xs'],
31435             ['sm'],
31436             
31437             ['tall', 'xs', 'xs', 'xs'],
31438             ['tall', 'xs', 'xs'],
31439             ['tall', 'xs'],
31440             ['tall']
31441             
31442         ];
31443         
31444         var queue = [];
31445         
31446         var boxes = [];
31447         
31448         var box = [];
31449         
31450         Roo.each(items, function(item, k){
31451             
31452             switch (item.size) {
31453                 // these layouts take up a full box,
31454                 case 'md' :
31455                 case 'md-left' :
31456                 case 'md-right' :
31457                 case 'wide' :
31458                     
31459                     if(box.length){
31460                         boxes.push(box);
31461                         box = [];
31462                     }
31463                     
31464                     boxes.push([item]);
31465                     
31466                     break;
31467                     
31468                 case 'xs' :
31469                 case 'sm' :
31470                 case 'tall' :
31471                     
31472                     box.push(item);
31473                     
31474                     break;
31475                 default :
31476                     break;
31477                     
31478             }
31479             
31480         }, this);
31481         
31482         if(box.length){
31483             boxes.push(box);
31484             box = [];
31485         }
31486         
31487         var filterPattern = function(box, length)
31488         {
31489             if(!box.length){
31490                 return;
31491             }
31492             
31493             var match = false;
31494             
31495             var pattern = box.slice(0, length);
31496             
31497             var format = [];
31498             
31499             Roo.each(pattern, function(i){
31500                 format.push(i.size);
31501             }, this);
31502             
31503             Roo.each(standard, function(s){
31504                 
31505                 if(String(s) != String(format)){
31506                     return;
31507                 }
31508                 
31509                 match = true;
31510                 return false;
31511                 
31512             }, this);
31513             
31514             if(!match && length == 1){
31515                 return;
31516             }
31517             
31518             if(!match){
31519                 filterPattern(box, length - 1);
31520                 return;
31521             }
31522                 
31523             queue.push(pattern);
31524
31525             box = box.slice(length, box.length);
31526
31527             filterPattern(box, 4);
31528
31529             return;
31530             
31531         }
31532         
31533         Roo.each(boxes, function(box, k){
31534             
31535             if(!box.length){
31536                 return;
31537             }
31538             
31539             if(box.length == 1){
31540                 queue.push(box);
31541                 return;
31542             }
31543             
31544             filterPattern(box, 4);
31545             
31546         }, this);
31547         
31548         this._processVerticalLayoutQueue( queue, isInstant );
31549         
31550     },
31551     
31552 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31553 //    {
31554 //        if ( !items || !items.length ) {
31555 //            return;
31556 //        }
31557 //
31558 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31559 //        
31560 //    },
31561     
31562     _horizontalLayoutItems : function ( items , isInstant)
31563     {
31564         if ( !items || !items.length || items.length < 3) {
31565             return;
31566         }
31567         
31568         items.reverse();
31569         
31570         var eItems = items.slice(0, 3);
31571         
31572         items = items.slice(3, items.length);
31573         
31574         var standard = [
31575             ['xs', 'xs', 'xs', 'wide'],
31576             ['xs', 'xs', 'wide'],
31577             ['xs', 'xs', 'sm'],
31578             ['xs', 'xs', 'xs'],
31579             ['xs', 'wide'],
31580             ['xs', 'sm'],
31581             ['xs', 'xs'],
31582             ['xs'],
31583             
31584             ['sm', 'xs', 'xs'],
31585             ['sm', 'xs'],
31586             ['sm'],
31587             
31588             ['wide', 'xs', 'xs', 'xs'],
31589             ['wide', 'xs', 'xs'],
31590             ['wide', 'xs'],
31591             ['wide'],
31592             
31593             ['wide-thin']
31594         ];
31595         
31596         var queue = [];
31597         
31598         var boxes = [];
31599         
31600         var box = [];
31601         
31602         Roo.each(items, function(item, k){
31603             
31604             switch (item.size) {
31605                 case 'md' :
31606                 case 'md-left' :
31607                 case 'md-right' :
31608                 case 'tall' :
31609                     
31610                     if(box.length){
31611                         boxes.push(box);
31612                         box = [];
31613                     }
31614                     
31615                     boxes.push([item]);
31616                     
31617                     break;
31618                     
31619                 case 'xs' :
31620                 case 'sm' :
31621                 case 'wide' :
31622                 case 'wide-thin' :
31623                     
31624                     box.push(item);
31625                     
31626                     break;
31627                 default :
31628                     break;
31629                     
31630             }
31631             
31632         }, this);
31633         
31634         if(box.length){
31635             boxes.push(box);
31636             box = [];
31637         }
31638         
31639         var filterPattern = function(box, length)
31640         {
31641             if(!box.length){
31642                 return;
31643             }
31644             
31645             var match = false;
31646             
31647             var pattern = box.slice(0, length);
31648             
31649             var format = [];
31650             
31651             Roo.each(pattern, function(i){
31652                 format.push(i.size);
31653             }, this);
31654             
31655             Roo.each(standard, function(s){
31656                 
31657                 if(String(s) != String(format)){
31658                     return;
31659                 }
31660                 
31661                 match = true;
31662                 return false;
31663                 
31664             }, this);
31665             
31666             if(!match && length == 1){
31667                 return;
31668             }
31669             
31670             if(!match){
31671                 filterPattern(box, length - 1);
31672                 return;
31673             }
31674                 
31675             queue.push(pattern);
31676
31677             box = box.slice(length, box.length);
31678
31679             filterPattern(box, 4);
31680
31681             return;
31682             
31683         }
31684         
31685         Roo.each(boxes, function(box, k){
31686             
31687             if(!box.length){
31688                 return;
31689             }
31690             
31691             if(box.length == 1){
31692                 queue.push(box);
31693                 return;
31694             }
31695             
31696             filterPattern(box, 4);
31697             
31698         }, this);
31699         
31700         
31701         var prune = [];
31702         
31703         var pos = this.el.getBox(true);
31704         
31705         var minX = pos.x;
31706         
31707         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31708         
31709         var hit_end = false;
31710         
31711         Roo.each(queue, function(box){
31712             
31713             if(hit_end){
31714                 
31715                 Roo.each(box, function(b){
31716                 
31717                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31718                     b.el.hide();
31719
31720                 }, this);
31721
31722                 return;
31723             }
31724             
31725             var mx = 0;
31726             
31727             Roo.each(box, function(b){
31728                 
31729                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31730                 b.el.show();
31731
31732                 mx = Math.max(mx, b.x);
31733                 
31734             }, this);
31735             
31736             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31737             
31738             if(maxX < minX){
31739                 
31740                 Roo.each(box, function(b){
31741                 
31742                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31743                     b.el.hide();
31744                     
31745                 }, this);
31746                 
31747                 hit_end = true;
31748                 
31749                 return;
31750             }
31751             
31752             prune.push(box);
31753             
31754         }, this);
31755         
31756         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31757     },
31758     
31759     /** Sets position of item in DOM
31760     * @param {Element} item
31761     * @param {Number} x - horizontal position
31762     * @param {Number} y - vertical position
31763     * @param {Boolean} isInstant - disables transitions
31764     */
31765     _processVerticalLayoutQueue : function( queue, isInstant )
31766     {
31767         var pos = this.el.getBox(true);
31768         var x = pos.x;
31769         var y = pos.y;
31770         var maxY = [];
31771         
31772         for (var i = 0; i < this.cols; i++){
31773             maxY[i] = pos.y;
31774         }
31775         
31776         Roo.each(queue, function(box, k){
31777             
31778             var col = k % this.cols;
31779             
31780             Roo.each(box, function(b,kk){
31781                 
31782                 b.el.position('absolute');
31783                 
31784                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31785                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31786                 
31787                 if(b.size == 'md-left' || b.size == 'md-right'){
31788                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31789                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31790                 }
31791                 
31792                 b.el.setWidth(width);
31793                 b.el.setHeight(height);
31794                 // iframe?
31795                 b.el.select('iframe',true).setSize(width,height);
31796                 
31797             }, this);
31798             
31799             for (var i = 0; i < this.cols; i++){
31800                 
31801                 if(maxY[i] < maxY[col]){
31802                     col = i;
31803                     continue;
31804                 }
31805                 
31806                 col = Math.min(col, i);
31807                 
31808             }
31809             
31810             x = pos.x + col * (this.colWidth + this.padWidth);
31811             
31812             y = maxY[col];
31813             
31814             var positions = [];
31815             
31816             switch (box.length){
31817                 case 1 :
31818                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31819                     break;
31820                 case 2 :
31821                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31822                     break;
31823                 case 3 :
31824                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31825                     break;
31826                 case 4 :
31827                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31828                     break;
31829                 default :
31830                     break;
31831             }
31832             
31833             Roo.each(box, function(b,kk){
31834                 
31835                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31836                 
31837                 var sz = b.el.getSize();
31838                 
31839                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31840                 
31841             }, this);
31842             
31843         }, this);
31844         
31845         var mY = 0;
31846         
31847         for (var i = 0; i < this.cols; i++){
31848             mY = Math.max(mY, maxY[i]);
31849         }
31850         
31851         this.el.setHeight(mY - pos.y);
31852         
31853     },
31854     
31855 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31856 //    {
31857 //        var pos = this.el.getBox(true);
31858 //        var x = pos.x;
31859 //        var y = pos.y;
31860 //        var maxX = pos.right;
31861 //        
31862 //        var maxHeight = 0;
31863 //        
31864 //        Roo.each(items, function(item, k){
31865 //            
31866 //            var c = k % 2;
31867 //            
31868 //            item.el.position('absolute');
31869 //                
31870 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31871 //
31872 //            item.el.setWidth(width);
31873 //
31874 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31875 //
31876 //            item.el.setHeight(height);
31877 //            
31878 //            if(c == 0){
31879 //                item.el.setXY([x, y], isInstant ? false : true);
31880 //            } else {
31881 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31882 //            }
31883 //            
31884 //            y = y + height + this.alternativePadWidth;
31885 //            
31886 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31887 //            
31888 //        }, this);
31889 //        
31890 //        this.el.setHeight(maxHeight);
31891 //        
31892 //    },
31893     
31894     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31895     {
31896         var pos = this.el.getBox(true);
31897         
31898         var minX = pos.x;
31899         var minY = pos.y;
31900         
31901         var maxX = pos.right;
31902         
31903         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31904         
31905         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31906         
31907         Roo.each(queue, function(box, k){
31908             
31909             Roo.each(box, function(b, kk){
31910                 
31911                 b.el.position('absolute');
31912                 
31913                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31914                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31915                 
31916                 if(b.size == 'md-left' || b.size == 'md-right'){
31917                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31918                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31919                 }
31920                 
31921                 b.el.setWidth(width);
31922                 b.el.setHeight(height);
31923                 
31924             }, this);
31925             
31926             if(!box.length){
31927                 return;
31928             }
31929             
31930             var positions = [];
31931             
31932             switch (box.length){
31933                 case 1 :
31934                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31935                     break;
31936                 case 2 :
31937                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31938                     break;
31939                 case 3 :
31940                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31941                     break;
31942                 case 4 :
31943                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31944                     break;
31945                 default :
31946                     break;
31947             }
31948             
31949             Roo.each(box, function(b,kk){
31950                 
31951                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31952                 
31953                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31954                 
31955             }, this);
31956             
31957         }, this);
31958         
31959     },
31960     
31961     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31962     {
31963         Roo.each(eItems, function(b,k){
31964             
31965             b.size = (k == 0) ? 'sm' : 'xs';
31966             b.x = (k == 0) ? 2 : 1;
31967             b.y = (k == 0) ? 2 : 1;
31968             
31969             b.el.position('absolute');
31970             
31971             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31972                 
31973             b.el.setWidth(width);
31974             
31975             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31976             
31977             b.el.setHeight(height);
31978             
31979         }, this);
31980
31981         var positions = [];
31982         
31983         positions.push({
31984             x : maxX - this.unitWidth * 2 - this.gutter,
31985             y : minY
31986         });
31987         
31988         positions.push({
31989             x : maxX - this.unitWidth,
31990             y : minY + (this.unitWidth + this.gutter) * 2
31991         });
31992         
31993         positions.push({
31994             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31995             y : minY
31996         });
31997         
31998         Roo.each(eItems, function(b,k){
31999             
32000             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32001
32002         }, this);
32003         
32004     },
32005     
32006     getVerticalOneBoxColPositions : function(x, y, box)
32007     {
32008         var pos = [];
32009         
32010         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32011         
32012         if(box[0].size == 'md-left'){
32013             rand = 0;
32014         }
32015         
32016         if(box[0].size == 'md-right'){
32017             rand = 1;
32018         }
32019         
32020         pos.push({
32021             x : x + (this.unitWidth + this.gutter) * rand,
32022             y : y
32023         });
32024         
32025         return pos;
32026     },
32027     
32028     getVerticalTwoBoxColPositions : function(x, y, box)
32029     {
32030         var pos = [];
32031         
32032         if(box[0].size == 'xs'){
32033             
32034             pos.push({
32035                 x : x,
32036                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32037             });
32038
32039             pos.push({
32040                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32041                 y : y
32042             });
32043             
32044             return pos;
32045             
32046         }
32047         
32048         pos.push({
32049             x : x,
32050             y : y
32051         });
32052
32053         pos.push({
32054             x : x + (this.unitWidth + this.gutter) * 2,
32055             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32056         });
32057         
32058         return pos;
32059         
32060     },
32061     
32062     getVerticalThreeBoxColPositions : function(x, y, box)
32063     {
32064         var pos = [];
32065         
32066         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32067             
32068             pos.push({
32069                 x : x,
32070                 y : y
32071             });
32072
32073             pos.push({
32074                 x : x + (this.unitWidth + this.gutter) * 1,
32075                 y : y
32076             });
32077             
32078             pos.push({
32079                 x : x + (this.unitWidth + this.gutter) * 2,
32080                 y : y
32081             });
32082             
32083             return pos;
32084             
32085         }
32086         
32087         if(box[0].size == 'xs' && box[1].size == 'xs'){
32088             
32089             pos.push({
32090                 x : x,
32091                 y : y
32092             });
32093
32094             pos.push({
32095                 x : x,
32096                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32097             });
32098             
32099             pos.push({
32100                 x : x + (this.unitWidth + this.gutter) * 1,
32101                 y : y
32102             });
32103             
32104             return pos;
32105             
32106         }
32107         
32108         pos.push({
32109             x : x,
32110             y : y
32111         });
32112
32113         pos.push({
32114             x : x + (this.unitWidth + this.gutter) * 2,
32115             y : y
32116         });
32117
32118         pos.push({
32119             x : x + (this.unitWidth + this.gutter) * 2,
32120             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32121         });
32122             
32123         return pos;
32124         
32125     },
32126     
32127     getVerticalFourBoxColPositions : function(x, y, box)
32128     {
32129         var pos = [];
32130         
32131         if(box[0].size == 'xs'){
32132             
32133             pos.push({
32134                 x : x,
32135                 y : y
32136             });
32137
32138             pos.push({
32139                 x : x,
32140                 y : y + (this.unitHeight + this.gutter) * 1
32141             });
32142             
32143             pos.push({
32144                 x : x,
32145                 y : y + (this.unitHeight + this.gutter) * 2
32146             });
32147             
32148             pos.push({
32149                 x : x + (this.unitWidth + this.gutter) * 1,
32150                 y : y
32151             });
32152             
32153             return pos;
32154             
32155         }
32156         
32157         pos.push({
32158             x : x,
32159             y : y
32160         });
32161
32162         pos.push({
32163             x : x + (this.unitWidth + this.gutter) * 2,
32164             y : y
32165         });
32166
32167         pos.push({
32168             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32169             y : y + (this.unitHeight + this.gutter) * 1
32170         });
32171
32172         pos.push({
32173             x : x + (this.unitWidth + this.gutter) * 2,
32174             y : y + (this.unitWidth + this.gutter) * 2
32175         });
32176
32177         return pos;
32178         
32179     },
32180     
32181     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32182     {
32183         var pos = [];
32184         
32185         if(box[0].size == 'md-left'){
32186             pos.push({
32187                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32188                 y : minY
32189             });
32190             
32191             return pos;
32192         }
32193         
32194         if(box[0].size == 'md-right'){
32195             pos.push({
32196                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32197                 y : minY + (this.unitWidth + this.gutter) * 1
32198             });
32199             
32200             return pos;
32201         }
32202         
32203         var rand = Math.floor(Math.random() * (4 - box[0].y));
32204         
32205         pos.push({
32206             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32207             y : minY + (this.unitWidth + this.gutter) * rand
32208         });
32209         
32210         return pos;
32211         
32212     },
32213     
32214     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32215     {
32216         var pos = [];
32217         
32218         if(box[0].size == 'xs'){
32219             
32220             pos.push({
32221                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32222                 y : minY
32223             });
32224
32225             pos.push({
32226                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32227                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32228             });
32229             
32230             return pos;
32231             
32232         }
32233         
32234         pos.push({
32235             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32236             y : minY
32237         });
32238
32239         pos.push({
32240             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32241             y : minY + (this.unitWidth + this.gutter) * 2
32242         });
32243         
32244         return pos;
32245         
32246     },
32247     
32248     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32249     {
32250         var pos = [];
32251         
32252         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32253             
32254             pos.push({
32255                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32256                 y : minY
32257             });
32258
32259             pos.push({
32260                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32261                 y : minY + (this.unitWidth + this.gutter) * 1
32262             });
32263             
32264             pos.push({
32265                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32266                 y : minY + (this.unitWidth + this.gutter) * 2
32267             });
32268             
32269             return pos;
32270             
32271         }
32272         
32273         if(box[0].size == 'xs' && box[1].size == 'xs'){
32274             
32275             pos.push({
32276                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32277                 y : minY
32278             });
32279
32280             pos.push({
32281                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32282                 y : minY
32283             });
32284             
32285             pos.push({
32286                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32287                 y : minY + (this.unitWidth + this.gutter) * 1
32288             });
32289             
32290             return pos;
32291             
32292         }
32293         
32294         pos.push({
32295             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32296             y : minY
32297         });
32298
32299         pos.push({
32300             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32301             y : minY + (this.unitWidth + this.gutter) * 2
32302         });
32303
32304         pos.push({
32305             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32306             y : minY + (this.unitWidth + this.gutter) * 2
32307         });
32308             
32309         return pos;
32310         
32311     },
32312     
32313     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32314     {
32315         var pos = [];
32316         
32317         if(box[0].size == 'xs'){
32318             
32319             pos.push({
32320                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32321                 y : minY
32322             });
32323
32324             pos.push({
32325                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32326                 y : minY
32327             });
32328             
32329             pos.push({
32330                 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),
32331                 y : minY
32332             });
32333             
32334             pos.push({
32335                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32336                 y : minY + (this.unitWidth + this.gutter) * 1
32337             });
32338             
32339             return pos;
32340             
32341         }
32342         
32343         pos.push({
32344             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32345             y : minY
32346         });
32347         
32348         pos.push({
32349             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32350             y : minY + (this.unitWidth + this.gutter) * 2
32351         });
32352         
32353         pos.push({
32354             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32355             y : minY + (this.unitWidth + this.gutter) * 2
32356         });
32357         
32358         pos.push({
32359             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),
32360             y : minY + (this.unitWidth + this.gutter) * 2
32361         });
32362
32363         return pos;
32364         
32365     },
32366     
32367     /**
32368     * remove a Masonry Brick
32369     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32370     */
32371     removeBrick : function(brick_id)
32372     {
32373         if (!brick_id) {
32374             return;
32375         }
32376         
32377         for (var i = 0; i<this.bricks.length; i++) {
32378             if (this.bricks[i].id == brick_id) {
32379                 this.bricks.splice(i,1);
32380                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32381                 this.initial();
32382             }
32383         }
32384     },
32385     
32386     /**
32387     * adds a Masonry Brick
32388     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32389     */
32390     addBrick : function(cfg)
32391     {
32392         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32393         //this.register(cn);
32394         cn.parentId = this.id;
32395         cn.render(this.el);
32396         return cn;
32397     },
32398     
32399     /**
32400     * register a Masonry Brick
32401     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32402     */
32403     
32404     register : function(brick)
32405     {
32406         this.bricks.push(brick);
32407         brick.masonryId = this.id;
32408     },
32409     
32410     /**
32411     * clear all the Masonry Brick
32412     */
32413     clearAll : function()
32414     {
32415         this.bricks = [];
32416         //this.getChildContainer().dom.innerHTML = "";
32417         this.el.dom.innerHTML = '';
32418     },
32419     
32420     getSelected : function()
32421     {
32422         if (!this.selectedBrick) {
32423             return false;
32424         }
32425         
32426         return this.selectedBrick;
32427     }
32428 });
32429
32430 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32431     
32432     groups: {},
32433      /**
32434     * register a Masonry Layout
32435     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32436     */
32437     
32438     register : function(layout)
32439     {
32440         this.groups[layout.id] = layout;
32441     },
32442     /**
32443     * fetch a  Masonry Layout based on the masonry layout ID
32444     * @param {string} the masonry layout to add
32445     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32446     */
32447     
32448     get: function(layout_id) {
32449         if (typeof(this.groups[layout_id]) == 'undefined') {
32450             return false;
32451         }
32452         return this.groups[layout_id] ;
32453     }
32454     
32455     
32456     
32457 });
32458
32459  
32460
32461  /**
32462  *
32463  * This is based on 
32464  * http://masonry.desandro.com
32465  *
32466  * The idea is to render all the bricks based on vertical width...
32467  *
32468  * The original code extends 'outlayer' - we might need to use that....
32469  * 
32470  */
32471
32472
32473 /**
32474  * @class Roo.bootstrap.LayoutMasonryAuto
32475  * @extends Roo.bootstrap.Component
32476  * Bootstrap Layout Masonry class
32477  * 
32478  * @constructor
32479  * Create a new Element
32480  * @param {Object} config The config object
32481  */
32482
32483 Roo.bootstrap.LayoutMasonryAuto = function(config){
32484     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32485 };
32486
32487 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32488     
32489       /**
32490      * @cfg {Boolean} isFitWidth  - resize the width..
32491      */   
32492     isFitWidth : false,  // options..
32493     /**
32494      * @cfg {Boolean} isOriginLeft = left align?
32495      */   
32496     isOriginLeft : true,
32497     /**
32498      * @cfg {Boolean} isOriginTop = top align?
32499      */   
32500     isOriginTop : false,
32501     /**
32502      * @cfg {Boolean} isLayoutInstant = no animation?
32503      */   
32504     isLayoutInstant : false, // needed?
32505     /**
32506      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32507      */   
32508     isResizingContainer : true,
32509     /**
32510      * @cfg {Number} columnWidth  width of the columns 
32511      */   
32512     
32513     columnWidth : 0,
32514     
32515     /**
32516      * @cfg {Number} maxCols maximum number of columns
32517      */   
32518     
32519     maxCols: 0,
32520     /**
32521      * @cfg {Number} padHeight padding below box..
32522      */   
32523     
32524     padHeight : 10, 
32525     
32526     /**
32527      * @cfg {Boolean} isAutoInitial defalut true
32528      */   
32529     
32530     isAutoInitial : true, 
32531     
32532     // private?
32533     gutter : 0,
32534     
32535     containerWidth: 0,
32536     initialColumnWidth : 0,
32537     currentSize : null,
32538     
32539     colYs : null, // array.
32540     maxY : 0,
32541     padWidth: 10,
32542     
32543     
32544     tag: 'div',
32545     cls: '',
32546     bricks: null, //CompositeElement
32547     cols : 0, // array?
32548     // element : null, // wrapped now this.el
32549     _isLayoutInited : null, 
32550     
32551     
32552     getAutoCreate : function(){
32553         
32554         var cfg = {
32555             tag: this.tag,
32556             cls: 'blog-masonary-wrapper ' + this.cls,
32557             cn : {
32558                 cls : 'mas-boxes masonary'
32559             }
32560         };
32561         
32562         return cfg;
32563     },
32564     
32565     getChildContainer: function( )
32566     {
32567         if (this.boxesEl) {
32568             return this.boxesEl;
32569         }
32570         
32571         this.boxesEl = this.el.select('.mas-boxes').first();
32572         
32573         return this.boxesEl;
32574     },
32575     
32576     
32577     initEvents : function()
32578     {
32579         var _this = this;
32580         
32581         if(this.isAutoInitial){
32582             Roo.log('hook children rendered');
32583             this.on('childrenrendered', function() {
32584                 Roo.log('children rendered');
32585                 _this.initial();
32586             } ,this);
32587         }
32588         
32589     },
32590     
32591     initial : function()
32592     {
32593         this.reloadItems();
32594
32595         this.currentSize = this.el.getBox(true);
32596
32597         /// was window resize... - let's see if this works..
32598         Roo.EventManager.onWindowResize(this.resize, this); 
32599
32600         if(!this.isAutoInitial){
32601             this.layout();
32602             return;
32603         }
32604         
32605         this.layout.defer(500,this);
32606     },
32607     
32608     reloadItems: function()
32609     {
32610         this.bricks = this.el.select('.masonry-brick', true);
32611         
32612         this.bricks.each(function(b) {
32613             //Roo.log(b.getSize());
32614             if (!b.attr('originalwidth')) {
32615                 b.attr('originalwidth',  b.getSize().width);
32616             }
32617             
32618         });
32619         
32620         Roo.log(this.bricks.elements.length);
32621     },
32622     
32623     resize : function()
32624     {
32625         Roo.log('resize');
32626         var cs = this.el.getBox(true);
32627         
32628         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32629             Roo.log("no change in with or X");
32630             return;
32631         }
32632         this.currentSize = cs;
32633         this.layout();
32634     },
32635     
32636     layout : function()
32637     {
32638          Roo.log('layout');
32639         this._resetLayout();
32640         //this._manageStamps();
32641       
32642         // don't animate first layout
32643         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32644         this.layoutItems( isInstant );
32645       
32646         // flag for initalized
32647         this._isLayoutInited = true;
32648     },
32649     
32650     layoutItems : function( isInstant )
32651     {
32652         //var items = this._getItemsForLayout( this.items );
32653         // original code supports filtering layout items.. we just ignore it..
32654         
32655         this._layoutItems( this.bricks , isInstant );
32656       
32657         this._postLayout();
32658     },
32659     _layoutItems : function ( items , isInstant)
32660     {
32661        //this.fireEvent( 'layout', this, items );
32662     
32663
32664         if ( !items || !items.elements.length ) {
32665           // no items, emit event with empty array
32666             return;
32667         }
32668
32669         var queue = [];
32670         items.each(function(item) {
32671             Roo.log("layout item");
32672             Roo.log(item);
32673             // get x/y object from method
32674             var position = this._getItemLayoutPosition( item );
32675             // enqueue
32676             position.item = item;
32677             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32678             queue.push( position );
32679         }, this);
32680       
32681         this._processLayoutQueue( queue );
32682     },
32683     /** Sets position of item in DOM
32684     * @param {Element} item
32685     * @param {Number} x - horizontal position
32686     * @param {Number} y - vertical position
32687     * @param {Boolean} isInstant - disables transitions
32688     */
32689     _processLayoutQueue : function( queue )
32690     {
32691         for ( var i=0, len = queue.length; i < len; i++ ) {
32692             var obj = queue[i];
32693             obj.item.position('absolute');
32694             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32695         }
32696     },
32697       
32698     
32699     /**
32700     * Any logic you want to do after each layout,
32701     * i.e. size the container
32702     */
32703     _postLayout : function()
32704     {
32705         this.resizeContainer();
32706     },
32707     
32708     resizeContainer : function()
32709     {
32710         if ( !this.isResizingContainer ) {
32711             return;
32712         }
32713         var size = this._getContainerSize();
32714         if ( size ) {
32715             this.el.setSize(size.width,size.height);
32716             this.boxesEl.setSize(size.width,size.height);
32717         }
32718     },
32719     
32720     
32721     
32722     _resetLayout : function()
32723     {
32724         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32725         this.colWidth = this.el.getWidth();
32726         //this.gutter = this.el.getWidth(); 
32727         
32728         this.measureColumns();
32729
32730         // reset column Y
32731         var i = this.cols;
32732         this.colYs = [];
32733         while (i--) {
32734             this.colYs.push( 0 );
32735         }
32736     
32737         this.maxY = 0;
32738     },
32739
32740     measureColumns : function()
32741     {
32742         this.getContainerWidth();
32743       // if columnWidth is 0, default to outerWidth of first item
32744         if ( !this.columnWidth ) {
32745             var firstItem = this.bricks.first();
32746             Roo.log(firstItem);
32747             this.columnWidth  = this.containerWidth;
32748             if (firstItem && firstItem.attr('originalwidth') ) {
32749                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32750             }
32751             // columnWidth fall back to item of first element
32752             Roo.log("set column width?");
32753                         this.initialColumnWidth = this.columnWidth  ;
32754
32755             // if first elem has no width, default to size of container
32756             
32757         }
32758         
32759         
32760         if (this.initialColumnWidth) {
32761             this.columnWidth = this.initialColumnWidth;
32762         }
32763         
32764         
32765             
32766         // column width is fixed at the top - however if container width get's smaller we should
32767         // reduce it...
32768         
32769         // this bit calcs how man columns..
32770             
32771         var columnWidth = this.columnWidth += this.gutter;
32772       
32773         // calculate columns
32774         var containerWidth = this.containerWidth + this.gutter;
32775         
32776         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32777         // fix rounding errors, typically with gutters
32778         var excess = columnWidth - containerWidth % columnWidth;
32779         
32780         
32781         // if overshoot is less than a pixel, round up, otherwise floor it
32782         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32783         cols = Math[ mathMethod ]( cols );
32784         this.cols = Math.max( cols, 1 );
32785         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32786         
32787          // padding positioning..
32788         var totalColWidth = this.cols * this.columnWidth;
32789         var padavail = this.containerWidth - totalColWidth;
32790         // so for 2 columns - we need 3 'pads'
32791         
32792         var padNeeded = (1+this.cols) * this.padWidth;
32793         
32794         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32795         
32796         this.columnWidth += padExtra
32797         //this.padWidth = Math.floor(padavail /  ( this.cols));
32798         
32799         // adjust colum width so that padding is fixed??
32800         
32801         // we have 3 columns ... total = width * 3
32802         // we have X left over... that should be used by 
32803         
32804         //if (this.expandC) {
32805             
32806         //}
32807         
32808         
32809         
32810     },
32811     
32812     getContainerWidth : function()
32813     {
32814        /* // container is parent if fit width
32815         var container = this.isFitWidth ? this.element.parentNode : this.element;
32816         // check that this.size and size are there
32817         // IE8 triggers resize on body size change, so they might not be
32818         
32819         var size = getSize( container );  //FIXME
32820         this.containerWidth = size && size.innerWidth; //FIXME
32821         */
32822          
32823         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32824         
32825     },
32826     
32827     _getItemLayoutPosition : function( item )  // what is item?
32828     {
32829         // we resize the item to our columnWidth..
32830       
32831         item.setWidth(this.columnWidth);
32832         item.autoBoxAdjust  = false;
32833         
32834         var sz = item.getSize();
32835  
32836         // how many columns does this brick span
32837         var remainder = this.containerWidth % this.columnWidth;
32838         
32839         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32840         // round if off by 1 pixel, otherwise use ceil
32841         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32842         colSpan = Math.min( colSpan, this.cols );
32843         
32844         // normally this should be '1' as we dont' currently allow multi width columns..
32845         
32846         var colGroup = this._getColGroup( colSpan );
32847         // get the minimum Y value from the columns
32848         var minimumY = Math.min.apply( Math, colGroup );
32849         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32850         
32851         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32852          
32853         // position the brick
32854         var position = {
32855             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32856             y: this.currentSize.y + minimumY + this.padHeight
32857         };
32858         
32859         Roo.log(position);
32860         // apply setHeight to necessary columns
32861         var setHeight = minimumY + sz.height + this.padHeight;
32862         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32863         
32864         var setSpan = this.cols + 1 - colGroup.length;
32865         for ( var i = 0; i < setSpan; i++ ) {
32866           this.colYs[ shortColIndex + i ] = setHeight ;
32867         }
32868       
32869         return position;
32870     },
32871     
32872     /**
32873      * @param {Number} colSpan - number of columns the element spans
32874      * @returns {Array} colGroup
32875      */
32876     _getColGroup : function( colSpan )
32877     {
32878         if ( colSpan < 2 ) {
32879           // if brick spans only one column, use all the column Ys
32880           return this.colYs;
32881         }
32882       
32883         var colGroup = [];
32884         // how many different places could this brick fit horizontally
32885         var groupCount = this.cols + 1 - colSpan;
32886         // for each group potential horizontal position
32887         for ( var i = 0; i < groupCount; i++ ) {
32888           // make an array of colY values for that one group
32889           var groupColYs = this.colYs.slice( i, i + colSpan );
32890           // and get the max value of the array
32891           colGroup[i] = Math.max.apply( Math, groupColYs );
32892         }
32893         return colGroup;
32894     },
32895     /*
32896     _manageStamp : function( stamp )
32897     {
32898         var stampSize =  stamp.getSize();
32899         var offset = stamp.getBox();
32900         // get the columns that this stamp affects
32901         var firstX = this.isOriginLeft ? offset.x : offset.right;
32902         var lastX = firstX + stampSize.width;
32903         var firstCol = Math.floor( firstX / this.columnWidth );
32904         firstCol = Math.max( 0, firstCol );
32905         
32906         var lastCol = Math.floor( lastX / this.columnWidth );
32907         // lastCol should not go over if multiple of columnWidth #425
32908         lastCol -= lastX % this.columnWidth ? 0 : 1;
32909         lastCol = Math.min( this.cols - 1, lastCol );
32910         
32911         // set colYs to bottom of the stamp
32912         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32913             stampSize.height;
32914             
32915         for ( var i = firstCol; i <= lastCol; i++ ) {
32916           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32917         }
32918     },
32919     */
32920     
32921     _getContainerSize : function()
32922     {
32923         this.maxY = Math.max.apply( Math, this.colYs );
32924         var size = {
32925             height: this.maxY
32926         };
32927       
32928         if ( this.isFitWidth ) {
32929             size.width = this._getContainerFitWidth();
32930         }
32931       
32932         return size;
32933     },
32934     
32935     _getContainerFitWidth : function()
32936     {
32937         var unusedCols = 0;
32938         // count unused columns
32939         var i = this.cols;
32940         while ( --i ) {
32941           if ( this.colYs[i] !== 0 ) {
32942             break;
32943           }
32944           unusedCols++;
32945         }
32946         // fit container to columns that have been used
32947         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32948     },
32949     
32950     needsResizeLayout : function()
32951     {
32952         var previousWidth = this.containerWidth;
32953         this.getContainerWidth();
32954         return previousWidth !== this.containerWidth;
32955     }
32956  
32957 });
32958
32959  
32960
32961  /*
32962  * - LGPL
32963  *
32964  * element
32965  * 
32966  */
32967
32968 /**
32969  * @class Roo.bootstrap.MasonryBrick
32970  * @extends Roo.bootstrap.Component
32971  * Bootstrap MasonryBrick class
32972  * 
32973  * @constructor
32974  * Create a new MasonryBrick
32975  * @param {Object} config The config object
32976  */
32977
32978 Roo.bootstrap.MasonryBrick = function(config){
32979     
32980     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32981     
32982     Roo.bootstrap.MasonryBrick.register(this);
32983     
32984     this.addEvents({
32985         // raw events
32986         /**
32987          * @event click
32988          * When a MasonryBrick is clcik
32989          * @param {Roo.bootstrap.MasonryBrick} this
32990          * @param {Roo.EventObject} e
32991          */
32992         "click" : true
32993     });
32994 };
32995
32996 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32997     
32998     /**
32999      * @cfg {String} title
33000      */   
33001     title : '',
33002     /**
33003      * @cfg {String} html
33004      */   
33005     html : '',
33006     /**
33007      * @cfg {String} bgimage
33008      */   
33009     bgimage : '',
33010     /**
33011      * @cfg {String} videourl
33012      */   
33013     videourl : '',
33014     /**
33015      * @cfg {String} cls
33016      */   
33017     cls : '',
33018     /**
33019      * @cfg {String} href
33020      */   
33021     href : '',
33022     /**
33023      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33024      */   
33025     size : 'xs',
33026     
33027     /**
33028      * @cfg {String} placetitle (center|bottom)
33029      */   
33030     placetitle : '',
33031     
33032     /**
33033      * @cfg {Boolean} isFitContainer defalut true
33034      */   
33035     isFitContainer : true, 
33036     
33037     /**
33038      * @cfg {Boolean} preventDefault defalut false
33039      */   
33040     preventDefault : false, 
33041     
33042     /**
33043      * @cfg {Boolean} inverse defalut false
33044      */   
33045     maskInverse : false, 
33046     
33047     getAutoCreate : function()
33048     {
33049         if(!this.isFitContainer){
33050             return this.getSplitAutoCreate();
33051         }
33052         
33053         var cls = 'masonry-brick masonry-brick-full';
33054         
33055         if(this.href.length){
33056             cls += ' masonry-brick-link';
33057         }
33058         
33059         if(this.bgimage.length){
33060             cls += ' masonry-brick-image';
33061         }
33062         
33063         if(this.maskInverse){
33064             cls += ' mask-inverse';
33065         }
33066         
33067         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33068             cls += ' enable-mask';
33069         }
33070         
33071         if(this.size){
33072             cls += ' masonry-' + this.size + '-brick';
33073         }
33074         
33075         if(this.placetitle.length){
33076             
33077             switch (this.placetitle) {
33078                 case 'center' :
33079                     cls += ' masonry-center-title';
33080                     break;
33081                 case 'bottom' :
33082                     cls += ' masonry-bottom-title';
33083                     break;
33084                 default:
33085                     break;
33086             }
33087             
33088         } else {
33089             if(!this.html.length && !this.bgimage.length){
33090                 cls += ' masonry-center-title';
33091             }
33092
33093             if(!this.html.length && this.bgimage.length){
33094                 cls += ' masonry-bottom-title';
33095             }
33096         }
33097         
33098         if(this.cls){
33099             cls += ' ' + this.cls;
33100         }
33101         
33102         var cfg = {
33103             tag: (this.href.length) ? 'a' : 'div',
33104             cls: cls,
33105             cn: [
33106                 {
33107                     tag: 'div',
33108                     cls: 'masonry-brick-mask'
33109                 },
33110                 {
33111                     tag: 'div',
33112                     cls: 'masonry-brick-paragraph',
33113                     cn: []
33114                 }
33115             ]
33116         };
33117         
33118         if(this.href.length){
33119             cfg.href = this.href;
33120         }
33121         
33122         var cn = cfg.cn[1].cn;
33123         
33124         if(this.title.length){
33125             cn.push({
33126                 tag: 'h4',
33127                 cls: 'masonry-brick-title',
33128                 html: this.title
33129             });
33130         }
33131         
33132         if(this.html.length){
33133             cn.push({
33134                 tag: 'p',
33135                 cls: 'masonry-brick-text',
33136                 html: this.html
33137             });
33138         }
33139         
33140         if (!this.title.length && !this.html.length) {
33141             cfg.cn[1].cls += ' hide';
33142         }
33143         
33144         if(this.bgimage.length){
33145             cfg.cn.push({
33146                 tag: 'img',
33147                 cls: 'masonry-brick-image-view',
33148                 src: this.bgimage
33149             });
33150         }
33151         
33152         if(this.videourl.length){
33153             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33154             // youtube support only?
33155             cfg.cn.push({
33156                 tag: 'iframe',
33157                 cls: 'masonry-brick-image-view',
33158                 src: vurl,
33159                 frameborder : 0,
33160                 allowfullscreen : true
33161             });
33162         }
33163         
33164         return cfg;
33165         
33166     },
33167     
33168     getSplitAutoCreate : function()
33169     {
33170         var cls = 'masonry-brick masonry-brick-split';
33171         
33172         if(this.href.length){
33173             cls += ' masonry-brick-link';
33174         }
33175         
33176         if(this.bgimage.length){
33177             cls += ' masonry-brick-image';
33178         }
33179         
33180         if(this.size){
33181             cls += ' masonry-' + this.size + '-brick';
33182         }
33183         
33184         switch (this.placetitle) {
33185             case 'center' :
33186                 cls += ' masonry-center-title';
33187                 break;
33188             case 'bottom' :
33189                 cls += ' masonry-bottom-title';
33190                 break;
33191             default:
33192                 if(!this.bgimage.length){
33193                     cls += ' masonry-center-title';
33194                 }
33195
33196                 if(this.bgimage.length){
33197                     cls += ' masonry-bottom-title';
33198                 }
33199                 break;
33200         }
33201         
33202         if(this.cls){
33203             cls += ' ' + this.cls;
33204         }
33205         
33206         var cfg = {
33207             tag: (this.href.length) ? 'a' : 'div',
33208             cls: cls,
33209             cn: [
33210                 {
33211                     tag: 'div',
33212                     cls: 'masonry-brick-split-head',
33213                     cn: [
33214                         {
33215                             tag: 'div',
33216                             cls: 'masonry-brick-paragraph',
33217                             cn: []
33218                         }
33219                     ]
33220                 },
33221                 {
33222                     tag: 'div',
33223                     cls: 'masonry-brick-split-body',
33224                     cn: []
33225                 }
33226             ]
33227         };
33228         
33229         if(this.href.length){
33230             cfg.href = this.href;
33231         }
33232         
33233         if(this.title.length){
33234             cfg.cn[0].cn[0].cn.push({
33235                 tag: 'h4',
33236                 cls: 'masonry-brick-title',
33237                 html: this.title
33238             });
33239         }
33240         
33241         if(this.html.length){
33242             cfg.cn[1].cn.push({
33243                 tag: 'p',
33244                 cls: 'masonry-brick-text',
33245                 html: this.html
33246             });
33247         }
33248
33249         if(this.bgimage.length){
33250             cfg.cn[0].cn.push({
33251                 tag: 'img',
33252                 cls: 'masonry-brick-image-view',
33253                 src: this.bgimage
33254             });
33255         }
33256         
33257         if(this.videourl.length){
33258             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33259             // youtube support only?
33260             cfg.cn[0].cn.cn.push({
33261                 tag: 'iframe',
33262                 cls: 'masonry-brick-image-view',
33263                 src: vurl,
33264                 frameborder : 0,
33265                 allowfullscreen : true
33266             });
33267         }
33268         
33269         return cfg;
33270     },
33271     
33272     initEvents: function() 
33273     {
33274         switch (this.size) {
33275             case 'xs' :
33276                 this.x = 1;
33277                 this.y = 1;
33278                 break;
33279             case 'sm' :
33280                 this.x = 2;
33281                 this.y = 2;
33282                 break;
33283             case 'md' :
33284             case 'md-left' :
33285             case 'md-right' :
33286                 this.x = 3;
33287                 this.y = 3;
33288                 break;
33289             case 'tall' :
33290                 this.x = 2;
33291                 this.y = 3;
33292                 break;
33293             case 'wide' :
33294                 this.x = 3;
33295                 this.y = 2;
33296                 break;
33297             case 'wide-thin' :
33298                 this.x = 3;
33299                 this.y = 1;
33300                 break;
33301                         
33302             default :
33303                 break;
33304         }
33305         
33306         if(Roo.isTouch){
33307             this.el.on('touchstart', this.onTouchStart, this);
33308             this.el.on('touchmove', this.onTouchMove, this);
33309             this.el.on('touchend', this.onTouchEnd, this);
33310             this.el.on('contextmenu', this.onContextMenu, this);
33311         } else {
33312             this.el.on('mouseenter'  ,this.enter, this);
33313             this.el.on('mouseleave', this.leave, this);
33314             this.el.on('click', this.onClick, this);
33315         }
33316         
33317         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33318             this.parent().bricks.push(this);   
33319         }
33320         
33321     },
33322     
33323     onClick: function(e, el)
33324     {
33325         var time = this.endTimer - this.startTimer;
33326         // Roo.log(e.preventDefault());
33327         if(Roo.isTouch){
33328             if(time > 1000){
33329                 e.preventDefault();
33330                 return;
33331             }
33332         }
33333         
33334         if(!this.preventDefault){
33335             return;
33336         }
33337         
33338         e.preventDefault();
33339         
33340         if (this.activeClass != '') {
33341             this.selectBrick();
33342         }
33343         
33344         this.fireEvent('click', this, e);
33345     },
33346     
33347     enter: function(e, el)
33348     {
33349         e.preventDefault();
33350         
33351         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33352             return;
33353         }
33354         
33355         if(this.bgimage.length && this.html.length){
33356             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33357         }
33358     },
33359     
33360     leave: function(e, el)
33361     {
33362         e.preventDefault();
33363         
33364         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33365             return;
33366         }
33367         
33368         if(this.bgimage.length && this.html.length){
33369             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33370         }
33371     },
33372     
33373     onTouchStart: function(e, el)
33374     {
33375 //        e.preventDefault();
33376         
33377         this.touchmoved = false;
33378         
33379         if(!this.isFitContainer){
33380             return;
33381         }
33382         
33383         if(!this.bgimage.length || !this.html.length){
33384             return;
33385         }
33386         
33387         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33388         
33389         this.timer = new Date().getTime();
33390         
33391     },
33392     
33393     onTouchMove: function(e, el)
33394     {
33395         this.touchmoved = true;
33396     },
33397     
33398     onContextMenu : function(e,el)
33399     {
33400         e.preventDefault();
33401         e.stopPropagation();
33402         return false;
33403     },
33404     
33405     onTouchEnd: function(e, el)
33406     {
33407 //        e.preventDefault();
33408         
33409         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33410         
33411             this.leave(e,el);
33412             
33413             return;
33414         }
33415         
33416         if(!this.bgimage.length || !this.html.length){
33417             
33418             if(this.href.length){
33419                 window.location.href = this.href;
33420             }
33421             
33422             return;
33423         }
33424         
33425         if(!this.isFitContainer){
33426             return;
33427         }
33428         
33429         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33430         
33431         window.location.href = this.href;
33432     },
33433     
33434     //selection on single brick only
33435     selectBrick : function() {
33436         
33437         if (!this.parentId) {
33438             return;
33439         }
33440         
33441         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33442         var index = m.selectedBrick.indexOf(this.id);
33443         
33444         if ( index > -1) {
33445             m.selectedBrick.splice(index,1);
33446             this.el.removeClass(this.activeClass);
33447             return;
33448         }
33449         
33450         for(var i = 0; i < m.selectedBrick.length; i++) {
33451             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33452             b.el.removeClass(b.activeClass);
33453         }
33454         
33455         m.selectedBrick = [];
33456         
33457         m.selectedBrick.push(this.id);
33458         this.el.addClass(this.activeClass);
33459         return;
33460     },
33461     
33462     isSelected : function(){
33463         return this.el.hasClass(this.activeClass);
33464         
33465     }
33466 });
33467
33468 Roo.apply(Roo.bootstrap.MasonryBrick, {
33469     
33470     //groups: {},
33471     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33472      /**
33473     * register a Masonry Brick
33474     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33475     */
33476     
33477     register : function(brick)
33478     {
33479         //this.groups[brick.id] = brick;
33480         this.groups.add(brick.id, brick);
33481     },
33482     /**
33483     * fetch a  masonry brick based on the masonry brick ID
33484     * @param {string} the masonry brick to add
33485     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33486     */
33487     
33488     get: function(brick_id) 
33489     {
33490         // if (typeof(this.groups[brick_id]) == 'undefined') {
33491         //     return false;
33492         // }
33493         // return this.groups[brick_id] ;
33494         
33495         if(this.groups.key(brick_id)) {
33496             return this.groups.key(brick_id);
33497         }
33498         
33499         return false;
33500     }
33501     
33502     
33503     
33504 });
33505
33506  /*
33507  * - LGPL
33508  *
33509  * element
33510  * 
33511  */
33512
33513 /**
33514  * @class Roo.bootstrap.Brick
33515  * @extends Roo.bootstrap.Component
33516  * Bootstrap Brick class
33517  * 
33518  * @constructor
33519  * Create a new Brick
33520  * @param {Object} config The config object
33521  */
33522
33523 Roo.bootstrap.Brick = function(config){
33524     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33525     
33526     this.addEvents({
33527         // raw events
33528         /**
33529          * @event click
33530          * When a Brick is click
33531          * @param {Roo.bootstrap.Brick} this
33532          * @param {Roo.EventObject} e
33533          */
33534         "click" : true
33535     });
33536 };
33537
33538 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33539     
33540     /**
33541      * @cfg {String} title
33542      */   
33543     title : '',
33544     /**
33545      * @cfg {String} html
33546      */   
33547     html : '',
33548     /**
33549      * @cfg {String} bgimage
33550      */   
33551     bgimage : '',
33552     /**
33553      * @cfg {String} cls
33554      */   
33555     cls : '',
33556     /**
33557      * @cfg {String} href
33558      */   
33559     href : '',
33560     /**
33561      * @cfg {String} video
33562      */   
33563     video : '',
33564     /**
33565      * @cfg {Boolean} square
33566      */   
33567     square : true,
33568     
33569     getAutoCreate : function()
33570     {
33571         var cls = 'roo-brick';
33572         
33573         if(this.href.length){
33574             cls += ' roo-brick-link';
33575         }
33576         
33577         if(this.bgimage.length){
33578             cls += ' roo-brick-image';
33579         }
33580         
33581         if(!this.html.length && !this.bgimage.length){
33582             cls += ' roo-brick-center-title';
33583         }
33584         
33585         if(!this.html.length && this.bgimage.length){
33586             cls += ' roo-brick-bottom-title';
33587         }
33588         
33589         if(this.cls){
33590             cls += ' ' + this.cls;
33591         }
33592         
33593         var cfg = {
33594             tag: (this.href.length) ? 'a' : 'div',
33595             cls: cls,
33596             cn: [
33597                 {
33598                     tag: 'div',
33599                     cls: 'roo-brick-paragraph',
33600                     cn: []
33601                 }
33602             ]
33603         };
33604         
33605         if(this.href.length){
33606             cfg.href = this.href;
33607         }
33608         
33609         var cn = cfg.cn[0].cn;
33610         
33611         if(this.title.length){
33612             cn.push({
33613                 tag: 'h4',
33614                 cls: 'roo-brick-title',
33615                 html: this.title
33616             });
33617         }
33618         
33619         if(this.html.length){
33620             cn.push({
33621                 tag: 'p',
33622                 cls: 'roo-brick-text',
33623                 html: this.html
33624             });
33625         } else {
33626             cn.cls += ' hide';
33627         }
33628         
33629         if(this.bgimage.length){
33630             cfg.cn.push({
33631                 tag: 'img',
33632                 cls: 'roo-brick-image-view',
33633                 src: this.bgimage
33634             });
33635         }
33636         
33637         return cfg;
33638     },
33639     
33640     initEvents: function() 
33641     {
33642         if(this.title.length || this.html.length){
33643             this.el.on('mouseenter'  ,this.enter, this);
33644             this.el.on('mouseleave', this.leave, this);
33645         }
33646         
33647         Roo.EventManager.onWindowResize(this.resize, this); 
33648         
33649         if(this.bgimage.length){
33650             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33651             this.imageEl.on('load', this.onImageLoad, this);
33652             return;
33653         }
33654         
33655         this.resize();
33656     },
33657     
33658     onImageLoad : function()
33659     {
33660         this.resize();
33661     },
33662     
33663     resize : function()
33664     {
33665         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33666         
33667         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33668         
33669         if(this.bgimage.length){
33670             var image = this.el.select('.roo-brick-image-view', true).first();
33671             
33672             image.setWidth(paragraph.getWidth());
33673             
33674             if(this.square){
33675                 image.setHeight(paragraph.getWidth());
33676             }
33677             
33678             this.el.setHeight(image.getHeight());
33679             paragraph.setHeight(image.getHeight());
33680             
33681         }
33682         
33683     },
33684     
33685     enter: function(e, el)
33686     {
33687         e.preventDefault();
33688         
33689         if(this.bgimage.length){
33690             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33691             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33692         }
33693     },
33694     
33695     leave: function(e, el)
33696     {
33697         e.preventDefault();
33698         
33699         if(this.bgimage.length){
33700             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33701             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33702         }
33703     }
33704     
33705 });
33706
33707  
33708
33709  /*
33710  * - LGPL
33711  *
33712  * Number field 
33713  */
33714
33715 /**
33716  * @class Roo.bootstrap.NumberField
33717  * @extends Roo.bootstrap.Input
33718  * Bootstrap NumberField class
33719  * 
33720  * 
33721  * 
33722  * 
33723  * @constructor
33724  * Create a new NumberField
33725  * @param {Object} config The config object
33726  */
33727
33728 Roo.bootstrap.NumberField = function(config){
33729     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33730 };
33731
33732 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33733     
33734     /**
33735      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33736      */
33737     allowDecimals : true,
33738     /**
33739      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33740      */
33741     decimalSeparator : ".",
33742     /**
33743      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33744      */
33745     decimalPrecision : 2,
33746     /**
33747      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33748      */
33749     allowNegative : true,
33750     
33751     /**
33752      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33753      */
33754     allowZero: true,
33755     /**
33756      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33757      */
33758     minValue : Number.NEGATIVE_INFINITY,
33759     /**
33760      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33761      */
33762     maxValue : Number.MAX_VALUE,
33763     /**
33764      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33765      */
33766     minText : "The minimum value for this field is {0}",
33767     /**
33768      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33769      */
33770     maxText : "The maximum value for this field is {0}",
33771     /**
33772      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33773      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33774      */
33775     nanText : "{0} is not a valid number",
33776     /**
33777      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33778      */
33779     thousandsDelimiter : false,
33780     /**
33781      * @cfg {String} valueAlign alignment of value
33782      */
33783     valueAlign : "left",
33784
33785     getAutoCreate : function()
33786     {
33787         var hiddenInput = {
33788             tag: 'input',
33789             type: 'hidden',
33790             id: Roo.id(),
33791             cls: 'hidden-number-input'
33792         };
33793         
33794         if (this.name) {
33795             hiddenInput.name = this.name;
33796         }
33797         
33798         this.name = '';
33799         
33800         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33801         
33802         this.name = hiddenInput.name;
33803         
33804         if(cfg.cn.length > 0) {
33805             cfg.cn.push(hiddenInput);
33806         }
33807         
33808         return cfg;
33809     },
33810
33811     // private
33812     initEvents : function()
33813     {   
33814         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33815         
33816         var allowed = "0123456789";
33817         
33818         if(this.allowDecimals){
33819             allowed += this.decimalSeparator;
33820         }
33821         
33822         if(this.allowNegative){
33823             allowed += "-";
33824         }
33825         
33826         if(this.thousandsDelimiter) {
33827             allowed += ",";
33828         }
33829         
33830         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33831         
33832         var keyPress = function(e){
33833             
33834             var k = e.getKey();
33835             
33836             var c = e.getCharCode();
33837             
33838             if(
33839                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33840                     allowed.indexOf(String.fromCharCode(c)) === -1
33841             ){
33842                 e.stopEvent();
33843                 return;
33844             }
33845             
33846             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33847                 return;
33848             }
33849             
33850             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33851                 e.stopEvent();
33852             }
33853         };
33854         
33855         this.el.on("keypress", keyPress, this);
33856     },
33857     
33858     validateValue : function(value)
33859     {
33860         
33861         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33862             return false;
33863         }
33864         
33865         var num = this.parseValue(value);
33866         
33867         if(isNaN(num)){
33868             this.markInvalid(String.format(this.nanText, value));
33869             return false;
33870         }
33871         
33872         if(num < this.minValue){
33873             this.markInvalid(String.format(this.minText, this.minValue));
33874             return false;
33875         }
33876         
33877         if(num > this.maxValue){
33878             this.markInvalid(String.format(this.maxText, this.maxValue));
33879             return false;
33880         }
33881         
33882         return true;
33883     },
33884
33885     getValue : function()
33886     {
33887         var v = this.hiddenEl().getValue();
33888         
33889         return this.fixPrecision(this.parseValue(v));
33890     },
33891
33892     parseValue : function(value)
33893     {
33894         if(this.thousandsDelimiter) {
33895             value += "";
33896             r = new RegExp(",", "g");
33897             value = value.replace(r, "");
33898         }
33899         
33900         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33901         return isNaN(value) ? '' : value;
33902     },
33903
33904     fixPrecision : function(value)
33905     {
33906         if(this.thousandsDelimiter) {
33907             value += "";
33908             r = new RegExp(",", "g");
33909             value = value.replace(r, "");
33910         }
33911         
33912         var nan = isNaN(value);
33913         
33914         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33915             return nan ? '' : value;
33916         }
33917         return parseFloat(value).toFixed(this.decimalPrecision);
33918     },
33919
33920     setValue : function(v)
33921     {
33922         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33923         
33924         this.value = v;
33925         
33926         if(this.rendered){
33927             
33928             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33929             
33930             this.inputEl().dom.value = (v == '') ? '' :
33931                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33932             
33933             if(!this.allowZero && v === '0') {
33934                 this.hiddenEl().dom.value = '';
33935                 this.inputEl().dom.value = '';
33936             }
33937             
33938             this.validate();
33939         }
33940     },
33941
33942     decimalPrecisionFcn : function(v)
33943     {
33944         return Math.floor(v);
33945     },
33946
33947     beforeBlur : function()
33948     {
33949         var v = this.parseValue(this.getRawValue());
33950         
33951         if(v || v === 0 || v === ''){
33952             this.setValue(v);
33953         }
33954     },
33955     
33956     hiddenEl : function()
33957     {
33958         return this.el.select('input.hidden-number-input',true).first();
33959     }
33960     
33961 });
33962
33963  
33964
33965 /*
33966 * Licence: LGPL
33967 */
33968
33969 /**
33970  * @class Roo.bootstrap.DocumentSlider
33971  * @extends Roo.bootstrap.Component
33972  * Bootstrap DocumentSlider class
33973  * 
33974  * @constructor
33975  * Create a new DocumentViewer
33976  * @param {Object} config The config object
33977  */
33978
33979 Roo.bootstrap.DocumentSlider = function(config){
33980     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33981     
33982     this.files = [];
33983     
33984     this.addEvents({
33985         /**
33986          * @event initial
33987          * Fire after initEvent
33988          * @param {Roo.bootstrap.DocumentSlider} this
33989          */
33990         "initial" : true,
33991         /**
33992          * @event update
33993          * Fire after update
33994          * @param {Roo.bootstrap.DocumentSlider} this
33995          */
33996         "update" : true,
33997         /**
33998          * @event click
33999          * Fire after click
34000          * @param {Roo.bootstrap.DocumentSlider} this
34001          */
34002         "click" : true
34003     });
34004 };
34005
34006 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
34007     
34008     files : false,
34009     
34010     indicator : 0,
34011     
34012     getAutoCreate : function()
34013     {
34014         var cfg = {
34015             tag : 'div',
34016             cls : 'roo-document-slider',
34017             cn : [
34018                 {
34019                     tag : 'div',
34020                     cls : 'roo-document-slider-header',
34021                     cn : [
34022                         {
34023                             tag : 'div',
34024                             cls : 'roo-document-slider-header-title'
34025                         }
34026                     ]
34027                 },
34028                 {
34029                     tag : 'div',
34030                     cls : 'roo-document-slider-body',
34031                     cn : [
34032                         {
34033                             tag : 'div',
34034                             cls : 'roo-document-slider-prev',
34035                             cn : [
34036                                 {
34037                                     tag : 'i',
34038                                     cls : 'fa fa-chevron-left'
34039                                 }
34040                             ]
34041                         },
34042                         {
34043                             tag : 'div',
34044                             cls : 'roo-document-slider-thumb',
34045                             cn : [
34046                                 {
34047                                     tag : 'img',
34048                                     cls : 'roo-document-slider-image'
34049                                 }
34050                             ]
34051                         },
34052                         {
34053                             tag : 'div',
34054                             cls : 'roo-document-slider-next',
34055                             cn : [
34056                                 {
34057                                     tag : 'i',
34058                                     cls : 'fa fa-chevron-right'
34059                                 }
34060                             ]
34061                         }
34062                     ]
34063                 }
34064             ]
34065         };
34066         
34067         return cfg;
34068     },
34069     
34070     initEvents : function()
34071     {
34072         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34073         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34074         
34075         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34076         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34077         
34078         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34079         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34080         
34081         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34082         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34083         
34084         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34085         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34086         
34087         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34088         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34089         
34090         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34091         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34092         
34093         this.thumbEl.on('click', this.onClick, this);
34094         
34095         this.prevIndicator.on('click', this.prev, this);
34096         
34097         this.nextIndicator.on('click', this.next, this);
34098         
34099     },
34100     
34101     initial : function()
34102     {
34103         if(this.files.length){
34104             this.indicator = 1;
34105             this.update()
34106         }
34107         
34108         this.fireEvent('initial', this);
34109     },
34110     
34111     update : function()
34112     {
34113         this.imageEl.attr('src', this.files[this.indicator - 1]);
34114         
34115         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34116         
34117         this.prevIndicator.show();
34118         
34119         if(this.indicator == 1){
34120             this.prevIndicator.hide();
34121         }
34122         
34123         this.nextIndicator.show();
34124         
34125         if(this.indicator == this.files.length){
34126             this.nextIndicator.hide();
34127         }
34128         
34129         this.thumbEl.scrollTo('top');
34130         
34131         this.fireEvent('update', this);
34132     },
34133     
34134     onClick : function(e)
34135     {
34136         e.preventDefault();
34137         
34138         this.fireEvent('click', this);
34139     },
34140     
34141     prev : function(e)
34142     {
34143         e.preventDefault();
34144         
34145         this.indicator = Math.max(1, this.indicator - 1);
34146         
34147         this.update();
34148     },
34149     
34150     next : function(e)
34151     {
34152         e.preventDefault();
34153         
34154         this.indicator = Math.min(this.files.length, this.indicator + 1);
34155         
34156         this.update();
34157     }
34158 });
34159 /*
34160  * - LGPL
34161  *
34162  * RadioSet
34163  *
34164  *
34165  */
34166
34167 /**
34168  * @class Roo.bootstrap.RadioSet
34169  * @extends Roo.bootstrap.Input
34170  * Bootstrap RadioSet class
34171  * @cfg {String} indicatorpos (left|right) default left
34172  * @cfg {Boolean} inline (true|false) inline the element (default true)
34173  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34174  * @constructor
34175  * Create a new RadioSet
34176  * @param {Object} config The config object
34177  */
34178
34179 Roo.bootstrap.RadioSet = function(config){
34180     
34181     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34182     
34183     this.radioes = [];
34184     
34185     Roo.bootstrap.RadioSet.register(this);
34186     
34187     this.addEvents({
34188         /**
34189         * @event check
34190         * Fires when the element is checked or unchecked.
34191         * @param {Roo.bootstrap.RadioSet} this This radio
34192         * @param {Roo.bootstrap.Radio} item The checked item
34193         */
34194        check : true,
34195        /**
34196         * @event click
34197         * Fires when the element is click.
34198         * @param {Roo.bootstrap.RadioSet} this This radio set
34199         * @param {Roo.bootstrap.Radio} item The checked item
34200         * @param {Roo.EventObject} e The event object
34201         */
34202        click : true
34203     });
34204     
34205 };
34206
34207 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34208
34209     radioes : false,
34210     
34211     inline : true,
34212     
34213     weight : '',
34214     
34215     indicatorpos : 'left',
34216     
34217     getAutoCreate : function()
34218     {
34219         var label = {
34220             tag : 'label',
34221             cls : 'roo-radio-set-label',
34222             cn : [
34223                 {
34224                     tag : 'span',
34225                     html : this.fieldLabel
34226                 }
34227             ]
34228         };
34229         if (Roo.bootstrap.version == 3) {
34230             
34231             
34232             if(this.indicatorpos == 'left'){
34233                 label.cn.unshift({
34234                     tag : 'i',
34235                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34236                     tooltip : 'This field is required'
34237                 });
34238             } else {
34239                 label.cn.push({
34240                     tag : 'i',
34241                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34242                     tooltip : 'This field is required'
34243                 });
34244             }
34245         }
34246         var items = {
34247             tag : 'div',
34248             cls : 'roo-radio-set-items'
34249         };
34250         
34251         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34252         
34253         if (align === 'left' && this.fieldLabel.length) {
34254             
34255             items = {
34256                 cls : "roo-radio-set-right", 
34257                 cn: [
34258                     items
34259                 ]
34260             };
34261             
34262             if(this.labelWidth > 12){
34263                 label.style = "width: " + this.labelWidth + 'px';
34264             }
34265             
34266             if(this.labelWidth < 13 && this.labelmd == 0){
34267                 this.labelmd = this.labelWidth;
34268             }
34269             
34270             if(this.labellg > 0){
34271                 label.cls += ' col-lg-' + this.labellg;
34272                 items.cls += ' col-lg-' + (12 - this.labellg);
34273             }
34274             
34275             if(this.labelmd > 0){
34276                 label.cls += ' col-md-' + this.labelmd;
34277                 items.cls += ' col-md-' + (12 - this.labelmd);
34278             }
34279             
34280             if(this.labelsm > 0){
34281                 label.cls += ' col-sm-' + this.labelsm;
34282                 items.cls += ' col-sm-' + (12 - this.labelsm);
34283             }
34284             
34285             if(this.labelxs > 0){
34286                 label.cls += ' col-xs-' + this.labelxs;
34287                 items.cls += ' col-xs-' + (12 - this.labelxs);
34288             }
34289         }
34290         
34291         var cfg = {
34292             tag : 'div',
34293             cls : 'roo-radio-set',
34294             cn : [
34295                 {
34296                     tag : 'input',
34297                     cls : 'roo-radio-set-input',
34298                     type : 'hidden',
34299                     name : this.name,
34300                     value : this.value ? this.value :  ''
34301                 },
34302                 label,
34303                 items
34304             ]
34305         };
34306         
34307         if(this.weight.length){
34308             cfg.cls += ' roo-radio-' + this.weight;
34309         }
34310         
34311         if(this.inline) {
34312             cfg.cls += ' roo-radio-set-inline';
34313         }
34314         
34315         var settings=this;
34316         ['xs','sm','md','lg'].map(function(size){
34317             if (settings[size]) {
34318                 cfg.cls += ' col-' + size + '-' + settings[size];
34319             }
34320         });
34321         
34322         return cfg;
34323         
34324     },
34325
34326     initEvents : function()
34327     {
34328         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34329         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34330         
34331         if(!this.fieldLabel.length){
34332             this.labelEl.hide();
34333         }
34334         
34335         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34336         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34337         
34338         this.indicator = this.indicatorEl();
34339         
34340         if(this.indicator){
34341             this.indicator.addClass('invisible');
34342         }
34343         
34344         this.originalValue = this.getValue();
34345         
34346     },
34347     
34348     inputEl: function ()
34349     {
34350         return this.el.select('.roo-radio-set-input', true).first();
34351     },
34352     
34353     getChildContainer : function()
34354     {
34355         return this.itemsEl;
34356     },
34357     
34358     register : function(item)
34359     {
34360         this.radioes.push(item);
34361         
34362     },
34363     
34364     validate : function()
34365     {   
34366         if(this.getVisibilityEl().hasClass('hidden')){
34367             return true;
34368         }
34369         
34370         var valid = false;
34371         
34372         Roo.each(this.radioes, function(i){
34373             if(!i.checked){
34374                 return;
34375             }
34376             
34377             valid = true;
34378             return false;
34379         });
34380         
34381         if(this.allowBlank) {
34382             return true;
34383         }
34384         
34385         if(this.disabled || valid){
34386             this.markValid();
34387             return true;
34388         }
34389         
34390         this.markInvalid();
34391         return false;
34392         
34393     },
34394     
34395     markValid : function()
34396     {
34397         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34398             this.indicatorEl().removeClass('visible');
34399             this.indicatorEl().addClass('invisible');
34400         }
34401         
34402         
34403         if (Roo.bootstrap.version == 3) {
34404             this.el.removeClass([this.invalidClass, this.validClass]);
34405             this.el.addClass(this.validClass);
34406         } else {
34407             this.el.removeClass(['is-invalid','is-valid']);
34408             this.el.addClass(['is-valid']);
34409         }
34410         this.fireEvent('valid', this);
34411     },
34412     
34413     markInvalid : function(msg)
34414     {
34415         if(this.allowBlank || this.disabled){
34416             return;
34417         }
34418         
34419         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34420             this.indicatorEl().removeClass('invisible');
34421             this.indicatorEl().addClass('visible');
34422         }
34423         if (Roo.bootstrap.version == 3) {
34424             this.el.removeClass([this.invalidClass, this.validClass]);
34425             this.el.addClass(this.invalidClass);
34426         } else {
34427             this.el.removeClass(['is-invalid','is-valid']);
34428             this.el.addClass(['is-invalid']);
34429         }
34430         
34431         this.fireEvent('invalid', this, msg);
34432         
34433     },
34434     
34435     setValue : function(v, suppressEvent)
34436     {   
34437         if(this.value === v){
34438             return;
34439         }
34440         
34441         this.value = v;
34442         
34443         if(this.rendered){
34444             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34445         }
34446         
34447         Roo.each(this.radioes, function(i){
34448             i.checked = false;
34449             i.el.removeClass('checked');
34450         });
34451         
34452         Roo.each(this.radioes, function(i){
34453             
34454             if(i.value === v || i.value.toString() === v.toString()){
34455                 i.checked = true;
34456                 i.el.addClass('checked');
34457                 
34458                 if(suppressEvent !== true){
34459                     this.fireEvent('check', this, i);
34460                 }
34461                 
34462                 return false;
34463             }
34464             
34465         }, this);
34466         
34467         this.validate();
34468     },
34469     
34470     clearInvalid : function(){
34471         
34472         if(!this.el || this.preventMark){
34473             return;
34474         }
34475         
34476         this.el.removeClass([this.invalidClass]);
34477         
34478         this.fireEvent('valid', this);
34479     }
34480     
34481 });
34482
34483 Roo.apply(Roo.bootstrap.RadioSet, {
34484     
34485     groups: {},
34486     
34487     register : function(set)
34488     {
34489         this.groups[set.name] = set;
34490     },
34491     
34492     get: function(name) 
34493     {
34494         if (typeof(this.groups[name]) == 'undefined') {
34495             return false;
34496         }
34497         
34498         return this.groups[name] ;
34499     }
34500     
34501 });
34502 /*
34503  * Based on:
34504  * Ext JS Library 1.1.1
34505  * Copyright(c) 2006-2007, Ext JS, LLC.
34506  *
34507  * Originally Released Under LGPL - original licence link has changed is not relivant.
34508  *
34509  * Fork - LGPL
34510  * <script type="text/javascript">
34511  */
34512
34513
34514 /**
34515  * @class Roo.bootstrap.SplitBar
34516  * @extends Roo.util.Observable
34517  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34518  * <br><br>
34519  * Usage:
34520  * <pre><code>
34521 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34522                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34523 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34524 split.minSize = 100;
34525 split.maxSize = 600;
34526 split.animate = true;
34527 split.on('moved', splitterMoved);
34528 </code></pre>
34529  * @constructor
34530  * Create a new SplitBar
34531  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34532  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34533  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34534  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34535                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34536                         position of the SplitBar).
34537  */
34538 Roo.bootstrap.SplitBar = function(cfg){
34539     
34540     /** @private */
34541     
34542     //{
34543     //  dragElement : elm
34544     //  resizingElement: el,
34545         // optional..
34546     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34547     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34548         // existingProxy ???
34549     //}
34550     
34551     this.el = Roo.get(cfg.dragElement, true);
34552     this.el.dom.unselectable = "on";
34553     /** @private */
34554     this.resizingEl = Roo.get(cfg.resizingElement, true);
34555
34556     /**
34557      * @private
34558      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34559      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34560      * @type Number
34561      */
34562     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34563     
34564     /**
34565      * The minimum size of the resizing element. (Defaults to 0)
34566      * @type Number
34567      */
34568     this.minSize = 0;
34569     
34570     /**
34571      * The maximum size of the resizing element. (Defaults to 2000)
34572      * @type Number
34573      */
34574     this.maxSize = 2000;
34575     
34576     /**
34577      * Whether to animate the transition to the new size
34578      * @type Boolean
34579      */
34580     this.animate = false;
34581     
34582     /**
34583      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34584      * @type Boolean
34585      */
34586     this.useShim = false;
34587     
34588     /** @private */
34589     this.shim = null;
34590     
34591     if(!cfg.existingProxy){
34592         /** @private */
34593         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34594     }else{
34595         this.proxy = Roo.get(cfg.existingProxy).dom;
34596     }
34597     /** @private */
34598     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34599     
34600     /** @private */
34601     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34602     
34603     /** @private */
34604     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34605     
34606     /** @private */
34607     this.dragSpecs = {};
34608     
34609     /**
34610      * @private The adapter to use to positon and resize elements
34611      */
34612     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34613     this.adapter.init(this);
34614     
34615     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34616         /** @private */
34617         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34618         this.el.addClass("roo-splitbar-h");
34619     }else{
34620         /** @private */
34621         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34622         this.el.addClass("roo-splitbar-v");
34623     }
34624     
34625     this.addEvents({
34626         /**
34627          * @event resize
34628          * Fires when the splitter is moved (alias for {@link #event-moved})
34629          * @param {Roo.bootstrap.SplitBar} this
34630          * @param {Number} newSize the new width or height
34631          */
34632         "resize" : true,
34633         /**
34634          * @event moved
34635          * Fires when the splitter is moved
34636          * @param {Roo.bootstrap.SplitBar} this
34637          * @param {Number} newSize the new width or height
34638          */
34639         "moved" : true,
34640         /**
34641          * @event beforeresize
34642          * Fires before the splitter is dragged
34643          * @param {Roo.bootstrap.SplitBar} this
34644          */
34645         "beforeresize" : true,
34646
34647         "beforeapply" : true
34648     });
34649
34650     Roo.util.Observable.call(this);
34651 };
34652
34653 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34654     onStartProxyDrag : function(x, y){
34655         this.fireEvent("beforeresize", this);
34656         if(!this.overlay){
34657             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34658             o.unselectable();
34659             o.enableDisplayMode("block");
34660             // all splitbars share the same overlay
34661             Roo.bootstrap.SplitBar.prototype.overlay = o;
34662         }
34663         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34664         this.overlay.show();
34665         Roo.get(this.proxy).setDisplayed("block");
34666         var size = this.adapter.getElementSize(this);
34667         this.activeMinSize = this.getMinimumSize();;
34668         this.activeMaxSize = this.getMaximumSize();;
34669         var c1 = size - this.activeMinSize;
34670         var c2 = Math.max(this.activeMaxSize - size, 0);
34671         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34672             this.dd.resetConstraints();
34673             this.dd.setXConstraint(
34674                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34675                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34676             );
34677             this.dd.setYConstraint(0, 0);
34678         }else{
34679             this.dd.resetConstraints();
34680             this.dd.setXConstraint(0, 0);
34681             this.dd.setYConstraint(
34682                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34683                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34684             );
34685          }
34686         this.dragSpecs.startSize = size;
34687         this.dragSpecs.startPoint = [x, y];
34688         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34689     },
34690     
34691     /** 
34692      * @private Called after the drag operation by the DDProxy
34693      */
34694     onEndProxyDrag : function(e){
34695         Roo.get(this.proxy).setDisplayed(false);
34696         var endPoint = Roo.lib.Event.getXY(e);
34697         if(this.overlay){
34698             this.overlay.hide();
34699         }
34700         var newSize;
34701         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34702             newSize = this.dragSpecs.startSize + 
34703                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34704                     endPoint[0] - this.dragSpecs.startPoint[0] :
34705                     this.dragSpecs.startPoint[0] - endPoint[0]
34706                 );
34707         }else{
34708             newSize = this.dragSpecs.startSize + 
34709                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34710                     endPoint[1] - this.dragSpecs.startPoint[1] :
34711                     this.dragSpecs.startPoint[1] - endPoint[1]
34712                 );
34713         }
34714         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34715         if(newSize != this.dragSpecs.startSize){
34716             if(this.fireEvent('beforeapply', this, newSize) !== false){
34717                 this.adapter.setElementSize(this, newSize);
34718                 this.fireEvent("moved", this, newSize);
34719                 this.fireEvent("resize", this, newSize);
34720             }
34721         }
34722     },
34723     
34724     /**
34725      * Get the adapter this SplitBar uses
34726      * @return The adapter object
34727      */
34728     getAdapter : function(){
34729         return this.adapter;
34730     },
34731     
34732     /**
34733      * Set the adapter this SplitBar uses
34734      * @param {Object} adapter A SplitBar adapter object
34735      */
34736     setAdapter : function(adapter){
34737         this.adapter = adapter;
34738         this.adapter.init(this);
34739     },
34740     
34741     /**
34742      * Gets the minimum size for the resizing element
34743      * @return {Number} The minimum size
34744      */
34745     getMinimumSize : function(){
34746         return this.minSize;
34747     },
34748     
34749     /**
34750      * Sets the minimum size for the resizing element
34751      * @param {Number} minSize The minimum size
34752      */
34753     setMinimumSize : function(minSize){
34754         this.minSize = minSize;
34755     },
34756     
34757     /**
34758      * Gets the maximum size for the resizing element
34759      * @return {Number} The maximum size
34760      */
34761     getMaximumSize : function(){
34762         return this.maxSize;
34763     },
34764     
34765     /**
34766      * Sets the maximum size for the resizing element
34767      * @param {Number} maxSize The maximum size
34768      */
34769     setMaximumSize : function(maxSize){
34770         this.maxSize = maxSize;
34771     },
34772     
34773     /**
34774      * Sets the initialize size for the resizing element
34775      * @param {Number} size The initial size
34776      */
34777     setCurrentSize : function(size){
34778         var oldAnimate = this.animate;
34779         this.animate = false;
34780         this.adapter.setElementSize(this, size);
34781         this.animate = oldAnimate;
34782     },
34783     
34784     /**
34785      * Destroy this splitbar. 
34786      * @param {Boolean} removeEl True to remove the element
34787      */
34788     destroy : function(removeEl){
34789         if(this.shim){
34790             this.shim.remove();
34791         }
34792         this.dd.unreg();
34793         this.proxy.parentNode.removeChild(this.proxy);
34794         if(removeEl){
34795             this.el.remove();
34796         }
34797     }
34798 });
34799
34800 /**
34801  * @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.
34802  */
34803 Roo.bootstrap.SplitBar.createProxy = function(dir){
34804     var proxy = new Roo.Element(document.createElement("div"));
34805     proxy.unselectable();
34806     var cls = 'roo-splitbar-proxy';
34807     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34808     document.body.appendChild(proxy.dom);
34809     return proxy.dom;
34810 };
34811
34812 /** 
34813  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34814  * Default Adapter. It assumes the splitter and resizing element are not positioned
34815  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34816  */
34817 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34818 };
34819
34820 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34821     // do nothing for now
34822     init : function(s){
34823     
34824     },
34825     /**
34826      * Called before drag operations to get the current size of the resizing element. 
34827      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34828      */
34829      getElementSize : function(s){
34830         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34831             return s.resizingEl.getWidth();
34832         }else{
34833             return s.resizingEl.getHeight();
34834         }
34835     },
34836     
34837     /**
34838      * Called after drag operations to set the size of the resizing element.
34839      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34840      * @param {Number} newSize The new size to set
34841      * @param {Function} onComplete A function to be invoked when resizing is complete
34842      */
34843     setElementSize : function(s, newSize, onComplete){
34844         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34845             if(!s.animate){
34846                 s.resizingEl.setWidth(newSize);
34847                 if(onComplete){
34848                     onComplete(s, newSize);
34849                 }
34850             }else{
34851                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34852             }
34853         }else{
34854             
34855             if(!s.animate){
34856                 s.resizingEl.setHeight(newSize);
34857                 if(onComplete){
34858                     onComplete(s, newSize);
34859                 }
34860             }else{
34861                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34862             }
34863         }
34864     }
34865 };
34866
34867 /** 
34868  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34869  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34870  * Adapter that  moves the splitter element to align with the resized sizing element. 
34871  * Used with an absolute positioned SplitBar.
34872  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34873  * document.body, make sure you assign an id to the body element.
34874  */
34875 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34876     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34877     this.container = Roo.get(container);
34878 };
34879
34880 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34881     init : function(s){
34882         this.basic.init(s);
34883     },
34884     
34885     getElementSize : function(s){
34886         return this.basic.getElementSize(s);
34887     },
34888     
34889     setElementSize : function(s, newSize, onComplete){
34890         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34891     },
34892     
34893     moveSplitter : function(s){
34894         var yes = Roo.bootstrap.SplitBar;
34895         switch(s.placement){
34896             case yes.LEFT:
34897                 s.el.setX(s.resizingEl.getRight());
34898                 break;
34899             case yes.RIGHT:
34900                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34901                 break;
34902             case yes.TOP:
34903                 s.el.setY(s.resizingEl.getBottom());
34904                 break;
34905             case yes.BOTTOM:
34906                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34907                 break;
34908         }
34909     }
34910 };
34911
34912 /**
34913  * Orientation constant - Create a vertical SplitBar
34914  * @static
34915  * @type Number
34916  */
34917 Roo.bootstrap.SplitBar.VERTICAL = 1;
34918
34919 /**
34920  * Orientation constant - Create a horizontal SplitBar
34921  * @static
34922  * @type Number
34923  */
34924 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34925
34926 /**
34927  * Placement constant - The resizing element is to the left of the splitter element
34928  * @static
34929  * @type Number
34930  */
34931 Roo.bootstrap.SplitBar.LEFT = 1;
34932
34933 /**
34934  * Placement constant - The resizing element is to the right of the splitter element
34935  * @static
34936  * @type Number
34937  */
34938 Roo.bootstrap.SplitBar.RIGHT = 2;
34939
34940 /**
34941  * Placement constant - The resizing element is positioned above the splitter element
34942  * @static
34943  * @type Number
34944  */
34945 Roo.bootstrap.SplitBar.TOP = 3;
34946
34947 /**
34948  * Placement constant - The resizing element is positioned under splitter element
34949  * @static
34950  * @type Number
34951  */
34952 Roo.bootstrap.SplitBar.BOTTOM = 4;
34953 Roo.namespace("Roo.bootstrap.layout");/*
34954  * Based on:
34955  * Ext JS Library 1.1.1
34956  * Copyright(c) 2006-2007, Ext JS, LLC.
34957  *
34958  * Originally Released Under LGPL - original licence link has changed is not relivant.
34959  *
34960  * Fork - LGPL
34961  * <script type="text/javascript">
34962  */
34963
34964 /**
34965  * @class Roo.bootstrap.layout.Manager
34966  * @extends Roo.bootstrap.Component
34967  * Base class for layout managers.
34968  */
34969 Roo.bootstrap.layout.Manager = function(config)
34970 {
34971     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34972
34973
34974
34975
34976
34977     /** false to disable window resize monitoring @type Boolean */
34978     this.monitorWindowResize = true;
34979     this.regions = {};
34980     this.addEvents({
34981         /**
34982          * @event layout
34983          * Fires when a layout is performed.
34984          * @param {Roo.LayoutManager} this
34985          */
34986         "layout" : true,
34987         /**
34988          * @event regionresized
34989          * Fires when the user resizes a region.
34990          * @param {Roo.LayoutRegion} region The resized region
34991          * @param {Number} newSize The new size (width for east/west, height for north/south)
34992          */
34993         "regionresized" : true,
34994         /**
34995          * @event regioncollapsed
34996          * Fires when a region is collapsed.
34997          * @param {Roo.LayoutRegion} region The collapsed region
34998          */
34999         "regioncollapsed" : true,
35000         /**
35001          * @event regionexpanded
35002          * Fires when a region is expanded.
35003          * @param {Roo.LayoutRegion} region The expanded region
35004          */
35005         "regionexpanded" : true
35006     });
35007     this.updating = false;
35008
35009     if (config.el) {
35010         this.el = Roo.get(config.el);
35011         this.initEvents();
35012     }
35013
35014 };
35015
35016 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35017
35018
35019     regions : null,
35020
35021     monitorWindowResize : true,
35022
35023
35024     updating : false,
35025
35026
35027     onRender : function(ct, position)
35028     {
35029         if(!this.el){
35030             this.el = Roo.get(ct);
35031             this.initEvents();
35032         }
35033         //this.fireEvent('render',this);
35034     },
35035
35036
35037     initEvents: function()
35038     {
35039
35040
35041         // ie scrollbar fix
35042         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35043             document.body.scroll = "no";
35044         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35045             this.el.position('relative');
35046         }
35047         this.id = this.el.id;
35048         this.el.addClass("roo-layout-container");
35049         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35050         if(this.el.dom != document.body ) {
35051             this.el.on('resize', this.layout,this);
35052             this.el.on('show', this.layout,this);
35053         }
35054
35055     },
35056
35057     /**
35058      * Returns true if this layout is currently being updated
35059      * @return {Boolean}
35060      */
35061     isUpdating : function(){
35062         return this.updating;
35063     },
35064
35065     /**
35066      * Suspend the LayoutManager from doing auto-layouts while
35067      * making multiple add or remove calls
35068      */
35069     beginUpdate : function(){
35070         this.updating = true;
35071     },
35072
35073     /**
35074      * Restore auto-layouts and optionally disable the manager from performing a layout
35075      * @param {Boolean} noLayout true to disable a layout update
35076      */
35077     endUpdate : function(noLayout){
35078         this.updating = false;
35079         if(!noLayout){
35080             this.layout();
35081         }
35082     },
35083
35084     layout: function(){
35085         // abstract...
35086     },
35087
35088     onRegionResized : function(region, newSize){
35089         this.fireEvent("regionresized", region, newSize);
35090         this.layout();
35091     },
35092
35093     onRegionCollapsed : function(region){
35094         this.fireEvent("regioncollapsed", region);
35095     },
35096
35097     onRegionExpanded : function(region){
35098         this.fireEvent("regionexpanded", region);
35099     },
35100
35101     /**
35102      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35103      * performs box-model adjustments.
35104      * @return {Object} The size as an object {width: (the width), height: (the height)}
35105      */
35106     getViewSize : function()
35107     {
35108         var size;
35109         if(this.el.dom != document.body){
35110             size = this.el.getSize();
35111         }else{
35112             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35113         }
35114         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35115         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35116         return size;
35117     },
35118
35119     /**
35120      * Returns the Element this layout is bound to.
35121      * @return {Roo.Element}
35122      */
35123     getEl : function(){
35124         return this.el;
35125     },
35126
35127     /**
35128      * Returns the specified region.
35129      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35130      * @return {Roo.LayoutRegion}
35131      */
35132     getRegion : function(target){
35133         return this.regions[target.toLowerCase()];
35134     },
35135
35136     onWindowResize : function(){
35137         if(this.monitorWindowResize){
35138             this.layout();
35139         }
35140     }
35141 });
35142 /*
35143  * Based on:
35144  * Ext JS Library 1.1.1
35145  * Copyright(c) 2006-2007, Ext JS, LLC.
35146  *
35147  * Originally Released Under LGPL - original licence link has changed is not relivant.
35148  *
35149  * Fork - LGPL
35150  * <script type="text/javascript">
35151  */
35152 /**
35153  * @class Roo.bootstrap.layout.Border
35154  * @extends Roo.bootstrap.layout.Manager
35155  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35156  * please see: examples/bootstrap/nested.html<br><br>
35157  
35158 <b>The container the layout is rendered into can be either the body element or any other element.
35159 If it is not the body element, the container needs to either be an absolute positioned element,
35160 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35161 the container size if it is not the body element.</b>
35162
35163 * @constructor
35164 * Create a new Border
35165 * @param {Object} config Configuration options
35166  */
35167 Roo.bootstrap.layout.Border = function(config){
35168     config = config || {};
35169     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35170     
35171     
35172     
35173     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35174         if(config[region]){
35175             config[region].region = region;
35176             this.addRegion(config[region]);
35177         }
35178     },this);
35179     
35180 };
35181
35182 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35183
35184 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35185     
35186     parent : false, // this might point to a 'nest' or a ???
35187     
35188     /**
35189      * Creates and adds a new region if it doesn't already exist.
35190      * @param {String} target The target region key (north, south, east, west or center).
35191      * @param {Object} config The regions config object
35192      * @return {BorderLayoutRegion} The new region
35193      */
35194     addRegion : function(config)
35195     {
35196         if(!this.regions[config.region]){
35197             var r = this.factory(config);
35198             this.bindRegion(r);
35199         }
35200         return this.regions[config.region];
35201     },
35202
35203     // private (kinda)
35204     bindRegion : function(r){
35205         this.regions[r.config.region] = r;
35206         
35207         r.on("visibilitychange",    this.layout, this);
35208         r.on("paneladded",          this.layout, this);
35209         r.on("panelremoved",        this.layout, this);
35210         r.on("invalidated",         this.layout, this);
35211         r.on("resized",             this.onRegionResized, this);
35212         r.on("collapsed",           this.onRegionCollapsed, this);
35213         r.on("expanded",            this.onRegionExpanded, this);
35214     },
35215
35216     /**
35217      * Performs a layout update.
35218      */
35219     layout : function()
35220     {
35221         if(this.updating) {
35222             return;
35223         }
35224         
35225         // render all the rebions if they have not been done alreayd?
35226         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35227             if(this.regions[region] && !this.regions[region].bodyEl){
35228                 this.regions[region].onRender(this.el)
35229             }
35230         },this);
35231         
35232         var size = this.getViewSize();
35233         var w = size.width;
35234         var h = size.height;
35235         var centerW = w;
35236         var centerH = h;
35237         var centerY = 0;
35238         var centerX = 0;
35239         //var x = 0, y = 0;
35240
35241         var rs = this.regions;
35242         var north = rs["north"];
35243         var south = rs["south"]; 
35244         var west = rs["west"];
35245         var east = rs["east"];
35246         var center = rs["center"];
35247         //if(this.hideOnLayout){ // not supported anymore
35248             //c.el.setStyle("display", "none");
35249         //}
35250         if(north && north.isVisible()){
35251             var b = north.getBox();
35252             var m = north.getMargins();
35253             b.width = w - (m.left+m.right);
35254             b.x = m.left;
35255             b.y = m.top;
35256             centerY = b.height + b.y + m.bottom;
35257             centerH -= centerY;
35258             north.updateBox(this.safeBox(b));
35259         }
35260         if(south && south.isVisible()){
35261             var b = south.getBox();
35262             var m = south.getMargins();
35263             b.width = w - (m.left+m.right);
35264             b.x = m.left;
35265             var totalHeight = (b.height + m.top + m.bottom);
35266             b.y = h - totalHeight + m.top;
35267             centerH -= totalHeight;
35268             south.updateBox(this.safeBox(b));
35269         }
35270         if(west && west.isVisible()){
35271             var b = west.getBox();
35272             var m = west.getMargins();
35273             b.height = centerH - (m.top+m.bottom);
35274             b.x = m.left;
35275             b.y = centerY + m.top;
35276             var totalWidth = (b.width + m.left + m.right);
35277             centerX += totalWidth;
35278             centerW -= totalWidth;
35279             west.updateBox(this.safeBox(b));
35280         }
35281         if(east && east.isVisible()){
35282             var b = east.getBox();
35283             var m = east.getMargins();
35284             b.height = centerH - (m.top+m.bottom);
35285             var totalWidth = (b.width + m.left + m.right);
35286             b.x = w - totalWidth + m.left;
35287             b.y = centerY + m.top;
35288             centerW -= totalWidth;
35289             east.updateBox(this.safeBox(b));
35290         }
35291         if(center){
35292             var m = center.getMargins();
35293             var centerBox = {
35294                 x: centerX + m.left,
35295                 y: centerY + m.top,
35296                 width: centerW - (m.left+m.right),
35297                 height: centerH - (m.top+m.bottom)
35298             };
35299             //if(this.hideOnLayout){
35300                 //center.el.setStyle("display", "block");
35301             //}
35302             center.updateBox(this.safeBox(centerBox));
35303         }
35304         this.el.repaint();
35305         this.fireEvent("layout", this);
35306     },
35307
35308     // private
35309     safeBox : function(box){
35310         box.width = Math.max(0, box.width);
35311         box.height = Math.max(0, box.height);
35312         return box;
35313     },
35314
35315     /**
35316      * Adds a ContentPanel (or subclass) to this layout.
35317      * @param {String} target The target region key (north, south, east, west or center).
35318      * @param {Roo.ContentPanel} panel The panel to add
35319      * @return {Roo.ContentPanel} The added panel
35320      */
35321     add : function(target, panel){
35322          
35323         target = target.toLowerCase();
35324         return this.regions[target].add(panel);
35325     },
35326
35327     /**
35328      * Remove a ContentPanel (or subclass) to this layout.
35329      * @param {String} target The target region key (north, south, east, west or center).
35330      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35331      * @return {Roo.ContentPanel} The removed panel
35332      */
35333     remove : function(target, panel){
35334         target = target.toLowerCase();
35335         return this.regions[target].remove(panel);
35336     },
35337
35338     /**
35339      * Searches all regions for a panel with the specified id
35340      * @param {String} panelId
35341      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35342      */
35343     findPanel : function(panelId){
35344         var rs = this.regions;
35345         for(var target in rs){
35346             if(typeof rs[target] != "function"){
35347                 var p = rs[target].getPanel(panelId);
35348                 if(p){
35349                     return p;
35350                 }
35351             }
35352         }
35353         return null;
35354     },
35355
35356     /**
35357      * Searches all regions for a panel with the specified id and activates (shows) it.
35358      * @param {String/ContentPanel} panelId The panels id or the panel itself
35359      * @return {Roo.ContentPanel} The shown panel or null
35360      */
35361     showPanel : function(panelId) {
35362       var rs = this.regions;
35363       for(var target in rs){
35364          var r = rs[target];
35365          if(typeof r != "function"){
35366             if(r.hasPanel(panelId)){
35367                return r.showPanel(panelId);
35368             }
35369          }
35370       }
35371       return null;
35372    },
35373
35374    /**
35375      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35376      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35377      */
35378    /*
35379     restoreState : function(provider){
35380         if(!provider){
35381             provider = Roo.state.Manager;
35382         }
35383         var sm = new Roo.LayoutStateManager();
35384         sm.init(this, provider);
35385     },
35386 */
35387  
35388  
35389     /**
35390      * Adds a xtype elements to the layout.
35391      * <pre><code>
35392
35393 layout.addxtype({
35394        xtype : 'ContentPanel',
35395        region: 'west',
35396        items: [ .... ]
35397    }
35398 );
35399
35400 layout.addxtype({
35401         xtype : 'NestedLayoutPanel',
35402         region: 'west',
35403         layout: {
35404            center: { },
35405            west: { }   
35406         },
35407         items : [ ... list of content panels or nested layout panels.. ]
35408    }
35409 );
35410 </code></pre>
35411      * @param {Object} cfg Xtype definition of item to add.
35412      */
35413     addxtype : function(cfg)
35414     {
35415         // basically accepts a pannel...
35416         // can accept a layout region..!?!?
35417         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35418         
35419         
35420         // theory?  children can only be panels??
35421         
35422         //if (!cfg.xtype.match(/Panel$/)) {
35423         //    return false;
35424         //}
35425         var ret = false;
35426         
35427         if (typeof(cfg.region) == 'undefined') {
35428             Roo.log("Failed to add Panel, region was not set");
35429             Roo.log(cfg);
35430             return false;
35431         }
35432         var region = cfg.region;
35433         delete cfg.region;
35434         
35435           
35436         var xitems = [];
35437         if (cfg.items) {
35438             xitems = cfg.items;
35439             delete cfg.items;
35440         }
35441         var nb = false;
35442         
35443         if ( region == 'center') {
35444             Roo.log("Center: " + cfg.title);
35445         }
35446         
35447         
35448         switch(cfg.xtype) 
35449         {
35450             case 'Content':  // ContentPanel (el, cfg)
35451             case 'Scroll':  // ContentPanel (el, cfg)
35452             case 'View': 
35453                 cfg.autoCreate = cfg.autoCreate || true;
35454                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35455                 //} else {
35456                 //    var el = this.el.createChild();
35457                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35458                 //}
35459                 
35460                 this.add(region, ret);
35461                 break;
35462             
35463             /*
35464             case 'TreePanel': // our new panel!
35465                 cfg.el = this.el.createChild();
35466                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35467                 this.add(region, ret);
35468                 break;
35469             */
35470             
35471             case 'Nest': 
35472                 // create a new Layout (which is  a Border Layout...
35473                 
35474                 var clayout = cfg.layout;
35475                 clayout.el  = this.el.createChild();
35476                 clayout.items   = clayout.items  || [];
35477                 
35478                 delete cfg.layout;
35479                 
35480                 // replace this exitems with the clayout ones..
35481                 xitems = clayout.items;
35482                  
35483                 // force background off if it's in center...
35484                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35485                     cfg.background = false;
35486                 }
35487                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35488                 
35489                 
35490                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35491                 //console.log('adding nested layout panel '  + cfg.toSource());
35492                 this.add(region, ret);
35493                 nb = {}; /// find first...
35494                 break;
35495             
35496             case 'Grid':
35497                 
35498                 // needs grid and region
35499                 
35500                 //var el = this.getRegion(region).el.createChild();
35501                 /*
35502                  *var el = this.el.createChild();
35503                 // create the grid first...
35504                 cfg.grid.container = el;
35505                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35506                 */
35507                 
35508                 if (region == 'center' && this.active ) {
35509                     cfg.background = false;
35510                 }
35511                 
35512                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35513                 
35514                 this.add(region, ret);
35515                 /*
35516                 if (cfg.background) {
35517                     // render grid on panel activation (if panel background)
35518                     ret.on('activate', function(gp) {
35519                         if (!gp.grid.rendered) {
35520                     //        gp.grid.render(el);
35521                         }
35522                     });
35523                 } else {
35524                   //  cfg.grid.render(el);
35525                 }
35526                 */
35527                 break;
35528            
35529            
35530             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35531                 // it was the old xcomponent building that caused this before.
35532                 // espeically if border is the top element in the tree.
35533                 ret = this;
35534                 break; 
35535                 
35536                     
35537                 
35538                 
35539                 
35540             default:
35541                 /*
35542                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35543                     
35544                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35545                     this.add(region, ret);
35546                 } else {
35547                 */
35548                     Roo.log(cfg);
35549                     throw "Can not add '" + cfg.xtype + "' to Border";
35550                     return null;
35551              
35552                                 
35553              
35554         }
35555         this.beginUpdate();
35556         // add children..
35557         var region = '';
35558         var abn = {};
35559         Roo.each(xitems, function(i)  {
35560             region = nb && i.region ? i.region : false;
35561             
35562             var add = ret.addxtype(i);
35563            
35564             if (region) {
35565                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35566                 if (!i.background) {
35567                     abn[region] = nb[region] ;
35568                 }
35569             }
35570             
35571         });
35572         this.endUpdate();
35573
35574         // make the last non-background panel active..
35575         //if (nb) { Roo.log(abn); }
35576         if (nb) {
35577             
35578             for(var r in abn) {
35579                 region = this.getRegion(r);
35580                 if (region) {
35581                     // tried using nb[r], but it does not work..
35582                      
35583                     region.showPanel(abn[r]);
35584                    
35585                 }
35586             }
35587         }
35588         return ret;
35589         
35590     },
35591     
35592     
35593 // private
35594     factory : function(cfg)
35595     {
35596         
35597         var validRegions = Roo.bootstrap.layout.Border.regions;
35598
35599         var target = cfg.region;
35600         cfg.mgr = this;
35601         
35602         var r = Roo.bootstrap.layout;
35603         Roo.log(target);
35604         switch(target){
35605             case "north":
35606                 return new r.North(cfg);
35607             case "south":
35608                 return new r.South(cfg);
35609             case "east":
35610                 return new r.East(cfg);
35611             case "west":
35612                 return new r.West(cfg);
35613             case "center":
35614                 return new r.Center(cfg);
35615         }
35616         throw 'Layout region "'+target+'" not supported.';
35617     }
35618     
35619     
35620 });
35621  /*
35622  * Based on:
35623  * Ext JS Library 1.1.1
35624  * Copyright(c) 2006-2007, Ext JS, LLC.
35625  *
35626  * Originally Released Under LGPL - original licence link has changed is not relivant.
35627  *
35628  * Fork - LGPL
35629  * <script type="text/javascript">
35630  */
35631  
35632 /**
35633  * @class Roo.bootstrap.layout.Basic
35634  * @extends Roo.util.Observable
35635  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35636  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35637  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35638  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35639  * @cfg {string}   region  the region that it inhabits..
35640  * @cfg {bool}   skipConfig skip config?
35641  * 
35642
35643  */
35644 Roo.bootstrap.layout.Basic = function(config){
35645     
35646     this.mgr = config.mgr;
35647     
35648     this.position = config.region;
35649     
35650     var skipConfig = config.skipConfig;
35651     
35652     this.events = {
35653         /**
35654          * @scope Roo.BasicLayoutRegion
35655          */
35656         
35657         /**
35658          * @event beforeremove
35659          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35660          * @param {Roo.LayoutRegion} this
35661          * @param {Roo.ContentPanel} panel The panel
35662          * @param {Object} e The cancel event object
35663          */
35664         "beforeremove" : true,
35665         /**
35666          * @event invalidated
35667          * Fires when the layout for this region is changed.
35668          * @param {Roo.LayoutRegion} this
35669          */
35670         "invalidated" : true,
35671         /**
35672          * @event visibilitychange
35673          * Fires when this region is shown or hidden 
35674          * @param {Roo.LayoutRegion} this
35675          * @param {Boolean} visibility true or false
35676          */
35677         "visibilitychange" : true,
35678         /**
35679          * @event paneladded
35680          * Fires when a panel is added. 
35681          * @param {Roo.LayoutRegion} this
35682          * @param {Roo.ContentPanel} panel The panel
35683          */
35684         "paneladded" : true,
35685         /**
35686          * @event panelremoved
35687          * Fires when a panel is removed. 
35688          * @param {Roo.LayoutRegion} this
35689          * @param {Roo.ContentPanel} panel The panel
35690          */
35691         "panelremoved" : true,
35692         /**
35693          * @event beforecollapse
35694          * Fires when this region before collapse.
35695          * @param {Roo.LayoutRegion} this
35696          */
35697         "beforecollapse" : true,
35698         /**
35699          * @event collapsed
35700          * Fires when this region is collapsed.
35701          * @param {Roo.LayoutRegion} this
35702          */
35703         "collapsed" : true,
35704         /**
35705          * @event expanded
35706          * Fires when this region is expanded.
35707          * @param {Roo.LayoutRegion} this
35708          */
35709         "expanded" : true,
35710         /**
35711          * @event slideshow
35712          * Fires when this region is slid into view.
35713          * @param {Roo.LayoutRegion} this
35714          */
35715         "slideshow" : true,
35716         /**
35717          * @event slidehide
35718          * Fires when this region slides out of view. 
35719          * @param {Roo.LayoutRegion} this
35720          */
35721         "slidehide" : true,
35722         /**
35723          * @event panelactivated
35724          * Fires when a panel is activated. 
35725          * @param {Roo.LayoutRegion} this
35726          * @param {Roo.ContentPanel} panel The activated panel
35727          */
35728         "panelactivated" : true,
35729         /**
35730          * @event resized
35731          * Fires when the user resizes this region. 
35732          * @param {Roo.LayoutRegion} this
35733          * @param {Number} newSize The new size (width for east/west, height for north/south)
35734          */
35735         "resized" : true
35736     };
35737     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35738     this.panels = new Roo.util.MixedCollection();
35739     this.panels.getKey = this.getPanelId.createDelegate(this);
35740     this.box = null;
35741     this.activePanel = null;
35742     // ensure listeners are added...
35743     
35744     if (config.listeners || config.events) {
35745         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35746             listeners : config.listeners || {},
35747             events : config.events || {}
35748         });
35749     }
35750     
35751     if(skipConfig !== true){
35752         this.applyConfig(config);
35753     }
35754 };
35755
35756 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35757 {
35758     getPanelId : function(p){
35759         return p.getId();
35760     },
35761     
35762     applyConfig : function(config){
35763         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35764         this.config = config;
35765         
35766     },
35767     
35768     /**
35769      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35770      * the width, for horizontal (north, south) the height.
35771      * @param {Number} newSize The new width or height
35772      */
35773     resizeTo : function(newSize){
35774         var el = this.el ? this.el :
35775                  (this.activePanel ? this.activePanel.getEl() : null);
35776         if(el){
35777             switch(this.position){
35778                 case "east":
35779                 case "west":
35780                     el.setWidth(newSize);
35781                     this.fireEvent("resized", this, newSize);
35782                 break;
35783                 case "north":
35784                 case "south":
35785                     el.setHeight(newSize);
35786                     this.fireEvent("resized", this, newSize);
35787                 break;                
35788             }
35789         }
35790     },
35791     
35792     getBox : function(){
35793         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35794     },
35795     
35796     getMargins : function(){
35797         return this.margins;
35798     },
35799     
35800     updateBox : function(box){
35801         this.box = box;
35802         var el = this.activePanel.getEl();
35803         el.dom.style.left = box.x + "px";
35804         el.dom.style.top = box.y + "px";
35805         this.activePanel.setSize(box.width, box.height);
35806     },
35807     
35808     /**
35809      * Returns the container element for this region.
35810      * @return {Roo.Element}
35811      */
35812     getEl : function(){
35813         return this.activePanel;
35814     },
35815     
35816     /**
35817      * Returns true if this region is currently visible.
35818      * @return {Boolean}
35819      */
35820     isVisible : function(){
35821         return this.activePanel ? true : false;
35822     },
35823     
35824     setActivePanel : function(panel){
35825         panel = this.getPanel(panel);
35826         if(this.activePanel && this.activePanel != panel){
35827             this.activePanel.setActiveState(false);
35828             this.activePanel.getEl().setLeftTop(-10000,-10000);
35829         }
35830         this.activePanel = panel;
35831         panel.setActiveState(true);
35832         if(this.box){
35833             panel.setSize(this.box.width, this.box.height);
35834         }
35835         this.fireEvent("panelactivated", this, panel);
35836         this.fireEvent("invalidated");
35837     },
35838     
35839     /**
35840      * Show the specified panel.
35841      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35842      * @return {Roo.ContentPanel} The shown panel or null
35843      */
35844     showPanel : function(panel){
35845         panel = this.getPanel(panel);
35846         if(panel){
35847             this.setActivePanel(panel);
35848         }
35849         return panel;
35850     },
35851     
35852     /**
35853      * Get the active panel for this region.
35854      * @return {Roo.ContentPanel} The active panel or null
35855      */
35856     getActivePanel : function(){
35857         return this.activePanel;
35858     },
35859     
35860     /**
35861      * Add the passed ContentPanel(s)
35862      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35863      * @return {Roo.ContentPanel} The panel added (if only one was added)
35864      */
35865     add : function(panel){
35866         if(arguments.length > 1){
35867             for(var i = 0, len = arguments.length; i < len; i++) {
35868                 this.add(arguments[i]);
35869             }
35870             return null;
35871         }
35872         if(this.hasPanel(panel)){
35873             this.showPanel(panel);
35874             return panel;
35875         }
35876         var el = panel.getEl();
35877         if(el.dom.parentNode != this.mgr.el.dom){
35878             this.mgr.el.dom.appendChild(el.dom);
35879         }
35880         if(panel.setRegion){
35881             panel.setRegion(this);
35882         }
35883         this.panels.add(panel);
35884         el.setStyle("position", "absolute");
35885         if(!panel.background){
35886             this.setActivePanel(panel);
35887             if(this.config.initialSize && this.panels.getCount()==1){
35888                 this.resizeTo(this.config.initialSize);
35889             }
35890         }
35891         this.fireEvent("paneladded", this, panel);
35892         return panel;
35893     },
35894     
35895     /**
35896      * Returns true if the panel is in this region.
35897      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35898      * @return {Boolean}
35899      */
35900     hasPanel : function(panel){
35901         if(typeof panel == "object"){ // must be panel obj
35902             panel = panel.getId();
35903         }
35904         return this.getPanel(panel) ? true : false;
35905     },
35906     
35907     /**
35908      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35909      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35910      * @param {Boolean} preservePanel Overrides the config preservePanel option
35911      * @return {Roo.ContentPanel} The panel that was removed
35912      */
35913     remove : function(panel, preservePanel){
35914         panel = this.getPanel(panel);
35915         if(!panel){
35916             return null;
35917         }
35918         var e = {};
35919         this.fireEvent("beforeremove", this, panel, e);
35920         if(e.cancel === true){
35921             return null;
35922         }
35923         var panelId = panel.getId();
35924         this.panels.removeKey(panelId);
35925         return panel;
35926     },
35927     
35928     /**
35929      * Returns the panel specified or null if it's not in this region.
35930      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35931      * @return {Roo.ContentPanel}
35932      */
35933     getPanel : function(id){
35934         if(typeof id == "object"){ // must be panel obj
35935             return id;
35936         }
35937         return this.panels.get(id);
35938     },
35939     
35940     /**
35941      * Returns this regions position (north/south/east/west/center).
35942      * @return {String} 
35943      */
35944     getPosition: function(){
35945         return this.position;    
35946     }
35947 });/*
35948  * Based on:
35949  * Ext JS Library 1.1.1
35950  * Copyright(c) 2006-2007, Ext JS, LLC.
35951  *
35952  * Originally Released Under LGPL - original licence link has changed is not relivant.
35953  *
35954  * Fork - LGPL
35955  * <script type="text/javascript">
35956  */
35957  
35958 /**
35959  * @class Roo.bootstrap.layout.Region
35960  * @extends Roo.bootstrap.layout.Basic
35961  * This class represents a region in a layout manager.
35962  
35963  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35964  * @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})
35965  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35966  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35967  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35968  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35969  * @cfg {String}    title           The title for the region (overrides panel titles)
35970  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35971  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35972  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35973  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35974  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35975  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35976  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35977  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35978  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35979  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35980
35981  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35982  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35983  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35984  * @cfg {Number}    width           For East/West panels
35985  * @cfg {Number}    height          For North/South panels
35986  * @cfg {Boolean}   split           To show the splitter
35987  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35988  * 
35989  * @cfg {string}   cls             Extra CSS classes to add to region
35990  * 
35991  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35992  * @cfg {string}   region  the region that it inhabits..
35993  *
35994
35995  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35996  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35997
35998  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35999  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
36000  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
36001  */
36002 Roo.bootstrap.layout.Region = function(config)
36003 {
36004     this.applyConfig(config);
36005
36006     var mgr = config.mgr;
36007     var pos = config.region;
36008     config.skipConfig = true;
36009     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36010     
36011     if (mgr.el) {
36012         this.onRender(mgr.el);   
36013     }
36014      
36015     this.visible = true;
36016     this.collapsed = false;
36017     this.unrendered_panels = [];
36018 };
36019
36020 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36021
36022     position: '', // set by wrapper (eg. north/south etc..)
36023     unrendered_panels : null,  // unrendered panels.
36024     
36025     tabPosition : false,
36026     
36027     mgr: false, // points to 'Border'
36028     
36029     
36030     createBody : function(){
36031         /** This region's body element 
36032         * @type Roo.Element */
36033         this.bodyEl = this.el.createChild({
36034                 tag: "div",
36035                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36036         });
36037     },
36038
36039     onRender: function(ctr, pos)
36040     {
36041         var dh = Roo.DomHelper;
36042         /** This region's container element 
36043         * @type Roo.Element */
36044         this.el = dh.append(ctr.dom, {
36045                 tag: "div",
36046                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36047             }, true);
36048         /** This region's title element 
36049         * @type Roo.Element */
36050     
36051         this.titleEl = dh.append(this.el.dom,  {
36052                 tag: "div",
36053                 unselectable: "on",
36054                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36055                 children:[
36056                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36057                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36058                 ]
36059             }, true);
36060         
36061         this.titleEl.enableDisplayMode();
36062         /** This region's title text element 
36063         * @type HTMLElement */
36064         this.titleTextEl = this.titleEl.dom.firstChild;
36065         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36066         /*
36067         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36068         this.closeBtn.enableDisplayMode();
36069         this.closeBtn.on("click", this.closeClicked, this);
36070         this.closeBtn.hide();
36071     */
36072         this.createBody(this.config);
36073         if(this.config.hideWhenEmpty){
36074             this.hide();
36075             this.on("paneladded", this.validateVisibility, this);
36076             this.on("panelremoved", this.validateVisibility, this);
36077         }
36078         if(this.autoScroll){
36079             this.bodyEl.setStyle("overflow", "auto");
36080         }else{
36081             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36082         }
36083         //if(c.titlebar !== false){
36084             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36085                 this.titleEl.hide();
36086             }else{
36087                 this.titleEl.show();
36088                 if(this.config.title){
36089                     this.titleTextEl.innerHTML = this.config.title;
36090                 }
36091             }
36092         //}
36093         if(this.config.collapsed){
36094             this.collapse(true);
36095         }
36096         if(this.config.hidden){
36097             this.hide();
36098         }
36099         
36100         if (this.unrendered_panels && this.unrendered_panels.length) {
36101             for (var i =0;i< this.unrendered_panels.length; i++) {
36102                 this.add(this.unrendered_panels[i]);
36103             }
36104             this.unrendered_panels = null;
36105             
36106         }
36107         
36108     },
36109     
36110     applyConfig : function(c)
36111     {
36112         /*
36113          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36114             var dh = Roo.DomHelper;
36115             if(c.titlebar !== false){
36116                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36117                 this.collapseBtn.on("click", this.collapse, this);
36118                 this.collapseBtn.enableDisplayMode();
36119                 /*
36120                 if(c.showPin === true || this.showPin){
36121                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36122                     this.stickBtn.enableDisplayMode();
36123                     this.stickBtn.on("click", this.expand, this);
36124                     this.stickBtn.hide();
36125                 }
36126                 
36127             }
36128             */
36129             /** This region's collapsed element
36130             * @type Roo.Element */
36131             /*
36132              *
36133             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36134                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36135             ]}, true);
36136             
36137             if(c.floatable !== false){
36138                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36139                this.collapsedEl.on("click", this.collapseClick, this);
36140             }
36141
36142             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36143                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36144                    id: "message", unselectable: "on", style:{"float":"left"}});
36145                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36146              }
36147             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36148             this.expandBtn.on("click", this.expand, this);
36149             
36150         }
36151         
36152         if(this.collapseBtn){
36153             this.collapseBtn.setVisible(c.collapsible == true);
36154         }
36155         
36156         this.cmargins = c.cmargins || this.cmargins ||
36157                          (this.position == "west" || this.position == "east" ?
36158                              {top: 0, left: 2, right:2, bottom: 0} :
36159                              {top: 2, left: 0, right:0, bottom: 2});
36160         */
36161         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36162         
36163         
36164         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36165         
36166         this.autoScroll = c.autoScroll || false;
36167         
36168         
36169        
36170         
36171         this.duration = c.duration || .30;
36172         this.slideDuration = c.slideDuration || .45;
36173         this.config = c;
36174        
36175     },
36176     /**
36177      * Returns true if this region is currently visible.
36178      * @return {Boolean}
36179      */
36180     isVisible : function(){
36181         return this.visible;
36182     },
36183
36184     /**
36185      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36186      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36187      */
36188     //setCollapsedTitle : function(title){
36189     //    title = title || "&#160;";
36190      //   if(this.collapsedTitleTextEl){
36191       //      this.collapsedTitleTextEl.innerHTML = title;
36192        // }
36193     //},
36194
36195     getBox : function(){
36196         var b;
36197       //  if(!this.collapsed){
36198             b = this.el.getBox(false, true);
36199        // }else{
36200           //  b = this.collapsedEl.getBox(false, true);
36201         //}
36202         return b;
36203     },
36204
36205     getMargins : function(){
36206         return this.margins;
36207         //return this.collapsed ? this.cmargins : this.margins;
36208     },
36209 /*
36210     highlight : function(){
36211         this.el.addClass("x-layout-panel-dragover");
36212     },
36213
36214     unhighlight : function(){
36215         this.el.removeClass("x-layout-panel-dragover");
36216     },
36217 */
36218     updateBox : function(box)
36219     {
36220         if (!this.bodyEl) {
36221             return; // not rendered yet..
36222         }
36223         
36224         this.box = box;
36225         if(!this.collapsed){
36226             this.el.dom.style.left = box.x + "px";
36227             this.el.dom.style.top = box.y + "px";
36228             this.updateBody(box.width, box.height);
36229         }else{
36230             this.collapsedEl.dom.style.left = box.x + "px";
36231             this.collapsedEl.dom.style.top = box.y + "px";
36232             this.collapsedEl.setSize(box.width, box.height);
36233         }
36234         if(this.tabs){
36235             this.tabs.autoSizeTabs();
36236         }
36237     },
36238
36239     updateBody : function(w, h)
36240     {
36241         if(w !== null){
36242             this.el.setWidth(w);
36243             w -= this.el.getBorderWidth("rl");
36244             if(this.config.adjustments){
36245                 w += this.config.adjustments[0];
36246             }
36247         }
36248         if(h !== null && h > 0){
36249             this.el.setHeight(h);
36250             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36251             h -= this.el.getBorderWidth("tb");
36252             if(this.config.adjustments){
36253                 h += this.config.adjustments[1];
36254             }
36255             this.bodyEl.setHeight(h);
36256             if(this.tabs){
36257                 h = this.tabs.syncHeight(h);
36258             }
36259         }
36260         if(this.panelSize){
36261             w = w !== null ? w : this.panelSize.width;
36262             h = h !== null ? h : this.panelSize.height;
36263         }
36264         if(this.activePanel){
36265             var el = this.activePanel.getEl();
36266             w = w !== null ? w : el.getWidth();
36267             h = h !== null ? h : el.getHeight();
36268             this.panelSize = {width: w, height: h};
36269             this.activePanel.setSize(w, h);
36270         }
36271         if(Roo.isIE && this.tabs){
36272             this.tabs.el.repaint();
36273         }
36274     },
36275
36276     /**
36277      * Returns the container element for this region.
36278      * @return {Roo.Element}
36279      */
36280     getEl : function(){
36281         return this.el;
36282     },
36283
36284     /**
36285      * Hides this region.
36286      */
36287     hide : function(){
36288         //if(!this.collapsed){
36289             this.el.dom.style.left = "-2000px";
36290             this.el.hide();
36291         //}else{
36292          //   this.collapsedEl.dom.style.left = "-2000px";
36293          //   this.collapsedEl.hide();
36294        // }
36295         this.visible = false;
36296         this.fireEvent("visibilitychange", this, false);
36297     },
36298
36299     /**
36300      * Shows this region if it was previously hidden.
36301      */
36302     show : function(){
36303         //if(!this.collapsed){
36304             this.el.show();
36305         //}else{
36306         //    this.collapsedEl.show();
36307        // }
36308         this.visible = true;
36309         this.fireEvent("visibilitychange", this, true);
36310     },
36311 /*
36312     closeClicked : function(){
36313         if(this.activePanel){
36314             this.remove(this.activePanel);
36315         }
36316     },
36317
36318     collapseClick : function(e){
36319         if(this.isSlid){
36320            e.stopPropagation();
36321            this.slideIn();
36322         }else{
36323            e.stopPropagation();
36324            this.slideOut();
36325         }
36326     },
36327 */
36328     /**
36329      * Collapses this region.
36330      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36331      */
36332     /*
36333     collapse : function(skipAnim, skipCheck = false){
36334         if(this.collapsed) {
36335             return;
36336         }
36337         
36338         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36339             
36340             this.collapsed = true;
36341             if(this.split){
36342                 this.split.el.hide();
36343             }
36344             if(this.config.animate && skipAnim !== true){
36345                 this.fireEvent("invalidated", this);
36346                 this.animateCollapse();
36347             }else{
36348                 this.el.setLocation(-20000,-20000);
36349                 this.el.hide();
36350                 this.collapsedEl.show();
36351                 this.fireEvent("collapsed", this);
36352                 this.fireEvent("invalidated", this);
36353             }
36354         }
36355         
36356     },
36357 */
36358     animateCollapse : function(){
36359         // overridden
36360     },
36361
36362     /**
36363      * Expands this region if it was previously collapsed.
36364      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36365      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36366      */
36367     /*
36368     expand : function(e, skipAnim){
36369         if(e) {
36370             e.stopPropagation();
36371         }
36372         if(!this.collapsed || this.el.hasActiveFx()) {
36373             return;
36374         }
36375         if(this.isSlid){
36376             this.afterSlideIn();
36377             skipAnim = true;
36378         }
36379         this.collapsed = false;
36380         if(this.config.animate && skipAnim !== true){
36381             this.animateExpand();
36382         }else{
36383             this.el.show();
36384             if(this.split){
36385                 this.split.el.show();
36386             }
36387             this.collapsedEl.setLocation(-2000,-2000);
36388             this.collapsedEl.hide();
36389             this.fireEvent("invalidated", this);
36390             this.fireEvent("expanded", this);
36391         }
36392     },
36393 */
36394     animateExpand : function(){
36395         // overridden
36396     },
36397
36398     initTabs : function()
36399     {
36400         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36401         
36402         var ts = new Roo.bootstrap.panel.Tabs({
36403             el: this.bodyEl.dom,
36404             region : this,
36405             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36406             disableTooltips: this.config.disableTabTips,
36407             toolbar : this.config.toolbar
36408         });
36409         
36410         if(this.config.hideTabs){
36411             ts.stripWrap.setDisplayed(false);
36412         }
36413         this.tabs = ts;
36414         ts.resizeTabs = this.config.resizeTabs === true;
36415         ts.minTabWidth = this.config.minTabWidth || 40;
36416         ts.maxTabWidth = this.config.maxTabWidth || 250;
36417         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36418         ts.monitorResize = false;
36419         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36420         ts.bodyEl.addClass('roo-layout-tabs-body');
36421         this.panels.each(this.initPanelAsTab, this);
36422     },
36423
36424     initPanelAsTab : function(panel){
36425         var ti = this.tabs.addTab(
36426             panel.getEl().id,
36427             panel.getTitle(),
36428             null,
36429             this.config.closeOnTab && panel.isClosable(),
36430             panel.tpl
36431         );
36432         if(panel.tabTip !== undefined){
36433             ti.setTooltip(panel.tabTip);
36434         }
36435         ti.on("activate", function(){
36436               this.setActivePanel(panel);
36437         }, this);
36438         
36439         if(this.config.closeOnTab){
36440             ti.on("beforeclose", function(t, e){
36441                 e.cancel = true;
36442                 this.remove(panel);
36443             }, this);
36444         }
36445         
36446         panel.tabItem = ti;
36447         
36448         return ti;
36449     },
36450
36451     updatePanelTitle : function(panel, title)
36452     {
36453         if(this.activePanel == panel){
36454             this.updateTitle(title);
36455         }
36456         if(this.tabs){
36457             var ti = this.tabs.getTab(panel.getEl().id);
36458             ti.setText(title);
36459             if(panel.tabTip !== undefined){
36460                 ti.setTooltip(panel.tabTip);
36461             }
36462         }
36463     },
36464
36465     updateTitle : function(title){
36466         if(this.titleTextEl && !this.config.title){
36467             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36468         }
36469     },
36470
36471     setActivePanel : function(panel)
36472     {
36473         panel = this.getPanel(panel);
36474         if(this.activePanel && this.activePanel != panel){
36475             if(this.activePanel.setActiveState(false) === false){
36476                 return;
36477             }
36478         }
36479         this.activePanel = panel;
36480         panel.setActiveState(true);
36481         if(this.panelSize){
36482             panel.setSize(this.panelSize.width, this.panelSize.height);
36483         }
36484         if(this.closeBtn){
36485             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36486         }
36487         this.updateTitle(panel.getTitle());
36488         if(this.tabs){
36489             this.fireEvent("invalidated", this);
36490         }
36491         this.fireEvent("panelactivated", this, panel);
36492     },
36493
36494     /**
36495      * Shows the specified panel.
36496      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36497      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36498      */
36499     showPanel : function(panel)
36500     {
36501         panel = this.getPanel(panel);
36502         if(panel){
36503             if(this.tabs){
36504                 var tab = this.tabs.getTab(panel.getEl().id);
36505                 if(tab.isHidden()){
36506                     this.tabs.unhideTab(tab.id);
36507                 }
36508                 tab.activate();
36509             }else{
36510                 this.setActivePanel(panel);
36511             }
36512         }
36513         return panel;
36514     },
36515
36516     /**
36517      * Get the active panel for this region.
36518      * @return {Roo.ContentPanel} The active panel or null
36519      */
36520     getActivePanel : function(){
36521         return this.activePanel;
36522     },
36523
36524     validateVisibility : function(){
36525         if(this.panels.getCount() < 1){
36526             this.updateTitle("&#160;");
36527             this.closeBtn.hide();
36528             this.hide();
36529         }else{
36530             if(!this.isVisible()){
36531                 this.show();
36532             }
36533         }
36534     },
36535
36536     /**
36537      * Adds the passed ContentPanel(s) to this region.
36538      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36539      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36540      */
36541     add : function(panel)
36542     {
36543         if(arguments.length > 1){
36544             for(var i = 0, len = arguments.length; i < len; i++) {
36545                 this.add(arguments[i]);
36546             }
36547             return null;
36548         }
36549         
36550         // if we have not been rendered yet, then we can not really do much of this..
36551         if (!this.bodyEl) {
36552             this.unrendered_panels.push(panel);
36553             return panel;
36554         }
36555         
36556         
36557         
36558         
36559         if(this.hasPanel(panel)){
36560             this.showPanel(panel);
36561             return panel;
36562         }
36563         panel.setRegion(this);
36564         this.panels.add(panel);
36565        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36566             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36567             // and hide them... ???
36568             this.bodyEl.dom.appendChild(panel.getEl().dom);
36569             if(panel.background !== true){
36570                 this.setActivePanel(panel);
36571             }
36572             this.fireEvent("paneladded", this, panel);
36573             return panel;
36574         }
36575         */
36576         if(!this.tabs){
36577             this.initTabs();
36578         }else{
36579             this.initPanelAsTab(panel);
36580         }
36581         
36582         
36583         if(panel.background !== true){
36584             this.tabs.activate(panel.getEl().id);
36585         }
36586         this.fireEvent("paneladded", this, panel);
36587         return panel;
36588     },
36589
36590     /**
36591      * Hides the tab for the specified panel.
36592      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36593      */
36594     hidePanel : function(panel){
36595         if(this.tabs && (panel = this.getPanel(panel))){
36596             this.tabs.hideTab(panel.getEl().id);
36597         }
36598     },
36599
36600     /**
36601      * Unhides the tab for a previously hidden panel.
36602      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36603      */
36604     unhidePanel : function(panel){
36605         if(this.tabs && (panel = this.getPanel(panel))){
36606             this.tabs.unhideTab(panel.getEl().id);
36607         }
36608     },
36609
36610     clearPanels : function(){
36611         while(this.panels.getCount() > 0){
36612              this.remove(this.panels.first());
36613         }
36614     },
36615
36616     /**
36617      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36618      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36619      * @param {Boolean} preservePanel Overrides the config preservePanel option
36620      * @return {Roo.ContentPanel} The panel that was removed
36621      */
36622     remove : function(panel, preservePanel)
36623     {
36624         panel = this.getPanel(panel);
36625         if(!panel){
36626             return null;
36627         }
36628         var e = {};
36629         this.fireEvent("beforeremove", this, panel, e);
36630         if(e.cancel === true){
36631             return null;
36632         }
36633         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36634         var panelId = panel.getId();
36635         this.panels.removeKey(panelId);
36636         if(preservePanel){
36637             document.body.appendChild(panel.getEl().dom);
36638         }
36639         if(this.tabs){
36640             this.tabs.removeTab(panel.getEl().id);
36641         }else if (!preservePanel){
36642             this.bodyEl.dom.removeChild(panel.getEl().dom);
36643         }
36644         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36645             var p = this.panels.first();
36646             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36647             tempEl.appendChild(p.getEl().dom);
36648             this.bodyEl.update("");
36649             this.bodyEl.dom.appendChild(p.getEl().dom);
36650             tempEl = null;
36651             this.updateTitle(p.getTitle());
36652             this.tabs = null;
36653             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36654             this.setActivePanel(p);
36655         }
36656         panel.setRegion(null);
36657         if(this.activePanel == panel){
36658             this.activePanel = null;
36659         }
36660         if(this.config.autoDestroy !== false && preservePanel !== true){
36661             try{panel.destroy();}catch(e){}
36662         }
36663         this.fireEvent("panelremoved", this, panel);
36664         return panel;
36665     },
36666
36667     /**
36668      * Returns the TabPanel component used by this region
36669      * @return {Roo.TabPanel}
36670      */
36671     getTabs : function(){
36672         return this.tabs;
36673     },
36674
36675     createTool : function(parentEl, className){
36676         var btn = Roo.DomHelper.append(parentEl, {
36677             tag: "div",
36678             cls: "x-layout-tools-button",
36679             children: [ {
36680                 tag: "div",
36681                 cls: "roo-layout-tools-button-inner " + className,
36682                 html: "&#160;"
36683             }]
36684         }, true);
36685         btn.addClassOnOver("roo-layout-tools-button-over");
36686         return btn;
36687     }
36688 });/*
36689  * Based on:
36690  * Ext JS Library 1.1.1
36691  * Copyright(c) 2006-2007, Ext JS, LLC.
36692  *
36693  * Originally Released Under LGPL - original licence link has changed is not relivant.
36694  *
36695  * Fork - LGPL
36696  * <script type="text/javascript">
36697  */
36698  
36699
36700
36701 /**
36702  * @class Roo.SplitLayoutRegion
36703  * @extends Roo.LayoutRegion
36704  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36705  */
36706 Roo.bootstrap.layout.Split = function(config){
36707     this.cursor = config.cursor;
36708     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36709 };
36710
36711 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36712 {
36713     splitTip : "Drag to resize.",
36714     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36715     useSplitTips : false,
36716
36717     applyConfig : function(config){
36718         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36719     },
36720     
36721     onRender : function(ctr,pos) {
36722         
36723         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36724         if(!this.config.split){
36725             return;
36726         }
36727         if(!this.split){
36728             
36729             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36730                             tag: "div",
36731                             id: this.el.id + "-split",
36732                             cls: "roo-layout-split roo-layout-split-"+this.position,
36733                             html: "&#160;"
36734             });
36735             /** The SplitBar for this region 
36736             * @type Roo.SplitBar */
36737             // does not exist yet...
36738             Roo.log([this.position, this.orientation]);
36739             
36740             this.split = new Roo.bootstrap.SplitBar({
36741                 dragElement : splitEl,
36742                 resizingElement: this.el,
36743                 orientation : this.orientation
36744             });
36745             
36746             this.split.on("moved", this.onSplitMove, this);
36747             this.split.useShim = this.config.useShim === true;
36748             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36749             if(this.useSplitTips){
36750                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36751             }
36752             //if(config.collapsible){
36753             //    this.split.el.on("dblclick", this.collapse,  this);
36754             //}
36755         }
36756         if(typeof this.config.minSize != "undefined"){
36757             this.split.minSize = this.config.minSize;
36758         }
36759         if(typeof this.config.maxSize != "undefined"){
36760             this.split.maxSize = this.config.maxSize;
36761         }
36762         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36763             this.hideSplitter();
36764         }
36765         
36766     },
36767
36768     getHMaxSize : function(){
36769          var cmax = this.config.maxSize || 10000;
36770          var center = this.mgr.getRegion("center");
36771          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36772     },
36773
36774     getVMaxSize : function(){
36775          var cmax = this.config.maxSize || 10000;
36776          var center = this.mgr.getRegion("center");
36777          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36778     },
36779
36780     onSplitMove : function(split, newSize){
36781         this.fireEvent("resized", this, newSize);
36782     },
36783     
36784     /** 
36785      * Returns the {@link Roo.SplitBar} for this region.
36786      * @return {Roo.SplitBar}
36787      */
36788     getSplitBar : function(){
36789         return this.split;
36790     },
36791     
36792     hide : function(){
36793         this.hideSplitter();
36794         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36795     },
36796
36797     hideSplitter : function(){
36798         if(this.split){
36799             this.split.el.setLocation(-2000,-2000);
36800             this.split.el.hide();
36801         }
36802     },
36803
36804     show : function(){
36805         if(this.split){
36806             this.split.el.show();
36807         }
36808         Roo.bootstrap.layout.Split.superclass.show.call(this);
36809     },
36810     
36811     beforeSlide: function(){
36812         if(Roo.isGecko){// firefox overflow auto bug workaround
36813             this.bodyEl.clip();
36814             if(this.tabs) {
36815                 this.tabs.bodyEl.clip();
36816             }
36817             if(this.activePanel){
36818                 this.activePanel.getEl().clip();
36819                 
36820                 if(this.activePanel.beforeSlide){
36821                     this.activePanel.beforeSlide();
36822                 }
36823             }
36824         }
36825     },
36826     
36827     afterSlide : function(){
36828         if(Roo.isGecko){// firefox overflow auto bug workaround
36829             this.bodyEl.unclip();
36830             if(this.tabs) {
36831                 this.tabs.bodyEl.unclip();
36832             }
36833             if(this.activePanel){
36834                 this.activePanel.getEl().unclip();
36835                 if(this.activePanel.afterSlide){
36836                     this.activePanel.afterSlide();
36837                 }
36838             }
36839         }
36840     },
36841
36842     initAutoHide : function(){
36843         if(this.autoHide !== false){
36844             if(!this.autoHideHd){
36845                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36846                 this.autoHideHd = {
36847                     "mouseout": function(e){
36848                         if(!e.within(this.el, true)){
36849                             st.delay(500);
36850                         }
36851                     },
36852                     "mouseover" : function(e){
36853                         st.cancel();
36854                     },
36855                     scope : this
36856                 };
36857             }
36858             this.el.on(this.autoHideHd);
36859         }
36860     },
36861
36862     clearAutoHide : function(){
36863         if(this.autoHide !== false){
36864             this.el.un("mouseout", this.autoHideHd.mouseout);
36865             this.el.un("mouseover", this.autoHideHd.mouseover);
36866         }
36867     },
36868
36869     clearMonitor : function(){
36870         Roo.get(document).un("click", this.slideInIf, this);
36871     },
36872
36873     // these names are backwards but not changed for compat
36874     slideOut : function(){
36875         if(this.isSlid || this.el.hasActiveFx()){
36876             return;
36877         }
36878         this.isSlid = true;
36879         if(this.collapseBtn){
36880             this.collapseBtn.hide();
36881         }
36882         this.closeBtnState = this.closeBtn.getStyle('display');
36883         this.closeBtn.hide();
36884         if(this.stickBtn){
36885             this.stickBtn.show();
36886         }
36887         this.el.show();
36888         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36889         this.beforeSlide();
36890         this.el.setStyle("z-index", 10001);
36891         this.el.slideIn(this.getSlideAnchor(), {
36892             callback: function(){
36893                 this.afterSlide();
36894                 this.initAutoHide();
36895                 Roo.get(document).on("click", this.slideInIf, this);
36896                 this.fireEvent("slideshow", this);
36897             },
36898             scope: this,
36899             block: true
36900         });
36901     },
36902
36903     afterSlideIn : function(){
36904         this.clearAutoHide();
36905         this.isSlid = false;
36906         this.clearMonitor();
36907         this.el.setStyle("z-index", "");
36908         if(this.collapseBtn){
36909             this.collapseBtn.show();
36910         }
36911         this.closeBtn.setStyle('display', this.closeBtnState);
36912         if(this.stickBtn){
36913             this.stickBtn.hide();
36914         }
36915         this.fireEvent("slidehide", this);
36916     },
36917
36918     slideIn : function(cb){
36919         if(!this.isSlid || this.el.hasActiveFx()){
36920             Roo.callback(cb);
36921             return;
36922         }
36923         this.isSlid = false;
36924         this.beforeSlide();
36925         this.el.slideOut(this.getSlideAnchor(), {
36926             callback: function(){
36927                 this.el.setLeftTop(-10000, -10000);
36928                 this.afterSlide();
36929                 this.afterSlideIn();
36930                 Roo.callback(cb);
36931             },
36932             scope: this,
36933             block: true
36934         });
36935     },
36936     
36937     slideInIf : function(e){
36938         if(!e.within(this.el)){
36939             this.slideIn();
36940         }
36941     },
36942
36943     animateCollapse : function(){
36944         this.beforeSlide();
36945         this.el.setStyle("z-index", 20000);
36946         var anchor = this.getSlideAnchor();
36947         this.el.slideOut(anchor, {
36948             callback : function(){
36949                 this.el.setStyle("z-index", "");
36950                 this.collapsedEl.slideIn(anchor, {duration:.3});
36951                 this.afterSlide();
36952                 this.el.setLocation(-10000,-10000);
36953                 this.el.hide();
36954                 this.fireEvent("collapsed", this);
36955             },
36956             scope: this,
36957             block: true
36958         });
36959     },
36960
36961     animateExpand : function(){
36962         this.beforeSlide();
36963         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36964         this.el.setStyle("z-index", 20000);
36965         this.collapsedEl.hide({
36966             duration:.1
36967         });
36968         this.el.slideIn(this.getSlideAnchor(), {
36969             callback : function(){
36970                 this.el.setStyle("z-index", "");
36971                 this.afterSlide();
36972                 if(this.split){
36973                     this.split.el.show();
36974                 }
36975                 this.fireEvent("invalidated", this);
36976                 this.fireEvent("expanded", this);
36977             },
36978             scope: this,
36979             block: true
36980         });
36981     },
36982
36983     anchors : {
36984         "west" : "left",
36985         "east" : "right",
36986         "north" : "top",
36987         "south" : "bottom"
36988     },
36989
36990     sanchors : {
36991         "west" : "l",
36992         "east" : "r",
36993         "north" : "t",
36994         "south" : "b"
36995     },
36996
36997     canchors : {
36998         "west" : "tl-tr",
36999         "east" : "tr-tl",
37000         "north" : "tl-bl",
37001         "south" : "bl-tl"
37002     },
37003
37004     getAnchor : function(){
37005         return this.anchors[this.position];
37006     },
37007
37008     getCollapseAnchor : function(){
37009         return this.canchors[this.position];
37010     },
37011
37012     getSlideAnchor : function(){
37013         return this.sanchors[this.position];
37014     },
37015
37016     getAlignAdj : function(){
37017         var cm = this.cmargins;
37018         switch(this.position){
37019             case "west":
37020                 return [0, 0];
37021             break;
37022             case "east":
37023                 return [0, 0];
37024             break;
37025             case "north":
37026                 return [0, 0];
37027             break;
37028             case "south":
37029                 return [0, 0];
37030             break;
37031         }
37032     },
37033
37034     getExpandAdj : function(){
37035         var c = this.collapsedEl, cm = this.cmargins;
37036         switch(this.position){
37037             case "west":
37038                 return [-(cm.right+c.getWidth()+cm.left), 0];
37039             break;
37040             case "east":
37041                 return [cm.right+c.getWidth()+cm.left, 0];
37042             break;
37043             case "north":
37044                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37045             break;
37046             case "south":
37047                 return [0, cm.top+cm.bottom+c.getHeight()];
37048             break;
37049         }
37050     }
37051 });/*
37052  * Based on:
37053  * Ext JS Library 1.1.1
37054  * Copyright(c) 2006-2007, Ext JS, LLC.
37055  *
37056  * Originally Released Under LGPL - original licence link has changed is not relivant.
37057  *
37058  * Fork - LGPL
37059  * <script type="text/javascript">
37060  */
37061 /*
37062  * These classes are private internal classes
37063  */
37064 Roo.bootstrap.layout.Center = function(config){
37065     config.region = "center";
37066     Roo.bootstrap.layout.Region.call(this, config);
37067     this.visible = true;
37068     this.minWidth = config.minWidth || 20;
37069     this.minHeight = config.minHeight || 20;
37070 };
37071
37072 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37073     hide : function(){
37074         // center panel can't be hidden
37075     },
37076     
37077     show : function(){
37078         // center panel can't be hidden
37079     },
37080     
37081     getMinWidth: function(){
37082         return this.minWidth;
37083     },
37084     
37085     getMinHeight: function(){
37086         return this.minHeight;
37087     }
37088 });
37089
37090
37091
37092
37093  
37094
37095
37096
37097
37098
37099
37100 Roo.bootstrap.layout.North = function(config)
37101 {
37102     config.region = 'north';
37103     config.cursor = 'n-resize';
37104     
37105     Roo.bootstrap.layout.Split.call(this, config);
37106     
37107     
37108     if(this.split){
37109         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37110         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37111         this.split.el.addClass("roo-layout-split-v");
37112     }
37113     var size = config.initialSize || config.height;
37114     if(typeof size != "undefined"){
37115         this.el.setHeight(size);
37116     }
37117 };
37118 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37119 {
37120     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37121     
37122     
37123     
37124     getBox : function(){
37125         if(this.collapsed){
37126             return this.collapsedEl.getBox();
37127         }
37128         var box = this.el.getBox();
37129         if(this.split){
37130             box.height += this.split.el.getHeight();
37131         }
37132         return box;
37133     },
37134     
37135     updateBox : function(box){
37136         if(this.split && !this.collapsed){
37137             box.height -= this.split.el.getHeight();
37138             this.split.el.setLeft(box.x);
37139             this.split.el.setTop(box.y+box.height);
37140             this.split.el.setWidth(box.width);
37141         }
37142         if(this.collapsed){
37143             this.updateBody(box.width, null);
37144         }
37145         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37146     }
37147 });
37148
37149
37150
37151
37152
37153 Roo.bootstrap.layout.South = function(config){
37154     config.region = 'south';
37155     config.cursor = 's-resize';
37156     Roo.bootstrap.layout.Split.call(this, config);
37157     if(this.split){
37158         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37159         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37160         this.split.el.addClass("roo-layout-split-v");
37161     }
37162     var size = config.initialSize || config.height;
37163     if(typeof size != "undefined"){
37164         this.el.setHeight(size);
37165     }
37166 };
37167
37168 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37169     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37170     getBox : function(){
37171         if(this.collapsed){
37172             return this.collapsedEl.getBox();
37173         }
37174         var box = this.el.getBox();
37175         if(this.split){
37176             var sh = this.split.el.getHeight();
37177             box.height += sh;
37178             box.y -= sh;
37179         }
37180         return box;
37181     },
37182     
37183     updateBox : function(box){
37184         if(this.split && !this.collapsed){
37185             var sh = this.split.el.getHeight();
37186             box.height -= sh;
37187             box.y += sh;
37188             this.split.el.setLeft(box.x);
37189             this.split.el.setTop(box.y-sh);
37190             this.split.el.setWidth(box.width);
37191         }
37192         if(this.collapsed){
37193             this.updateBody(box.width, null);
37194         }
37195         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37196     }
37197 });
37198
37199 Roo.bootstrap.layout.East = function(config){
37200     config.region = "east";
37201     config.cursor = "e-resize";
37202     Roo.bootstrap.layout.Split.call(this, config);
37203     if(this.split){
37204         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37205         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37206         this.split.el.addClass("roo-layout-split-h");
37207     }
37208     var size = config.initialSize || config.width;
37209     if(typeof size != "undefined"){
37210         this.el.setWidth(size);
37211     }
37212 };
37213 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37214     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37215     getBox : function(){
37216         if(this.collapsed){
37217             return this.collapsedEl.getBox();
37218         }
37219         var box = this.el.getBox();
37220         if(this.split){
37221             var sw = this.split.el.getWidth();
37222             box.width += sw;
37223             box.x -= sw;
37224         }
37225         return box;
37226     },
37227
37228     updateBox : function(box){
37229         if(this.split && !this.collapsed){
37230             var sw = this.split.el.getWidth();
37231             box.width -= sw;
37232             this.split.el.setLeft(box.x);
37233             this.split.el.setTop(box.y);
37234             this.split.el.setHeight(box.height);
37235             box.x += sw;
37236         }
37237         if(this.collapsed){
37238             this.updateBody(null, box.height);
37239         }
37240         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37241     }
37242 });
37243
37244 Roo.bootstrap.layout.West = function(config){
37245     config.region = "west";
37246     config.cursor = "w-resize";
37247     
37248     Roo.bootstrap.layout.Split.call(this, config);
37249     if(this.split){
37250         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37251         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37252         this.split.el.addClass("roo-layout-split-h");
37253     }
37254     
37255 };
37256 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37257     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37258     
37259     onRender: function(ctr, pos)
37260     {
37261         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37262         var size = this.config.initialSize || this.config.width;
37263         if(typeof size != "undefined"){
37264             this.el.setWidth(size);
37265         }
37266     },
37267     
37268     getBox : function(){
37269         if(this.collapsed){
37270             return this.collapsedEl.getBox();
37271         }
37272         var box = this.el.getBox();
37273         if(this.split){
37274             box.width += this.split.el.getWidth();
37275         }
37276         return box;
37277     },
37278     
37279     updateBox : function(box){
37280         if(this.split && !this.collapsed){
37281             var sw = this.split.el.getWidth();
37282             box.width -= sw;
37283             this.split.el.setLeft(box.x+box.width);
37284             this.split.el.setTop(box.y);
37285             this.split.el.setHeight(box.height);
37286         }
37287         if(this.collapsed){
37288             this.updateBody(null, box.height);
37289         }
37290         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37291     }
37292 });Roo.namespace("Roo.bootstrap.panel");/*
37293  * Based on:
37294  * Ext JS Library 1.1.1
37295  * Copyright(c) 2006-2007, Ext JS, LLC.
37296  *
37297  * Originally Released Under LGPL - original licence link has changed is not relivant.
37298  *
37299  * Fork - LGPL
37300  * <script type="text/javascript">
37301  */
37302 /**
37303  * @class Roo.ContentPanel
37304  * @extends Roo.util.Observable
37305  * A basic ContentPanel element.
37306  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37307  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37308  * @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
37309  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37310  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37311  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37312  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37313  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37314  * @cfg {String} title          The title for this panel
37315  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37316  * @cfg {String} url            Calls {@link #setUrl} with this value
37317  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37318  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37319  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37320  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37321  * @cfg {Boolean} badges render the badges
37322
37323  * @constructor
37324  * Create a new ContentPanel.
37325  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37326  * @param {String/Object} config A string to set only the title or a config object
37327  * @param {String} content (optional) Set the HTML content for this panel
37328  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37329  */
37330 Roo.bootstrap.panel.Content = function( config){
37331     
37332     this.tpl = config.tpl || false;
37333     
37334     var el = config.el;
37335     var content = config.content;
37336
37337     if(config.autoCreate){ // xtype is available if this is called from factory
37338         el = Roo.id();
37339     }
37340     this.el = Roo.get(el);
37341     if(!this.el && config && config.autoCreate){
37342         if(typeof config.autoCreate == "object"){
37343             if(!config.autoCreate.id){
37344                 config.autoCreate.id = config.id||el;
37345             }
37346             this.el = Roo.DomHelper.append(document.body,
37347                         config.autoCreate, true);
37348         }else{
37349             var elcfg =  {   tag: "div",
37350                             cls: "roo-layout-inactive-content",
37351                             id: config.id||el
37352                             };
37353             if (config.html) {
37354                 elcfg.html = config.html;
37355                 
37356             }
37357                         
37358             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37359         }
37360     } 
37361     this.closable = false;
37362     this.loaded = false;
37363     this.active = false;
37364    
37365       
37366     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37367         
37368         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37369         
37370         this.wrapEl = this.el; //this.el.wrap();
37371         var ti = [];
37372         if (config.toolbar.items) {
37373             ti = config.toolbar.items ;
37374             delete config.toolbar.items ;
37375         }
37376         
37377         var nitems = [];
37378         this.toolbar.render(this.wrapEl, 'before');
37379         for(var i =0;i < ti.length;i++) {
37380           //  Roo.log(['add child', items[i]]);
37381             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37382         }
37383         this.toolbar.items = nitems;
37384         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37385         delete config.toolbar;
37386         
37387     }
37388     /*
37389     // xtype created footer. - not sure if will work as we normally have to render first..
37390     if (this.footer && !this.footer.el && this.footer.xtype) {
37391         if (!this.wrapEl) {
37392             this.wrapEl = this.el.wrap();
37393         }
37394     
37395         this.footer.container = this.wrapEl.createChild();
37396          
37397         this.footer = Roo.factory(this.footer, Roo);
37398         
37399     }
37400     */
37401     
37402      if(typeof config == "string"){
37403         this.title = config;
37404     }else{
37405         Roo.apply(this, config);
37406     }
37407     
37408     if(this.resizeEl){
37409         this.resizeEl = Roo.get(this.resizeEl, true);
37410     }else{
37411         this.resizeEl = this.el;
37412     }
37413     // handle view.xtype
37414     
37415  
37416     
37417     
37418     this.addEvents({
37419         /**
37420          * @event activate
37421          * Fires when this panel is activated. 
37422          * @param {Roo.ContentPanel} this
37423          */
37424         "activate" : true,
37425         /**
37426          * @event deactivate
37427          * Fires when this panel is activated. 
37428          * @param {Roo.ContentPanel} this
37429          */
37430         "deactivate" : true,
37431
37432         /**
37433          * @event resize
37434          * Fires when this panel is resized if fitToFrame is true.
37435          * @param {Roo.ContentPanel} this
37436          * @param {Number} width The width after any component adjustments
37437          * @param {Number} height The height after any component adjustments
37438          */
37439         "resize" : true,
37440         
37441          /**
37442          * @event render
37443          * Fires when this tab is created
37444          * @param {Roo.ContentPanel} this
37445          */
37446         "render" : true
37447         
37448         
37449         
37450     });
37451     
37452
37453     
37454     
37455     if(this.autoScroll){
37456         this.resizeEl.setStyle("overflow", "auto");
37457     } else {
37458         // fix randome scrolling
37459         //this.el.on('scroll', function() {
37460         //    Roo.log('fix random scolling');
37461         //    this.scrollTo('top',0); 
37462         //});
37463     }
37464     content = content || this.content;
37465     if(content){
37466         this.setContent(content);
37467     }
37468     if(config && config.url){
37469         this.setUrl(this.url, this.params, this.loadOnce);
37470     }
37471     
37472     
37473     
37474     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37475     
37476     if (this.view && typeof(this.view.xtype) != 'undefined') {
37477         this.view.el = this.el.appendChild(document.createElement("div"));
37478         this.view = Roo.factory(this.view); 
37479         this.view.render  &&  this.view.render(false, '');  
37480     }
37481     
37482     
37483     this.fireEvent('render', this);
37484 };
37485
37486 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37487     
37488     tabTip : '',
37489     
37490     setRegion : function(region){
37491         this.region = region;
37492         this.setActiveClass(region && !this.background);
37493     },
37494     
37495     
37496     setActiveClass: function(state)
37497     {
37498         if(state){
37499            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37500            this.el.setStyle('position','relative');
37501         }else{
37502            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37503            this.el.setStyle('position', 'absolute');
37504         } 
37505     },
37506     
37507     /**
37508      * Returns the toolbar for this Panel if one was configured. 
37509      * @return {Roo.Toolbar} 
37510      */
37511     getToolbar : function(){
37512         return this.toolbar;
37513     },
37514     
37515     setActiveState : function(active)
37516     {
37517         this.active = active;
37518         this.setActiveClass(active);
37519         if(!active){
37520             if(this.fireEvent("deactivate", this) === false){
37521                 return false;
37522             }
37523             return true;
37524         }
37525         this.fireEvent("activate", this);
37526         return true;
37527     },
37528     /**
37529      * Updates this panel's element
37530      * @param {String} content The new content
37531      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37532     */
37533     setContent : function(content, loadScripts){
37534         this.el.update(content, loadScripts);
37535     },
37536
37537     ignoreResize : function(w, h){
37538         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37539             return true;
37540         }else{
37541             this.lastSize = {width: w, height: h};
37542             return false;
37543         }
37544     },
37545     /**
37546      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37547      * @return {Roo.UpdateManager} The UpdateManager
37548      */
37549     getUpdateManager : function(){
37550         return this.el.getUpdateManager();
37551     },
37552      /**
37553      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37554      * @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:
37555 <pre><code>
37556 panel.load({
37557     url: "your-url.php",
37558     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37559     callback: yourFunction,
37560     scope: yourObject, //(optional scope)
37561     discardUrl: false,
37562     nocache: false,
37563     text: "Loading...",
37564     timeout: 30,
37565     scripts: false
37566 });
37567 </code></pre>
37568      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37569      * 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.
37570      * @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}
37571      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37572      * @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.
37573      * @return {Roo.ContentPanel} this
37574      */
37575     load : function(){
37576         var um = this.el.getUpdateManager();
37577         um.update.apply(um, arguments);
37578         return this;
37579     },
37580
37581
37582     /**
37583      * 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.
37584      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37585      * @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)
37586      * @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)
37587      * @return {Roo.UpdateManager} The UpdateManager
37588      */
37589     setUrl : function(url, params, loadOnce){
37590         if(this.refreshDelegate){
37591             this.removeListener("activate", this.refreshDelegate);
37592         }
37593         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37594         this.on("activate", this.refreshDelegate);
37595         return this.el.getUpdateManager();
37596     },
37597     
37598     _handleRefresh : function(url, params, loadOnce){
37599         if(!loadOnce || !this.loaded){
37600             var updater = this.el.getUpdateManager();
37601             updater.update(url, params, this._setLoaded.createDelegate(this));
37602         }
37603     },
37604     
37605     _setLoaded : function(){
37606         this.loaded = true;
37607     }, 
37608     
37609     /**
37610      * Returns this panel's id
37611      * @return {String} 
37612      */
37613     getId : function(){
37614         return this.el.id;
37615     },
37616     
37617     /** 
37618      * Returns this panel's element - used by regiosn to add.
37619      * @return {Roo.Element} 
37620      */
37621     getEl : function(){
37622         return this.wrapEl || this.el;
37623     },
37624     
37625    
37626     
37627     adjustForComponents : function(width, height)
37628     {
37629         //Roo.log('adjustForComponents ');
37630         if(this.resizeEl != this.el){
37631             width -= this.el.getFrameWidth('lr');
37632             height -= this.el.getFrameWidth('tb');
37633         }
37634         if(this.toolbar){
37635             var te = this.toolbar.getEl();
37636             te.setWidth(width);
37637             height -= te.getHeight();
37638         }
37639         if(this.footer){
37640             var te = this.footer.getEl();
37641             te.setWidth(width);
37642             height -= te.getHeight();
37643         }
37644         
37645         
37646         if(this.adjustments){
37647             width += this.adjustments[0];
37648             height += this.adjustments[1];
37649         }
37650         return {"width": width, "height": height};
37651     },
37652     
37653     setSize : function(width, height){
37654         if(this.fitToFrame && !this.ignoreResize(width, height)){
37655             if(this.fitContainer && this.resizeEl != this.el){
37656                 this.el.setSize(width, height);
37657             }
37658             var size = this.adjustForComponents(width, height);
37659             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37660             this.fireEvent('resize', this, size.width, size.height);
37661         }
37662     },
37663     
37664     /**
37665      * Returns this panel's title
37666      * @return {String} 
37667      */
37668     getTitle : function(){
37669         
37670         if (typeof(this.title) != 'object') {
37671             return this.title;
37672         }
37673         
37674         var t = '';
37675         for (var k in this.title) {
37676             if (!this.title.hasOwnProperty(k)) {
37677                 continue;
37678             }
37679             
37680             if (k.indexOf('-') >= 0) {
37681                 var s = k.split('-');
37682                 for (var i = 0; i<s.length; i++) {
37683                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37684                 }
37685             } else {
37686                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37687             }
37688         }
37689         return t;
37690     },
37691     
37692     /**
37693      * Set this panel's title
37694      * @param {String} title
37695      */
37696     setTitle : function(title){
37697         this.title = title;
37698         if(this.region){
37699             this.region.updatePanelTitle(this, title);
37700         }
37701     },
37702     
37703     /**
37704      * Returns true is this panel was configured to be closable
37705      * @return {Boolean} 
37706      */
37707     isClosable : function(){
37708         return this.closable;
37709     },
37710     
37711     beforeSlide : function(){
37712         this.el.clip();
37713         this.resizeEl.clip();
37714     },
37715     
37716     afterSlide : function(){
37717         this.el.unclip();
37718         this.resizeEl.unclip();
37719     },
37720     
37721     /**
37722      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37723      *   Will fail silently if the {@link #setUrl} method has not been called.
37724      *   This does not activate the panel, just updates its content.
37725      */
37726     refresh : function(){
37727         if(this.refreshDelegate){
37728            this.loaded = false;
37729            this.refreshDelegate();
37730         }
37731     },
37732     
37733     /**
37734      * Destroys this panel
37735      */
37736     destroy : function(){
37737         this.el.removeAllListeners();
37738         var tempEl = document.createElement("span");
37739         tempEl.appendChild(this.el.dom);
37740         tempEl.innerHTML = "";
37741         this.el.remove();
37742         this.el = null;
37743     },
37744     
37745     /**
37746      * form - if the content panel contains a form - this is a reference to it.
37747      * @type {Roo.form.Form}
37748      */
37749     form : false,
37750     /**
37751      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37752      *    This contains a reference to it.
37753      * @type {Roo.View}
37754      */
37755     view : false,
37756     
37757       /**
37758      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37759      * <pre><code>
37760
37761 layout.addxtype({
37762        xtype : 'Form',
37763        items: [ .... ]
37764    }
37765 );
37766
37767 </code></pre>
37768      * @param {Object} cfg Xtype definition of item to add.
37769      */
37770     
37771     
37772     getChildContainer: function () {
37773         return this.getEl();
37774     }
37775     
37776     
37777     /*
37778         var  ret = new Roo.factory(cfg);
37779         return ret;
37780         
37781         
37782         // add form..
37783         if (cfg.xtype.match(/^Form$/)) {
37784             
37785             var el;
37786             //if (this.footer) {
37787             //    el = this.footer.container.insertSibling(false, 'before');
37788             //} else {
37789                 el = this.el.createChild();
37790             //}
37791
37792             this.form = new  Roo.form.Form(cfg);
37793             
37794             
37795             if ( this.form.allItems.length) {
37796                 this.form.render(el.dom);
37797             }
37798             return this.form;
37799         }
37800         // should only have one of theses..
37801         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37802             // views.. should not be just added - used named prop 'view''
37803             
37804             cfg.el = this.el.appendChild(document.createElement("div"));
37805             // factory?
37806             
37807             var ret = new Roo.factory(cfg);
37808              
37809              ret.render && ret.render(false, ''); // render blank..
37810             this.view = ret;
37811             return ret;
37812         }
37813         return false;
37814     }
37815     \*/
37816 });
37817  
37818 /**
37819  * @class Roo.bootstrap.panel.Grid
37820  * @extends Roo.bootstrap.panel.Content
37821  * @constructor
37822  * Create a new GridPanel.
37823  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37824  * @param {Object} config A the config object
37825   
37826  */
37827
37828
37829
37830 Roo.bootstrap.panel.Grid = function(config)
37831 {
37832     
37833       
37834     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37835         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37836
37837     config.el = this.wrapper;
37838     //this.el = this.wrapper;
37839     
37840       if (config.container) {
37841         // ctor'ed from a Border/panel.grid
37842         
37843         
37844         this.wrapper.setStyle("overflow", "hidden");
37845         this.wrapper.addClass('roo-grid-container');
37846
37847     }
37848     
37849     
37850     if(config.toolbar){
37851         var tool_el = this.wrapper.createChild();    
37852         this.toolbar = Roo.factory(config.toolbar);
37853         var ti = [];
37854         if (config.toolbar.items) {
37855             ti = config.toolbar.items ;
37856             delete config.toolbar.items ;
37857         }
37858         
37859         var nitems = [];
37860         this.toolbar.render(tool_el);
37861         for(var i =0;i < ti.length;i++) {
37862           //  Roo.log(['add child', items[i]]);
37863             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37864         }
37865         this.toolbar.items = nitems;
37866         
37867         delete config.toolbar;
37868     }
37869     
37870     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37871     config.grid.scrollBody = true;;
37872     config.grid.monitorWindowResize = false; // turn off autosizing
37873     config.grid.autoHeight = false;
37874     config.grid.autoWidth = false;
37875     
37876     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37877     
37878     if (config.background) {
37879         // render grid on panel activation (if panel background)
37880         this.on('activate', function(gp) {
37881             if (!gp.grid.rendered) {
37882                 gp.grid.render(this.wrapper);
37883                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37884             }
37885         });
37886             
37887     } else {
37888         this.grid.render(this.wrapper);
37889         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37890
37891     }
37892     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37893     // ??? needed ??? config.el = this.wrapper;
37894     
37895     
37896     
37897   
37898     // xtype created footer. - not sure if will work as we normally have to render first..
37899     if (this.footer && !this.footer.el && this.footer.xtype) {
37900         
37901         var ctr = this.grid.getView().getFooterPanel(true);
37902         this.footer.dataSource = this.grid.dataSource;
37903         this.footer = Roo.factory(this.footer, Roo);
37904         this.footer.render(ctr);
37905         
37906     }
37907     
37908     
37909     
37910     
37911      
37912 };
37913
37914 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37915     getId : function(){
37916         return this.grid.id;
37917     },
37918     
37919     /**
37920      * Returns the grid for this panel
37921      * @return {Roo.bootstrap.Table} 
37922      */
37923     getGrid : function(){
37924         return this.grid;    
37925     },
37926     
37927     setSize : function(width, height){
37928         if(!this.ignoreResize(width, height)){
37929             var grid = this.grid;
37930             var size = this.adjustForComponents(width, height);
37931             var gridel = grid.getGridEl();
37932             gridel.setSize(size.width, size.height);
37933             /*
37934             var thd = grid.getGridEl().select('thead',true).first();
37935             var tbd = grid.getGridEl().select('tbody', true).first();
37936             if (tbd) {
37937                 tbd.setSize(width, height - thd.getHeight());
37938             }
37939             */
37940             grid.autoSize();
37941         }
37942     },
37943      
37944     
37945     
37946     beforeSlide : function(){
37947         this.grid.getView().scroller.clip();
37948     },
37949     
37950     afterSlide : function(){
37951         this.grid.getView().scroller.unclip();
37952     },
37953     
37954     destroy : function(){
37955         this.grid.destroy();
37956         delete this.grid;
37957         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37958     }
37959 });
37960
37961 /**
37962  * @class Roo.bootstrap.panel.Nest
37963  * @extends Roo.bootstrap.panel.Content
37964  * @constructor
37965  * Create a new Panel, that can contain a layout.Border.
37966  * 
37967  * 
37968  * @param {Roo.BorderLayout} layout The layout for this panel
37969  * @param {String/Object} config A string to set only the title or a config object
37970  */
37971 Roo.bootstrap.panel.Nest = function(config)
37972 {
37973     // construct with only one argument..
37974     /* FIXME - implement nicer consturctors
37975     if (layout.layout) {
37976         config = layout;
37977         layout = config.layout;
37978         delete config.layout;
37979     }
37980     if (layout.xtype && !layout.getEl) {
37981         // then layout needs constructing..
37982         layout = Roo.factory(layout, Roo);
37983     }
37984     */
37985     
37986     config.el =  config.layout.getEl();
37987     
37988     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37989     
37990     config.layout.monitorWindowResize = false; // turn off autosizing
37991     this.layout = config.layout;
37992     this.layout.getEl().addClass("roo-layout-nested-layout");
37993     this.layout.parent = this;
37994     
37995     
37996     
37997     
37998 };
37999
38000 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38001
38002     setSize : function(width, height){
38003         if(!this.ignoreResize(width, height)){
38004             var size = this.adjustForComponents(width, height);
38005             var el = this.layout.getEl();
38006             if (size.height < 1) {
38007                 el.setWidth(size.width);   
38008             } else {
38009                 el.setSize(size.width, size.height);
38010             }
38011             var touch = el.dom.offsetWidth;
38012             this.layout.layout();
38013             // ie requires a double layout on the first pass
38014             if(Roo.isIE && !this.initialized){
38015                 this.initialized = true;
38016                 this.layout.layout();
38017             }
38018         }
38019     },
38020     
38021     // activate all subpanels if not currently active..
38022     
38023     setActiveState : function(active){
38024         this.active = active;
38025         this.setActiveClass(active);
38026         
38027         if(!active){
38028             this.fireEvent("deactivate", this);
38029             return;
38030         }
38031         
38032         this.fireEvent("activate", this);
38033         // not sure if this should happen before or after..
38034         if (!this.layout) {
38035             return; // should not happen..
38036         }
38037         var reg = false;
38038         for (var r in this.layout.regions) {
38039             reg = this.layout.getRegion(r);
38040             if (reg.getActivePanel()) {
38041                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38042                 reg.setActivePanel(reg.getActivePanel());
38043                 continue;
38044             }
38045             if (!reg.panels.length) {
38046                 continue;
38047             }
38048             reg.showPanel(reg.getPanel(0));
38049         }
38050         
38051         
38052         
38053         
38054     },
38055     
38056     /**
38057      * Returns the nested BorderLayout for this panel
38058      * @return {Roo.BorderLayout} 
38059      */
38060     getLayout : function(){
38061         return this.layout;
38062     },
38063     
38064      /**
38065      * Adds a xtype elements to the layout of the nested panel
38066      * <pre><code>
38067
38068 panel.addxtype({
38069        xtype : 'ContentPanel',
38070        region: 'west',
38071        items: [ .... ]
38072    }
38073 );
38074
38075 panel.addxtype({
38076         xtype : 'NestedLayoutPanel',
38077         region: 'west',
38078         layout: {
38079            center: { },
38080            west: { }   
38081         },
38082         items : [ ... list of content panels or nested layout panels.. ]
38083    }
38084 );
38085 </code></pre>
38086      * @param {Object} cfg Xtype definition of item to add.
38087      */
38088     addxtype : function(cfg) {
38089         return this.layout.addxtype(cfg);
38090     
38091     }
38092 });/*
38093  * Based on:
38094  * Ext JS Library 1.1.1
38095  * Copyright(c) 2006-2007, Ext JS, LLC.
38096  *
38097  * Originally Released Under LGPL - original licence link has changed is not relivant.
38098  *
38099  * Fork - LGPL
38100  * <script type="text/javascript">
38101  */
38102 /**
38103  * @class Roo.TabPanel
38104  * @extends Roo.util.Observable
38105  * A lightweight tab container.
38106  * <br><br>
38107  * Usage:
38108  * <pre><code>
38109 // basic tabs 1, built from existing content
38110 var tabs = new Roo.TabPanel("tabs1");
38111 tabs.addTab("script", "View Script");
38112 tabs.addTab("markup", "View Markup");
38113 tabs.activate("script");
38114
38115 // more advanced tabs, built from javascript
38116 var jtabs = new Roo.TabPanel("jtabs");
38117 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38118
38119 // set up the UpdateManager
38120 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38121 var updater = tab2.getUpdateManager();
38122 updater.setDefaultUrl("ajax1.htm");
38123 tab2.on('activate', updater.refresh, updater, true);
38124
38125 // Use setUrl for Ajax loading
38126 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38127 tab3.setUrl("ajax2.htm", null, true);
38128
38129 // Disabled tab
38130 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38131 tab4.disable();
38132
38133 jtabs.activate("jtabs-1");
38134  * </code></pre>
38135  * @constructor
38136  * Create a new TabPanel.
38137  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38138  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38139  */
38140 Roo.bootstrap.panel.Tabs = function(config){
38141     /**
38142     * The container element for this TabPanel.
38143     * @type Roo.Element
38144     */
38145     this.el = Roo.get(config.el);
38146     delete config.el;
38147     if(config){
38148         if(typeof config == "boolean"){
38149             this.tabPosition = config ? "bottom" : "top";
38150         }else{
38151             Roo.apply(this, config);
38152         }
38153     }
38154     
38155     if(this.tabPosition == "bottom"){
38156         // if tabs are at the bottom = create the body first.
38157         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38158         this.el.addClass("roo-tabs-bottom");
38159     }
38160     // next create the tabs holders
38161     
38162     if (this.tabPosition == "west"){
38163         
38164         var reg = this.region; // fake it..
38165         while (reg) {
38166             if (!reg.mgr.parent) {
38167                 break;
38168             }
38169             reg = reg.mgr.parent.region;
38170         }
38171         Roo.log("got nest?");
38172         Roo.log(reg);
38173         if (reg.mgr.getRegion('west')) {
38174             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38175             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38176             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38177             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38178             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38179         
38180             
38181         }
38182         
38183         
38184     } else {
38185      
38186         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38187         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38188         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38189         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38190     }
38191     
38192     
38193     if(Roo.isIE){
38194         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38195     }
38196     
38197     // finally - if tabs are at the top, then create the body last..
38198     if(this.tabPosition != "bottom"){
38199         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38200          * @type Roo.Element
38201          */
38202         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38203         this.el.addClass("roo-tabs-top");
38204     }
38205     this.items = [];
38206
38207     this.bodyEl.setStyle("position", "relative");
38208
38209     this.active = null;
38210     this.activateDelegate = this.activate.createDelegate(this);
38211
38212     this.addEvents({
38213         /**
38214          * @event tabchange
38215          * Fires when the active tab changes
38216          * @param {Roo.TabPanel} this
38217          * @param {Roo.TabPanelItem} activePanel The new active tab
38218          */
38219         "tabchange": true,
38220         /**
38221          * @event beforetabchange
38222          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38223          * @param {Roo.TabPanel} this
38224          * @param {Object} e Set cancel to true on this object to cancel the tab change
38225          * @param {Roo.TabPanelItem} tab The tab being changed to
38226          */
38227         "beforetabchange" : true
38228     });
38229
38230     Roo.EventManager.onWindowResize(this.onResize, this);
38231     this.cpad = this.el.getPadding("lr");
38232     this.hiddenCount = 0;
38233
38234
38235     // toolbar on the tabbar support...
38236     if (this.toolbar) {
38237         alert("no toolbar support yet");
38238         this.toolbar  = false;
38239         /*
38240         var tcfg = this.toolbar;
38241         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38242         this.toolbar = new Roo.Toolbar(tcfg);
38243         if (Roo.isSafari) {
38244             var tbl = tcfg.container.child('table', true);
38245             tbl.setAttribute('width', '100%');
38246         }
38247         */
38248         
38249     }
38250    
38251
38252
38253     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38254 };
38255
38256 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38257     /*
38258      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38259      */
38260     tabPosition : "top",
38261     /*
38262      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38263      */
38264     currentTabWidth : 0,
38265     /*
38266      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38267      */
38268     minTabWidth : 40,
38269     /*
38270      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38271      */
38272     maxTabWidth : 250,
38273     /*
38274      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38275      */
38276     preferredTabWidth : 175,
38277     /*
38278      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38279      */
38280     resizeTabs : false,
38281     /*
38282      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38283      */
38284     monitorResize : true,
38285     /*
38286      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38287      */
38288     toolbar : false,  // set by caller..
38289     
38290     region : false, /// set by caller
38291     
38292     disableTooltips : true, // not used yet...
38293
38294     /**
38295      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38296      * @param {String} id The id of the div to use <b>or create</b>
38297      * @param {String} text The text for the tab
38298      * @param {String} content (optional) Content to put in the TabPanelItem body
38299      * @param {Boolean} closable (optional) True to create a close icon on the tab
38300      * @return {Roo.TabPanelItem} The created TabPanelItem
38301      */
38302     addTab : function(id, text, content, closable, tpl)
38303     {
38304         var item = new Roo.bootstrap.panel.TabItem({
38305             panel: this,
38306             id : id,
38307             text : text,
38308             closable : closable,
38309             tpl : tpl
38310         });
38311         this.addTabItem(item);
38312         if(content){
38313             item.setContent(content);
38314         }
38315         return item;
38316     },
38317
38318     /**
38319      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38320      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38321      * @return {Roo.TabPanelItem}
38322      */
38323     getTab : function(id){
38324         return this.items[id];
38325     },
38326
38327     /**
38328      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38329      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38330      */
38331     hideTab : function(id){
38332         var t = this.items[id];
38333         if(!t.isHidden()){
38334            t.setHidden(true);
38335            this.hiddenCount++;
38336            this.autoSizeTabs();
38337         }
38338     },
38339
38340     /**
38341      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38342      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38343      */
38344     unhideTab : function(id){
38345         var t = this.items[id];
38346         if(t.isHidden()){
38347            t.setHidden(false);
38348            this.hiddenCount--;
38349            this.autoSizeTabs();
38350         }
38351     },
38352
38353     /**
38354      * Adds an existing {@link Roo.TabPanelItem}.
38355      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38356      */
38357     addTabItem : function(item)
38358     {
38359         this.items[item.id] = item;
38360         this.items.push(item);
38361         this.autoSizeTabs();
38362       //  if(this.resizeTabs){
38363     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38364   //         this.autoSizeTabs();
38365 //        }else{
38366 //            item.autoSize();
38367        // }
38368     },
38369
38370     /**
38371      * Removes a {@link Roo.TabPanelItem}.
38372      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38373      */
38374     removeTab : function(id){
38375         var items = this.items;
38376         var tab = items[id];
38377         if(!tab) { return; }
38378         var index = items.indexOf(tab);
38379         if(this.active == tab && items.length > 1){
38380             var newTab = this.getNextAvailable(index);
38381             if(newTab) {
38382                 newTab.activate();
38383             }
38384         }
38385         this.stripEl.dom.removeChild(tab.pnode.dom);
38386         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38387             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38388         }
38389         items.splice(index, 1);
38390         delete this.items[tab.id];
38391         tab.fireEvent("close", tab);
38392         tab.purgeListeners();
38393         this.autoSizeTabs();
38394     },
38395
38396     getNextAvailable : function(start){
38397         var items = this.items;
38398         var index = start;
38399         // look for a next tab that will slide over to
38400         // replace the one being removed
38401         while(index < items.length){
38402             var item = items[++index];
38403             if(item && !item.isHidden()){
38404                 return item;
38405             }
38406         }
38407         // if one isn't found select the previous tab (on the left)
38408         index = start;
38409         while(index >= 0){
38410             var item = items[--index];
38411             if(item && !item.isHidden()){
38412                 return item;
38413             }
38414         }
38415         return null;
38416     },
38417
38418     /**
38419      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38420      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38421      */
38422     disableTab : function(id){
38423         var tab = this.items[id];
38424         if(tab && this.active != tab){
38425             tab.disable();
38426         }
38427     },
38428
38429     /**
38430      * Enables a {@link Roo.TabPanelItem} that is disabled.
38431      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38432      */
38433     enableTab : function(id){
38434         var tab = this.items[id];
38435         tab.enable();
38436     },
38437
38438     /**
38439      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38440      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38441      * @return {Roo.TabPanelItem} The TabPanelItem.
38442      */
38443     activate : function(id)
38444     {
38445         //Roo.log('activite:'  + id);
38446         
38447         var tab = this.items[id];
38448         if(!tab){
38449             return null;
38450         }
38451         if(tab == this.active || tab.disabled){
38452             return tab;
38453         }
38454         var e = {};
38455         this.fireEvent("beforetabchange", this, e, tab);
38456         if(e.cancel !== true && !tab.disabled){
38457             if(this.active){
38458                 this.active.hide();
38459             }
38460             this.active = this.items[id];
38461             this.active.show();
38462             this.fireEvent("tabchange", this, this.active);
38463         }
38464         return tab;
38465     },
38466
38467     /**
38468      * Gets the active {@link Roo.TabPanelItem}.
38469      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38470      */
38471     getActiveTab : function(){
38472         return this.active;
38473     },
38474
38475     /**
38476      * Updates the tab body element to fit the height of the container element
38477      * for overflow scrolling
38478      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38479      */
38480     syncHeight : function(targetHeight){
38481         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38482         var bm = this.bodyEl.getMargins();
38483         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38484         this.bodyEl.setHeight(newHeight);
38485         return newHeight;
38486     },
38487
38488     onResize : function(){
38489         if(this.monitorResize){
38490             this.autoSizeTabs();
38491         }
38492     },
38493
38494     /**
38495      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38496      */
38497     beginUpdate : function(){
38498         this.updating = true;
38499     },
38500
38501     /**
38502      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38503      */
38504     endUpdate : function(){
38505         this.updating = false;
38506         this.autoSizeTabs();
38507     },
38508
38509     /**
38510      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38511      */
38512     autoSizeTabs : function()
38513     {
38514         var count = this.items.length;
38515         var vcount = count - this.hiddenCount;
38516         
38517         if (vcount < 2) {
38518             this.stripEl.hide();
38519         } else {
38520             this.stripEl.show();
38521         }
38522         
38523         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38524             return;
38525         }
38526         
38527         
38528         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38529         var availWidth = Math.floor(w / vcount);
38530         var b = this.stripBody;
38531         if(b.getWidth() > w){
38532             var tabs = this.items;
38533             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38534             if(availWidth < this.minTabWidth){
38535                 /*if(!this.sleft){    // incomplete scrolling code
38536                     this.createScrollButtons();
38537                 }
38538                 this.showScroll();
38539                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38540             }
38541         }else{
38542             if(this.currentTabWidth < this.preferredTabWidth){
38543                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38544             }
38545         }
38546     },
38547
38548     /**
38549      * Returns the number of tabs in this TabPanel.
38550      * @return {Number}
38551      */
38552      getCount : function(){
38553          return this.items.length;
38554      },
38555
38556     /**
38557      * Resizes all the tabs to the passed width
38558      * @param {Number} The new width
38559      */
38560     setTabWidth : function(width){
38561         this.currentTabWidth = width;
38562         for(var i = 0, len = this.items.length; i < len; i++) {
38563                 if(!this.items[i].isHidden()) {
38564                 this.items[i].setWidth(width);
38565             }
38566         }
38567     },
38568
38569     /**
38570      * Destroys this TabPanel
38571      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38572      */
38573     destroy : function(removeEl){
38574         Roo.EventManager.removeResizeListener(this.onResize, this);
38575         for(var i = 0, len = this.items.length; i < len; i++){
38576             this.items[i].purgeListeners();
38577         }
38578         if(removeEl === true){
38579             this.el.update("");
38580             this.el.remove();
38581         }
38582     },
38583     
38584     createStrip : function(container)
38585     {
38586         var strip = document.createElement("nav");
38587         strip.className = Roo.bootstrap.version == 4 ?
38588             "navbar-light bg-light" : 
38589             "navbar navbar-default"; //"x-tabs-wrap";
38590         container.appendChild(strip);
38591         return strip;
38592     },
38593     
38594     createStripList : function(strip)
38595     {
38596         // div wrapper for retard IE
38597         // returns the "tr" element.
38598         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38599         //'<div class="x-tabs-strip-wrap">'+
38600           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38601           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38602         return strip.firstChild; //.firstChild.firstChild.firstChild;
38603     },
38604     createBody : function(container)
38605     {
38606         var body = document.createElement("div");
38607         Roo.id(body, "tab-body");
38608         //Roo.fly(body).addClass("x-tabs-body");
38609         Roo.fly(body).addClass("tab-content");
38610         container.appendChild(body);
38611         return body;
38612     },
38613     createItemBody :function(bodyEl, id){
38614         var body = Roo.getDom(id);
38615         if(!body){
38616             body = document.createElement("div");
38617             body.id = id;
38618         }
38619         //Roo.fly(body).addClass("x-tabs-item-body");
38620         Roo.fly(body).addClass("tab-pane");
38621          bodyEl.insertBefore(body, bodyEl.firstChild);
38622         return body;
38623     },
38624     /** @private */
38625     createStripElements :  function(stripEl, text, closable, tpl)
38626     {
38627         var td = document.createElement("li"); // was td..
38628         td.className = 'nav-item';
38629         
38630         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38631         
38632         
38633         stripEl.appendChild(td);
38634         /*if(closable){
38635             td.className = "x-tabs-closable";
38636             if(!this.closeTpl){
38637                 this.closeTpl = new Roo.Template(
38638                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38639                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38640                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38641                 );
38642             }
38643             var el = this.closeTpl.overwrite(td, {"text": text});
38644             var close = el.getElementsByTagName("div")[0];
38645             var inner = el.getElementsByTagName("em")[0];
38646             return {"el": el, "close": close, "inner": inner};
38647         } else {
38648         */
38649         // not sure what this is..
38650 //            if(!this.tabTpl){
38651                 //this.tabTpl = new Roo.Template(
38652                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38653                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38654                 //);
38655 //                this.tabTpl = new Roo.Template(
38656 //                   '<a href="#">' +
38657 //                   '<span unselectable="on"' +
38658 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38659 //                            ' >{text}</span></a>'
38660 //                );
38661 //                
38662 //            }
38663
38664
38665             var template = tpl || this.tabTpl || false;
38666             
38667             if(!template){
38668                 template =  new Roo.Template(
38669                         Roo.bootstrap.version == 4 ? 
38670                             (
38671                                 '<a class="nav-link" href="#" unselectable="on"' +
38672                                      (this.disableTooltips ? '' : ' title="{text}"') +
38673                                      ' >{text}</a>'
38674                             ) : (
38675                                 '<a class="nav-link" href="#">' +
38676                                 '<span unselectable="on"' +
38677                                          (this.disableTooltips ? '' : ' title="{text}"') +
38678                                     ' >{text}</span></a>'
38679                             )
38680                 );
38681             }
38682             
38683             switch (typeof(template)) {
38684                 case 'object' :
38685                     break;
38686                 case 'string' :
38687                     template = new Roo.Template(template);
38688                     break;
38689                 default :
38690                     break;
38691             }
38692             
38693             var el = template.overwrite(td, {"text": text});
38694             
38695             var inner = el.getElementsByTagName("span")[0];
38696             
38697             return {"el": el, "inner": inner};
38698             
38699     }
38700         
38701     
38702 });
38703
38704 /**
38705  * @class Roo.TabPanelItem
38706  * @extends Roo.util.Observable
38707  * Represents an individual item (tab plus body) in a TabPanel.
38708  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38709  * @param {String} id The id of this TabPanelItem
38710  * @param {String} text The text for the tab of this TabPanelItem
38711  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38712  */
38713 Roo.bootstrap.panel.TabItem = function(config){
38714     /**
38715      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38716      * @type Roo.TabPanel
38717      */
38718     this.tabPanel = config.panel;
38719     /**
38720      * The id for this TabPanelItem
38721      * @type String
38722      */
38723     this.id = config.id;
38724     /** @private */
38725     this.disabled = false;
38726     /** @private */
38727     this.text = config.text;
38728     /** @private */
38729     this.loaded = false;
38730     this.closable = config.closable;
38731
38732     /**
38733      * The body element for this TabPanelItem.
38734      * @type Roo.Element
38735      */
38736     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38737     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38738     this.bodyEl.setStyle("display", "block");
38739     this.bodyEl.setStyle("zoom", "1");
38740     //this.hideAction();
38741
38742     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38743     /** @private */
38744     this.el = Roo.get(els.el);
38745     this.inner = Roo.get(els.inner, true);
38746      this.textEl = Roo.bootstrap.version == 4 ?
38747         this.el : Roo.get(this.el.dom.firstChild, true);
38748
38749     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38750     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38751
38752     
38753 //    this.el.on("mousedown", this.onTabMouseDown, this);
38754     this.el.on("click", this.onTabClick, this);
38755     /** @private */
38756     if(config.closable){
38757         var c = Roo.get(els.close, true);
38758         c.dom.title = this.closeText;
38759         c.addClassOnOver("close-over");
38760         c.on("click", this.closeClick, this);
38761      }
38762
38763     this.addEvents({
38764          /**
38765          * @event activate
38766          * Fires when this tab becomes the active tab.
38767          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38768          * @param {Roo.TabPanelItem} this
38769          */
38770         "activate": true,
38771         /**
38772          * @event beforeclose
38773          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38774          * @param {Roo.TabPanelItem} this
38775          * @param {Object} e Set cancel to true on this object to cancel the close.
38776          */
38777         "beforeclose": true,
38778         /**
38779          * @event close
38780          * Fires when this tab is closed.
38781          * @param {Roo.TabPanelItem} this
38782          */
38783          "close": true,
38784         /**
38785          * @event deactivate
38786          * Fires when this tab is no longer the active tab.
38787          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38788          * @param {Roo.TabPanelItem} this
38789          */
38790          "deactivate" : true
38791     });
38792     this.hidden = false;
38793
38794     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38795 };
38796
38797 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38798            {
38799     purgeListeners : function(){
38800        Roo.util.Observable.prototype.purgeListeners.call(this);
38801        this.el.removeAllListeners();
38802     },
38803     /**
38804      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38805      */
38806     show : function(){
38807         this.status_node.addClass("active");
38808         this.showAction();
38809         if(Roo.isOpera){
38810             this.tabPanel.stripWrap.repaint();
38811         }
38812         this.fireEvent("activate", this.tabPanel, this);
38813     },
38814
38815     /**
38816      * Returns true if this tab is the active tab.
38817      * @return {Boolean}
38818      */
38819     isActive : function(){
38820         return this.tabPanel.getActiveTab() == this;
38821     },
38822
38823     /**
38824      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38825      */
38826     hide : function(){
38827         this.status_node.removeClass("active");
38828         this.hideAction();
38829         this.fireEvent("deactivate", this.tabPanel, this);
38830     },
38831
38832     hideAction : function(){
38833         this.bodyEl.hide();
38834         this.bodyEl.setStyle("position", "absolute");
38835         this.bodyEl.setLeft("-20000px");
38836         this.bodyEl.setTop("-20000px");
38837     },
38838
38839     showAction : function(){
38840         this.bodyEl.setStyle("position", "relative");
38841         this.bodyEl.setTop("");
38842         this.bodyEl.setLeft("");
38843         this.bodyEl.show();
38844     },
38845
38846     /**
38847      * Set the tooltip for the tab.
38848      * @param {String} tooltip The tab's tooltip
38849      */
38850     setTooltip : function(text){
38851         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38852             this.textEl.dom.qtip = text;
38853             this.textEl.dom.removeAttribute('title');
38854         }else{
38855             this.textEl.dom.title = text;
38856         }
38857     },
38858
38859     onTabClick : function(e){
38860         e.preventDefault();
38861         this.tabPanel.activate(this.id);
38862     },
38863
38864     onTabMouseDown : function(e){
38865         e.preventDefault();
38866         this.tabPanel.activate(this.id);
38867     },
38868 /*
38869     getWidth : function(){
38870         return this.inner.getWidth();
38871     },
38872
38873     setWidth : function(width){
38874         var iwidth = width - this.linode.getPadding("lr");
38875         this.inner.setWidth(iwidth);
38876         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38877         this.linode.setWidth(width);
38878     },
38879 */
38880     /**
38881      * Show or hide the tab
38882      * @param {Boolean} hidden True to hide or false to show.
38883      */
38884     setHidden : function(hidden){
38885         this.hidden = hidden;
38886         this.linode.setStyle("display", hidden ? "none" : "");
38887     },
38888
38889     /**
38890      * Returns true if this tab is "hidden"
38891      * @return {Boolean}
38892      */
38893     isHidden : function(){
38894         return this.hidden;
38895     },
38896
38897     /**
38898      * Returns the text for this tab
38899      * @return {String}
38900      */
38901     getText : function(){
38902         return this.text;
38903     },
38904     /*
38905     autoSize : function(){
38906         //this.el.beginMeasure();
38907         this.textEl.setWidth(1);
38908         /*
38909          *  #2804 [new] Tabs in Roojs
38910          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38911          */
38912         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38913         //this.el.endMeasure();
38914     //},
38915
38916     /**
38917      * Sets the text for the tab (Note: this also sets the tooltip text)
38918      * @param {String} text The tab's text and tooltip
38919      */
38920     setText : function(text){
38921         this.text = text;
38922         this.textEl.update(text);
38923         this.setTooltip(text);
38924         //if(!this.tabPanel.resizeTabs){
38925         //    this.autoSize();
38926         //}
38927     },
38928     /**
38929      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38930      */
38931     activate : function(){
38932         this.tabPanel.activate(this.id);
38933     },
38934
38935     /**
38936      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38937      */
38938     disable : function(){
38939         if(this.tabPanel.active != this){
38940             this.disabled = true;
38941             this.status_node.addClass("disabled");
38942         }
38943     },
38944
38945     /**
38946      * Enables this TabPanelItem if it was previously disabled.
38947      */
38948     enable : function(){
38949         this.disabled = false;
38950         this.status_node.removeClass("disabled");
38951     },
38952
38953     /**
38954      * Sets the content for this TabPanelItem.
38955      * @param {String} content The content
38956      * @param {Boolean} loadScripts true to look for and load scripts
38957      */
38958     setContent : function(content, loadScripts){
38959         this.bodyEl.update(content, loadScripts);
38960     },
38961
38962     /**
38963      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38964      * @return {Roo.UpdateManager} The UpdateManager
38965      */
38966     getUpdateManager : function(){
38967         return this.bodyEl.getUpdateManager();
38968     },
38969
38970     /**
38971      * Set a URL to be used to load the content for this TabPanelItem.
38972      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38973      * @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)
38974      * @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)
38975      * @return {Roo.UpdateManager} The UpdateManager
38976      */
38977     setUrl : function(url, params, loadOnce){
38978         if(this.refreshDelegate){
38979             this.un('activate', this.refreshDelegate);
38980         }
38981         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38982         this.on("activate", this.refreshDelegate);
38983         return this.bodyEl.getUpdateManager();
38984     },
38985
38986     /** @private */
38987     _handleRefresh : function(url, params, loadOnce){
38988         if(!loadOnce || !this.loaded){
38989             var updater = this.bodyEl.getUpdateManager();
38990             updater.update(url, params, this._setLoaded.createDelegate(this));
38991         }
38992     },
38993
38994     /**
38995      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38996      *   Will fail silently if the setUrl method has not been called.
38997      *   This does not activate the panel, just updates its content.
38998      */
38999     refresh : function(){
39000         if(this.refreshDelegate){
39001            this.loaded = false;
39002            this.refreshDelegate();
39003         }
39004     },
39005
39006     /** @private */
39007     _setLoaded : function(){
39008         this.loaded = true;
39009     },
39010
39011     /** @private */
39012     closeClick : function(e){
39013         var o = {};
39014         e.stopEvent();
39015         this.fireEvent("beforeclose", this, o);
39016         if(o.cancel !== true){
39017             this.tabPanel.removeTab(this.id);
39018         }
39019     },
39020     /**
39021      * The text displayed in the tooltip for the close icon.
39022      * @type String
39023      */
39024     closeText : "Close this tab"
39025 });
39026 /**
39027 *    This script refer to:
39028 *    Title: International Telephone Input
39029 *    Author: Jack O'Connor
39030 *    Code version:  v12.1.12
39031 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39032 **/
39033
39034 Roo.bootstrap.PhoneInputData = function() {
39035     var d = [
39036       [
39037         "Afghanistan (‫افغانستان‬‎)",
39038         "af",
39039         "93"
39040       ],
39041       [
39042         "Albania (Shqipëri)",
39043         "al",
39044         "355"
39045       ],
39046       [
39047         "Algeria (‫الجزائر‬‎)",
39048         "dz",
39049         "213"
39050       ],
39051       [
39052         "American Samoa",
39053         "as",
39054         "1684"
39055       ],
39056       [
39057         "Andorra",
39058         "ad",
39059         "376"
39060       ],
39061       [
39062         "Angola",
39063         "ao",
39064         "244"
39065       ],
39066       [
39067         "Anguilla",
39068         "ai",
39069         "1264"
39070       ],
39071       [
39072         "Antigua and Barbuda",
39073         "ag",
39074         "1268"
39075       ],
39076       [
39077         "Argentina",
39078         "ar",
39079         "54"
39080       ],
39081       [
39082         "Armenia (Հայաստան)",
39083         "am",
39084         "374"
39085       ],
39086       [
39087         "Aruba",
39088         "aw",
39089         "297"
39090       ],
39091       [
39092         "Australia",
39093         "au",
39094         "61",
39095         0
39096       ],
39097       [
39098         "Austria (Österreich)",
39099         "at",
39100         "43"
39101       ],
39102       [
39103         "Azerbaijan (Azərbaycan)",
39104         "az",
39105         "994"
39106       ],
39107       [
39108         "Bahamas",
39109         "bs",
39110         "1242"
39111       ],
39112       [
39113         "Bahrain (‫البحرين‬‎)",
39114         "bh",
39115         "973"
39116       ],
39117       [
39118         "Bangladesh (বাংলাদেশ)",
39119         "bd",
39120         "880"
39121       ],
39122       [
39123         "Barbados",
39124         "bb",
39125         "1246"
39126       ],
39127       [
39128         "Belarus (Беларусь)",
39129         "by",
39130         "375"
39131       ],
39132       [
39133         "Belgium (België)",
39134         "be",
39135         "32"
39136       ],
39137       [
39138         "Belize",
39139         "bz",
39140         "501"
39141       ],
39142       [
39143         "Benin (Bénin)",
39144         "bj",
39145         "229"
39146       ],
39147       [
39148         "Bermuda",
39149         "bm",
39150         "1441"
39151       ],
39152       [
39153         "Bhutan (འབྲུག)",
39154         "bt",
39155         "975"
39156       ],
39157       [
39158         "Bolivia",
39159         "bo",
39160         "591"
39161       ],
39162       [
39163         "Bosnia and Herzegovina (Босна и Херцеговина)",
39164         "ba",
39165         "387"
39166       ],
39167       [
39168         "Botswana",
39169         "bw",
39170         "267"
39171       ],
39172       [
39173         "Brazil (Brasil)",
39174         "br",
39175         "55"
39176       ],
39177       [
39178         "British Indian Ocean Territory",
39179         "io",
39180         "246"
39181       ],
39182       [
39183         "British Virgin Islands",
39184         "vg",
39185         "1284"
39186       ],
39187       [
39188         "Brunei",
39189         "bn",
39190         "673"
39191       ],
39192       [
39193         "Bulgaria (България)",
39194         "bg",
39195         "359"
39196       ],
39197       [
39198         "Burkina Faso",
39199         "bf",
39200         "226"
39201       ],
39202       [
39203         "Burundi (Uburundi)",
39204         "bi",
39205         "257"
39206       ],
39207       [
39208         "Cambodia (កម្ពុជា)",
39209         "kh",
39210         "855"
39211       ],
39212       [
39213         "Cameroon (Cameroun)",
39214         "cm",
39215         "237"
39216       ],
39217       [
39218         "Canada",
39219         "ca",
39220         "1",
39221         1,
39222         ["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"]
39223       ],
39224       [
39225         "Cape Verde (Kabu Verdi)",
39226         "cv",
39227         "238"
39228       ],
39229       [
39230         "Caribbean Netherlands",
39231         "bq",
39232         "599",
39233         1
39234       ],
39235       [
39236         "Cayman Islands",
39237         "ky",
39238         "1345"
39239       ],
39240       [
39241         "Central African Republic (République centrafricaine)",
39242         "cf",
39243         "236"
39244       ],
39245       [
39246         "Chad (Tchad)",
39247         "td",
39248         "235"
39249       ],
39250       [
39251         "Chile",
39252         "cl",
39253         "56"
39254       ],
39255       [
39256         "China (中国)",
39257         "cn",
39258         "86"
39259       ],
39260       [
39261         "Christmas Island",
39262         "cx",
39263         "61",
39264         2
39265       ],
39266       [
39267         "Cocos (Keeling) Islands",
39268         "cc",
39269         "61",
39270         1
39271       ],
39272       [
39273         "Colombia",
39274         "co",
39275         "57"
39276       ],
39277       [
39278         "Comoros (‫جزر القمر‬‎)",
39279         "km",
39280         "269"
39281       ],
39282       [
39283         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39284         "cd",
39285         "243"
39286       ],
39287       [
39288         "Congo (Republic) (Congo-Brazzaville)",
39289         "cg",
39290         "242"
39291       ],
39292       [
39293         "Cook Islands",
39294         "ck",
39295         "682"
39296       ],
39297       [
39298         "Costa Rica",
39299         "cr",
39300         "506"
39301       ],
39302       [
39303         "Côte d’Ivoire",
39304         "ci",
39305         "225"
39306       ],
39307       [
39308         "Croatia (Hrvatska)",
39309         "hr",
39310         "385"
39311       ],
39312       [
39313         "Cuba",
39314         "cu",
39315         "53"
39316       ],
39317       [
39318         "Curaçao",
39319         "cw",
39320         "599",
39321         0
39322       ],
39323       [
39324         "Cyprus (Κύπρος)",
39325         "cy",
39326         "357"
39327       ],
39328       [
39329         "Czech Republic (Česká republika)",
39330         "cz",
39331         "420"
39332       ],
39333       [
39334         "Denmark (Danmark)",
39335         "dk",
39336         "45"
39337       ],
39338       [
39339         "Djibouti",
39340         "dj",
39341         "253"
39342       ],
39343       [
39344         "Dominica",
39345         "dm",
39346         "1767"
39347       ],
39348       [
39349         "Dominican Republic (República Dominicana)",
39350         "do",
39351         "1",
39352         2,
39353         ["809", "829", "849"]
39354       ],
39355       [
39356         "Ecuador",
39357         "ec",
39358         "593"
39359       ],
39360       [
39361         "Egypt (‫مصر‬‎)",
39362         "eg",
39363         "20"
39364       ],
39365       [
39366         "El Salvador",
39367         "sv",
39368         "503"
39369       ],
39370       [
39371         "Equatorial Guinea (Guinea Ecuatorial)",
39372         "gq",
39373         "240"
39374       ],
39375       [
39376         "Eritrea",
39377         "er",
39378         "291"
39379       ],
39380       [
39381         "Estonia (Eesti)",
39382         "ee",
39383         "372"
39384       ],
39385       [
39386         "Ethiopia",
39387         "et",
39388         "251"
39389       ],
39390       [
39391         "Falkland Islands (Islas Malvinas)",
39392         "fk",
39393         "500"
39394       ],
39395       [
39396         "Faroe Islands (Føroyar)",
39397         "fo",
39398         "298"
39399       ],
39400       [
39401         "Fiji",
39402         "fj",
39403         "679"
39404       ],
39405       [
39406         "Finland (Suomi)",
39407         "fi",
39408         "358",
39409         0
39410       ],
39411       [
39412         "France",
39413         "fr",
39414         "33"
39415       ],
39416       [
39417         "French Guiana (Guyane française)",
39418         "gf",
39419         "594"
39420       ],
39421       [
39422         "French Polynesia (Polynésie française)",
39423         "pf",
39424         "689"
39425       ],
39426       [
39427         "Gabon",
39428         "ga",
39429         "241"
39430       ],
39431       [
39432         "Gambia",
39433         "gm",
39434         "220"
39435       ],
39436       [
39437         "Georgia (საქართველო)",
39438         "ge",
39439         "995"
39440       ],
39441       [
39442         "Germany (Deutschland)",
39443         "de",
39444         "49"
39445       ],
39446       [
39447         "Ghana (Gaana)",
39448         "gh",
39449         "233"
39450       ],
39451       [
39452         "Gibraltar",
39453         "gi",
39454         "350"
39455       ],
39456       [
39457         "Greece (Ελλάδα)",
39458         "gr",
39459         "30"
39460       ],
39461       [
39462         "Greenland (Kalaallit Nunaat)",
39463         "gl",
39464         "299"
39465       ],
39466       [
39467         "Grenada",
39468         "gd",
39469         "1473"
39470       ],
39471       [
39472         "Guadeloupe",
39473         "gp",
39474         "590",
39475         0
39476       ],
39477       [
39478         "Guam",
39479         "gu",
39480         "1671"
39481       ],
39482       [
39483         "Guatemala",
39484         "gt",
39485         "502"
39486       ],
39487       [
39488         "Guernsey",
39489         "gg",
39490         "44",
39491         1
39492       ],
39493       [
39494         "Guinea (Guinée)",
39495         "gn",
39496         "224"
39497       ],
39498       [
39499         "Guinea-Bissau (Guiné Bissau)",
39500         "gw",
39501         "245"
39502       ],
39503       [
39504         "Guyana",
39505         "gy",
39506         "592"
39507       ],
39508       [
39509         "Haiti",
39510         "ht",
39511         "509"
39512       ],
39513       [
39514         "Honduras",
39515         "hn",
39516         "504"
39517       ],
39518       [
39519         "Hong Kong (香港)",
39520         "hk",
39521         "852"
39522       ],
39523       [
39524         "Hungary (Magyarország)",
39525         "hu",
39526         "36"
39527       ],
39528       [
39529         "Iceland (Ísland)",
39530         "is",
39531         "354"
39532       ],
39533       [
39534         "India (भारत)",
39535         "in",
39536         "91"
39537       ],
39538       [
39539         "Indonesia",
39540         "id",
39541         "62"
39542       ],
39543       [
39544         "Iran (‫ایران‬‎)",
39545         "ir",
39546         "98"
39547       ],
39548       [
39549         "Iraq (‫العراق‬‎)",
39550         "iq",
39551         "964"
39552       ],
39553       [
39554         "Ireland",
39555         "ie",
39556         "353"
39557       ],
39558       [
39559         "Isle of Man",
39560         "im",
39561         "44",
39562         2
39563       ],
39564       [
39565         "Israel (‫ישראל‬‎)",
39566         "il",
39567         "972"
39568       ],
39569       [
39570         "Italy (Italia)",
39571         "it",
39572         "39",
39573         0
39574       ],
39575       [
39576         "Jamaica",
39577         "jm",
39578         "1876"
39579       ],
39580       [
39581         "Japan (日本)",
39582         "jp",
39583         "81"
39584       ],
39585       [
39586         "Jersey",
39587         "je",
39588         "44",
39589         3
39590       ],
39591       [
39592         "Jordan (‫الأردن‬‎)",
39593         "jo",
39594         "962"
39595       ],
39596       [
39597         "Kazakhstan (Казахстан)",
39598         "kz",
39599         "7",
39600         1
39601       ],
39602       [
39603         "Kenya",
39604         "ke",
39605         "254"
39606       ],
39607       [
39608         "Kiribati",
39609         "ki",
39610         "686"
39611       ],
39612       [
39613         "Kosovo",
39614         "xk",
39615         "383"
39616       ],
39617       [
39618         "Kuwait (‫الكويت‬‎)",
39619         "kw",
39620         "965"
39621       ],
39622       [
39623         "Kyrgyzstan (Кыргызстан)",
39624         "kg",
39625         "996"
39626       ],
39627       [
39628         "Laos (ລາວ)",
39629         "la",
39630         "856"
39631       ],
39632       [
39633         "Latvia (Latvija)",
39634         "lv",
39635         "371"
39636       ],
39637       [
39638         "Lebanon (‫لبنان‬‎)",
39639         "lb",
39640         "961"
39641       ],
39642       [
39643         "Lesotho",
39644         "ls",
39645         "266"
39646       ],
39647       [
39648         "Liberia",
39649         "lr",
39650         "231"
39651       ],
39652       [
39653         "Libya (‫ليبيا‬‎)",
39654         "ly",
39655         "218"
39656       ],
39657       [
39658         "Liechtenstein",
39659         "li",
39660         "423"
39661       ],
39662       [
39663         "Lithuania (Lietuva)",
39664         "lt",
39665         "370"
39666       ],
39667       [
39668         "Luxembourg",
39669         "lu",
39670         "352"
39671       ],
39672       [
39673         "Macau (澳門)",
39674         "mo",
39675         "853"
39676       ],
39677       [
39678         "Macedonia (FYROM) (Македонија)",
39679         "mk",
39680         "389"
39681       ],
39682       [
39683         "Madagascar (Madagasikara)",
39684         "mg",
39685         "261"
39686       ],
39687       [
39688         "Malawi",
39689         "mw",
39690         "265"
39691       ],
39692       [
39693         "Malaysia",
39694         "my",
39695         "60"
39696       ],
39697       [
39698         "Maldives",
39699         "mv",
39700         "960"
39701       ],
39702       [
39703         "Mali",
39704         "ml",
39705         "223"
39706       ],
39707       [
39708         "Malta",
39709         "mt",
39710         "356"
39711       ],
39712       [
39713         "Marshall Islands",
39714         "mh",
39715         "692"
39716       ],
39717       [
39718         "Martinique",
39719         "mq",
39720         "596"
39721       ],
39722       [
39723         "Mauritania (‫موريتانيا‬‎)",
39724         "mr",
39725         "222"
39726       ],
39727       [
39728         "Mauritius (Moris)",
39729         "mu",
39730         "230"
39731       ],
39732       [
39733         "Mayotte",
39734         "yt",
39735         "262",
39736         1
39737       ],
39738       [
39739         "Mexico (México)",
39740         "mx",
39741         "52"
39742       ],
39743       [
39744         "Micronesia",
39745         "fm",
39746         "691"
39747       ],
39748       [
39749         "Moldova (Republica Moldova)",
39750         "md",
39751         "373"
39752       ],
39753       [
39754         "Monaco",
39755         "mc",
39756         "377"
39757       ],
39758       [
39759         "Mongolia (Монгол)",
39760         "mn",
39761         "976"
39762       ],
39763       [
39764         "Montenegro (Crna Gora)",
39765         "me",
39766         "382"
39767       ],
39768       [
39769         "Montserrat",
39770         "ms",
39771         "1664"
39772       ],
39773       [
39774         "Morocco (‫المغرب‬‎)",
39775         "ma",
39776         "212",
39777         0
39778       ],
39779       [
39780         "Mozambique (Moçambique)",
39781         "mz",
39782         "258"
39783       ],
39784       [
39785         "Myanmar (Burma) (မြန်မာ)",
39786         "mm",
39787         "95"
39788       ],
39789       [
39790         "Namibia (Namibië)",
39791         "na",
39792         "264"
39793       ],
39794       [
39795         "Nauru",
39796         "nr",
39797         "674"
39798       ],
39799       [
39800         "Nepal (नेपाल)",
39801         "np",
39802         "977"
39803       ],
39804       [
39805         "Netherlands (Nederland)",
39806         "nl",
39807         "31"
39808       ],
39809       [
39810         "New Caledonia (Nouvelle-Calédonie)",
39811         "nc",
39812         "687"
39813       ],
39814       [
39815         "New Zealand",
39816         "nz",
39817         "64"
39818       ],
39819       [
39820         "Nicaragua",
39821         "ni",
39822         "505"
39823       ],
39824       [
39825         "Niger (Nijar)",
39826         "ne",
39827         "227"
39828       ],
39829       [
39830         "Nigeria",
39831         "ng",
39832         "234"
39833       ],
39834       [
39835         "Niue",
39836         "nu",
39837         "683"
39838       ],
39839       [
39840         "Norfolk Island",
39841         "nf",
39842         "672"
39843       ],
39844       [
39845         "North Korea (조선 민주주의 인민 공화국)",
39846         "kp",
39847         "850"
39848       ],
39849       [
39850         "Northern Mariana Islands",
39851         "mp",
39852         "1670"
39853       ],
39854       [
39855         "Norway (Norge)",
39856         "no",
39857         "47",
39858         0
39859       ],
39860       [
39861         "Oman (‫عُمان‬‎)",
39862         "om",
39863         "968"
39864       ],
39865       [
39866         "Pakistan (‫پاکستان‬‎)",
39867         "pk",
39868         "92"
39869       ],
39870       [
39871         "Palau",
39872         "pw",
39873         "680"
39874       ],
39875       [
39876         "Palestine (‫فلسطين‬‎)",
39877         "ps",
39878         "970"
39879       ],
39880       [
39881         "Panama (Panamá)",
39882         "pa",
39883         "507"
39884       ],
39885       [
39886         "Papua New Guinea",
39887         "pg",
39888         "675"
39889       ],
39890       [
39891         "Paraguay",
39892         "py",
39893         "595"
39894       ],
39895       [
39896         "Peru (Perú)",
39897         "pe",
39898         "51"
39899       ],
39900       [
39901         "Philippines",
39902         "ph",
39903         "63"
39904       ],
39905       [
39906         "Poland (Polska)",
39907         "pl",
39908         "48"
39909       ],
39910       [
39911         "Portugal",
39912         "pt",
39913         "351"
39914       ],
39915       [
39916         "Puerto Rico",
39917         "pr",
39918         "1",
39919         3,
39920         ["787", "939"]
39921       ],
39922       [
39923         "Qatar (‫قطر‬‎)",
39924         "qa",
39925         "974"
39926       ],
39927       [
39928         "Réunion (La Réunion)",
39929         "re",
39930         "262",
39931         0
39932       ],
39933       [
39934         "Romania (România)",
39935         "ro",
39936         "40"
39937       ],
39938       [
39939         "Russia (Россия)",
39940         "ru",
39941         "7",
39942         0
39943       ],
39944       [
39945         "Rwanda",
39946         "rw",
39947         "250"
39948       ],
39949       [
39950         "Saint Barthélemy",
39951         "bl",
39952         "590",
39953         1
39954       ],
39955       [
39956         "Saint Helena",
39957         "sh",
39958         "290"
39959       ],
39960       [
39961         "Saint Kitts and Nevis",
39962         "kn",
39963         "1869"
39964       ],
39965       [
39966         "Saint Lucia",
39967         "lc",
39968         "1758"
39969       ],
39970       [
39971         "Saint Martin (Saint-Martin (partie française))",
39972         "mf",
39973         "590",
39974         2
39975       ],
39976       [
39977         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39978         "pm",
39979         "508"
39980       ],
39981       [
39982         "Saint Vincent and the Grenadines",
39983         "vc",
39984         "1784"
39985       ],
39986       [
39987         "Samoa",
39988         "ws",
39989         "685"
39990       ],
39991       [
39992         "San Marino",
39993         "sm",
39994         "378"
39995       ],
39996       [
39997         "São Tomé and Príncipe (São Tomé e Príncipe)",
39998         "st",
39999         "239"
40000       ],
40001       [
40002         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
40003         "sa",
40004         "966"
40005       ],
40006       [
40007         "Senegal (Sénégal)",
40008         "sn",
40009         "221"
40010       ],
40011       [
40012         "Serbia (Србија)",
40013         "rs",
40014         "381"
40015       ],
40016       [
40017         "Seychelles",
40018         "sc",
40019         "248"
40020       ],
40021       [
40022         "Sierra Leone",
40023         "sl",
40024         "232"
40025       ],
40026       [
40027         "Singapore",
40028         "sg",
40029         "65"
40030       ],
40031       [
40032         "Sint Maarten",
40033         "sx",
40034         "1721"
40035       ],
40036       [
40037         "Slovakia (Slovensko)",
40038         "sk",
40039         "421"
40040       ],
40041       [
40042         "Slovenia (Slovenija)",
40043         "si",
40044         "386"
40045       ],
40046       [
40047         "Solomon Islands",
40048         "sb",
40049         "677"
40050       ],
40051       [
40052         "Somalia (Soomaaliya)",
40053         "so",
40054         "252"
40055       ],
40056       [
40057         "South Africa",
40058         "za",
40059         "27"
40060       ],
40061       [
40062         "South Korea (대한민국)",
40063         "kr",
40064         "82"
40065       ],
40066       [
40067         "South Sudan (‫جنوب السودان‬‎)",
40068         "ss",
40069         "211"
40070       ],
40071       [
40072         "Spain (España)",
40073         "es",
40074         "34"
40075       ],
40076       [
40077         "Sri Lanka (ශ්‍රී ලංකාව)",
40078         "lk",
40079         "94"
40080       ],
40081       [
40082         "Sudan (‫السودان‬‎)",
40083         "sd",
40084         "249"
40085       ],
40086       [
40087         "Suriname",
40088         "sr",
40089         "597"
40090       ],
40091       [
40092         "Svalbard and Jan Mayen",
40093         "sj",
40094         "47",
40095         1
40096       ],
40097       [
40098         "Swaziland",
40099         "sz",
40100         "268"
40101       ],
40102       [
40103         "Sweden (Sverige)",
40104         "se",
40105         "46"
40106       ],
40107       [
40108         "Switzerland (Schweiz)",
40109         "ch",
40110         "41"
40111       ],
40112       [
40113         "Syria (‫سوريا‬‎)",
40114         "sy",
40115         "963"
40116       ],
40117       [
40118         "Taiwan (台灣)",
40119         "tw",
40120         "886"
40121       ],
40122       [
40123         "Tajikistan",
40124         "tj",
40125         "992"
40126       ],
40127       [
40128         "Tanzania",
40129         "tz",
40130         "255"
40131       ],
40132       [
40133         "Thailand (ไทย)",
40134         "th",
40135         "66"
40136       ],
40137       [
40138         "Timor-Leste",
40139         "tl",
40140         "670"
40141       ],
40142       [
40143         "Togo",
40144         "tg",
40145         "228"
40146       ],
40147       [
40148         "Tokelau",
40149         "tk",
40150         "690"
40151       ],
40152       [
40153         "Tonga",
40154         "to",
40155         "676"
40156       ],
40157       [
40158         "Trinidad and Tobago",
40159         "tt",
40160         "1868"
40161       ],
40162       [
40163         "Tunisia (‫تونس‬‎)",
40164         "tn",
40165         "216"
40166       ],
40167       [
40168         "Turkey (Türkiye)",
40169         "tr",
40170         "90"
40171       ],
40172       [
40173         "Turkmenistan",
40174         "tm",
40175         "993"
40176       ],
40177       [
40178         "Turks and Caicos Islands",
40179         "tc",
40180         "1649"
40181       ],
40182       [
40183         "Tuvalu",
40184         "tv",
40185         "688"
40186       ],
40187       [
40188         "U.S. Virgin Islands",
40189         "vi",
40190         "1340"
40191       ],
40192       [
40193         "Uganda",
40194         "ug",
40195         "256"
40196       ],
40197       [
40198         "Ukraine (Україна)",
40199         "ua",
40200         "380"
40201       ],
40202       [
40203         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40204         "ae",
40205         "971"
40206       ],
40207       [
40208         "United Kingdom",
40209         "gb",
40210         "44",
40211         0
40212       ],
40213       [
40214         "United States",
40215         "us",
40216         "1",
40217         0
40218       ],
40219       [
40220         "Uruguay",
40221         "uy",
40222         "598"
40223       ],
40224       [
40225         "Uzbekistan (Oʻzbekiston)",
40226         "uz",
40227         "998"
40228       ],
40229       [
40230         "Vanuatu",
40231         "vu",
40232         "678"
40233       ],
40234       [
40235         "Vatican City (Città del Vaticano)",
40236         "va",
40237         "39",
40238         1
40239       ],
40240       [
40241         "Venezuela",
40242         "ve",
40243         "58"
40244       ],
40245       [
40246         "Vietnam (Việt Nam)",
40247         "vn",
40248         "84"
40249       ],
40250       [
40251         "Wallis and Futuna (Wallis-et-Futuna)",
40252         "wf",
40253         "681"
40254       ],
40255       [
40256         "Western Sahara (‫الصحراء الغربية‬‎)",
40257         "eh",
40258         "212",
40259         1
40260       ],
40261       [
40262         "Yemen (‫اليمن‬‎)",
40263         "ye",
40264         "967"
40265       ],
40266       [
40267         "Zambia",
40268         "zm",
40269         "260"
40270       ],
40271       [
40272         "Zimbabwe",
40273         "zw",
40274         "263"
40275       ],
40276       [
40277         "Åland Islands",
40278         "ax",
40279         "358",
40280         1
40281       ]
40282   ];
40283   
40284   return d;
40285 }/**
40286 *    This script refer to:
40287 *    Title: International Telephone Input
40288 *    Author: Jack O'Connor
40289 *    Code version:  v12.1.12
40290 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40291 **/
40292
40293 /**
40294  * @class Roo.bootstrap.PhoneInput
40295  * @extends Roo.bootstrap.TriggerField
40296  * An input with International dial-code selection
40297  
40298  * @cfg {String} defaultDialCode default '+852'
40299  * @cfg {Array} preferedCountries default []
40300   
40301  * @constructor
40302  * Create a new PhoneInput.
40303  * @param {Object} config Configuration options
40304  */
40305
40306 Roo.bootstrap.PhoneInput = function(config) {
40307     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40308 };
40309
40310 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40311         
40312         listWidth: undefined,
40313         
40314         selectedClass: 'active',
40315         
40316         invalidClass : "has-warning",
40317         
40318         validClass: 'has-success',
40319         
40320         allowed: '0123456789',
40321         
40322         max_length: 15,
40323         
40324         /**
40325          * @cfg {String} defaultDialCode The default dial code when initializing the input
40326          */
40327         defaultDialCode: '+852',
40328         
40329         /**
40330          * @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
40331          */
40332         preferedCountries: false,
40333         
40334         getAutoCreate : function()
40335         {
40336             var data = Roo.bootstrap.PhoneInputData();
40337             var align = this.labelAlign || this.parentLabelAlign();
40338             var id = Roo.id();
40339             
40340             this.allCountries = [];
40341             this.dialCodeMapping = [];
40342             
40343             for (var i = 0; i < data.length; i++) {
40344               var c = data[i];
40345               this.allCountries[i] = {
40346                 name: c[0],
40347                 iso2: c[1],
40348                 dialCode: c[2],
40349                 priority: c[3] || 0,
40350                 areaCodes: c[4] || null
40351               };
40352               this.dialCodeMapping[c[2]] = {
40353                   name: c[0],
40354                   iso2: c[1],
40355                   priority: c[3] || 0,
40356                   areaCodes: c[4] || null
40357               };
40358             }
40359             
40360             var cfg = {
40361                 cls: 'form-group',
40362                 cn: []
40363             };
40364             
40365             var input =  {
40366                 tag: 'input',
40367                 id : id,
40368                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40369                 maxlength: this.max_length,
40370                 cls : 'form-control tel-input',
40371                 autocomplete: 'new-password'
40372             };
40373             
40374             var hiddenInput = {
40375                 tag: 'input',
40376                 type: 'hidden',
40377                 cls: 'hidden-tel-input'
40378             };
40379             
40380             if (this.name) {
40381                 hiddenInput.name = this.name;
40382             }
40383             
40384             if (this.disabled) {
40385                 input.disabled = true;
40386             }
40387             
40388             var flag_container = {
40389                 tag: 'div',
40390                 cls: 'flag-box',
40391                 cn: [
40392                     {
40393                         tag: 'div',
40394                         cls: 'flag'
40395                     },
40396                     {
40397                         tag: 'div',
40398                         cls: 'caret'
40399                     }
40400                 ]
40401             };
40402             
40403             var box = {
40404                 tag: 'div',
40405                 cls: this.hasFeedback ? 'has-feedback' : '',
40406                 cn: [
40407                     hiddenInput,
40408                     input,
40409                     {
40410                         tag: 'input',
40411                         cls: 'dial-code-holder',
40412                         disabled: true
40413                     }
40414                 ]
40415             };
40416             
40417             var container = {
40418                 cls: 'roo-select2-container input-group',
40419                 cn: [
40420                     flag_container,
40421                     box
40422                 ]
40423             };
40424             
40425             if (this.fieldLabel.length) {
40426                 var indicator = {
40427                     tag: 'i',
40428                     tooltip: 'This field is required'
40429                 };
40430                 
40431                 var label = {
40432                     tag: 'label',
40433                     'for':  id,
40434                     cls: 'control-label',
40435                     cn: []
40436                 };
40437                 
40438                 var label_text = {
40439                     tag: 'span',
40440                     html: this.fieldLabel
40441                 };
40442                 
40443                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40444                 label.cn = [
40445                     indicator,
40446                     label_text
40447                 ];
40448                 
40449                 if(this.indicatorpos == 'right') {
40450                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40451                     label.cn = [
40452                         label_text,
40453                         indicator
40454                     ];
40455                 }
40456                 
40457                 if(align == 'left') {
40458                     container = {
40459                         tag: 'div',
40460                         cn: [
40461                             container
40462                         ]
40463                     };
40464                     
40465                     if(this.labelWidth > 12){
40466                         label.style = "width: " + this.labelWidth + 'px';
40467                     }
40468                     if(this.labelWidth < 13 && this.labelmd == 0){
40469                         this.labelmd = this.labelWidth;
40470                     }
40471                     if(this.labellg > 0){
40472                         label.cls += ' col-lg-' + this.labellg;
40473                         input.cls += ' col-lg-' + (12 - this.labellg);
40474                     }
40475                     if(this.labelmd > 0){
40476                         label.cls += ' col-md-' + this.labelmd;
40477                         container.cls += ' col-md-' + (12 - this.labelmd);
40478                     }
40479                     if(this.labelsm > 0){
40480                         label.cls += ' col-sm-' + this.labelsm;
40481                         container.cls += ' col-sm-' + (12 - this.labelsm);
40482                     }
40483                     if(this.labelxs > 0){
40484                         label.cls += ' col-xs-' + this.labelxs;
40485                         container.cls += ' col-xs-' + (12 - this.labelxs);
40486                     }
40487                 }
40488             }
40489             
40490             cfg.cn = [
40491                 label,
40492                 container
40493             ];
40494             
40495             var settings = this;
40496             
40497             ['xs','sm','md','lg'].map(function(size){
40498                 if (settings[size]) {
40499                     cfg.cls += ' col-' + size + '-' + settings[size];
40500                 }
40501             });
40502             
40503             this.store = new Roo.data.Store({
40504                 proxy : new Roo.data.MemoryProxy({}),
40505                 reader : new Roo.data.JsonReader({
40506                     fields : [
40507                         {
40508                             'name' : 'name',
40509                             'type' : 'string'
40510                         },
40511                         {
40512                             'name' : 'iso2',
40513                             'type' : 'string'
40514                         },
40515                         {
40516                             'name' : 'dialCode',
40517                             'type' : 'string'
40518                         },
40519                         {
40520                             'name' : 'priority',
40521                             'type' : 'string'
40522                         },
40523                         {
40524                             'name' : 'areaCodes',
40525                             'type' : 'string'
40526                         }
40527                     ]
40528                 })
40529             });
40530             
40531             if(!this.preferedCountries) {
40532                 this.preferedCountries = [
40533                     'hk',
40534                     'gb',
40535                     'us'
40536                 ];
40537             }
40538             
40539             var p = this.preferedCountries.reverse();
40540             
40541             if(p) {
40542                 for (var i = 0; i < p.length; i++) {
40543                     for (var j = 0; j < this.allCountries.length; j++) {
40544                         if(this.allCountries[j].iso2 == p[i]) {
40545                             var t = this.allCountries[j];
40546                             this.allCountries.splice(j,1);
40547                             this.allCountries.unshift(t);
40548                         }
40549                     } 
40550                 }
40551             }
40552             
40553             this.store.proxy.data = {
40554                 success: true,
40555                 data: this.allCountries
40556             };
40557             
40558             return cfg;
40559         },
40560         
40561         initEvents : function()
40562         {
40563             this.createList();
40564             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40565             
40566             this.indicator = this.indicatorEl();
40567             this.flag = this.flagEl();
40568             this.dialCodeHolder = this.dialCodeHolderEl();
40569             
40570             this.trigger = this.el.select('div.flag-box',true).first();
40571             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40572             
40573             var _this = this;
40574             
40575             (function(){
40576                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40577                 _this.list.setWidth(lw);
40578             }).defer(100);
40579             
40580             this.list.on('mouseover', this.onViewOver, this);
40581             this.list.on('mousemove', this.onViewMove, this);
40582             this.inputEl().on("keyup", this.onKeyUp, this);
40583             this.inputEl().on("keypress", this.onKeyPress, this);
40584             
40585             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40586
40587             this.view = new Roo.View(this.list, this.tpl, {
40588                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40589             });
40590             
40591             this.view.on('click', this.onViewClick, this);
40592             this.setValue(this.defaultDialCode);
40593         },
40594         
40595         onTriggerClick : function(e)
40596         {
40597             Roo.log('trigger click');
40598             if(this.disabled){
40599                 return;
40600             }
40601             
40602             if(this.isExpanded()){
40603                 this.collapse();
40604                 this.hasFocus = false;
40605             }else {
40606                 this.store.load({});
40607                 this.hasFocus = true;
40608                 this.expand();
40609             }
40610         },
40611         
40612         isExpanded : function()
40613         {
40614             return this.list.isVisible();
40615         },
40616         
40617         collapse : function()
40618         {
40619             if(!this.isExpanded()){
40620                 return;
40621             }
40622             this.list.hide();
40623             Roo.get(document).un('mousedown', this.collapseIf, this);
40624             Roo.get(document).un('mousewheel', this.collapseIf, this);
40625             this.fireEvent('collapse', this);
40626             this.validate();
40627         },
40628         
40629         expand : function()
40630         {
40631             Roo.log('expand');
40632
40633             if(this.isExpanded() || !this.hasFocus){
40634                 return;
40635             }
40636             
40637             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40638             this.list.setWidth(lw);
40639             
40640             this.list.show();
40641             this.restrictHeight();
40642             
40643             Roo.get(document).on('mousedown', this.collapseIf, this);
40644             Roo.get(document).on('mousewheel', this.collapseIf, this);
40645             
40646             this.fireEvent('expand', this);
40647         },
40648         
40649         restrictHeight : function()
40650         {
40651             this.list.alignTo(this.inputEl(), this.listAlign);
40652             this.list.alignTo(this.inputEl(), this.listAlign);
40653         },
40654         
40655         onViewOver : function(e, t)
40656         {
40657             if(this.inKeyMode){
40658                 return;
40659             }
40660             var item = this.view.findItemFromChild(t);
40661             
40662             if(item){
40663                 var index = this.view.indexOf(item);
40664                 this.select(index, false);
40665             }
40666         },
40667
40668         // private
40669         onViewClick : function(view, doFocus, el, e)
40670         {
40671             var index = this.view.getSelectedIndexes()[0];
40672             
40673             var r = this.store.getAt(index);
40674             
40675             if(r){
40676                 this.onSelect(r, index);
40677             }
40678             if(doFocus !== false && !this.blockFocus){
40679                 this.inputEl().focus();
40680             }
40681         },
40682         
40683         onViewMove : function(e, t)
40684         {
40685             this.inKeyMode = false;
40686         },
40687         
40688         select : function(index, scrollIntoView)
40689         {
40690             this.selectedIndex = index;
40691             this.view.select(index);
40692             if(scrollIntoView !== false){
40693                 var el = this.view.getNode(index);
40694                 if(el){
40695                     this.list.scrollChildIntoView(el, false);
40696                 }
40697             }
40698         },
40699         
40700         createList : function()
40701         {
40702             this.list = Roo.get(document.body).createChild({
40703                 tag: 'ul',
40704                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40705                 style: 'display:none'
40706             });
40707             
40708             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40709         },
40710         
40711         collapseIf : function(e)
40712         {
40713             var in_combo  = e.within(this.el);
40714             var in_list =  e.within(this.list);
40715             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40716             
40717             if (in_combo || in_list || is_list) {
40718                 return;
40719             }
40720             this.collapse();
40721         },
40722         
40723         onSelect : function(record, index)
40724         {
40725             if(this.fireEvent('beforeselect', this, record, index) !== false){
40726                 
40727                 this.setFlagClass(record.data.iso2);
40728                 this.setDialCode(record.data.dialCode);
40729                 this.hasFocus = false;
40730                 this.collapse();
40731                 this.fireEvent('select', this, record, index);
40732             }
40733         },
40734         
40735         flagEl : function()
40736         {
40737             var flag = this.el.select('div.flag',true).first();
40738             if(!flag){
40739                 return false;
40740             }
40741             return flag;
40742         },
40743         
40744         dialCodeHolderEl : function()
40745         {
40746             var d = this.el.select('input.dial-code-holder',true).first();
40747             if(!d){
40748                 return false;
40749             }
40750             return d;
40751         },
40752         
40753         setDialCode : function(v)
40754         {
40755             this.dialCodeHolder.dom.value = '+'+v;
40756         },
40757         
40758         setFlagClass : function(n)
40759         {
40760             this.flag.dom.className = 'flag '+n;
40761         },
40762         
40763         getValue : function()
40764         {
40765             var v = this.inputEl().getValue();
40766             if(this.dialCodeHolder) {
40767                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40768             }
40769             return v;
40770         },
40771         
40772         setValue : function(v)
40773         {
40774             var d = this.getDialCode(v);
40775             
40776             //invalid dial code
40777             if(v.length == 0 || !d || d.length == 0) {
40778                 if(this.rendered){
40779                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40780                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40781                 }
40782                 return;
40783             }
40784             
40785             //valid dial code
40786             this.setFlagClass(this.dialCodeMapping[d].iso2);
40787             this.setDialCode(d);
40788             this.inputEl().dom.value = v.replace('+'+d,'');
40789             this.hiddenEl().dom.value = this.getValue();
40790             
40791             this.validate();
40792         },
40793         
40794         getDialCode : function(v)
40795         {
40796             v = v ||  '';
40797             
40798             if (v.length == 0) {
40799                 return this.dialCodeHolder.dom.value;
40800             }
40801             
40802             var dialCode = "";
40803             if (v.charAt(0) != "+") {
40804                 return false;
40805             }
40806             var numericChars = "";
40807             for (var i = 1; i < v.length; i++) {
40808               var c = v.charAt(i);
40809               if (!isNaN(c)) {
40810                 numericChars += c;
40811                 if (this.dialCodeMapping[numericChars]) {
40812                   dialCode = v.substr(1, i);
40813                 }
40814                 if (numericChars.length == 4) {
40815                   break;
40816                 }
40817               }
40818             }
40819             return dialCode;
40820         },
40821         
40822         reset : function()
40823         {
40824             this.setValue(this.defaultDialCode);
40825             this.validate();
40826         },
40827         
40828         hiddenEl : function()
40829         {
40830             return this.el.select('input.hidden-tel-input',true).first();
40831         },
40832         
40833         // after setting val
40834         onKeyUp : function(e){
40835             this.setValue(this.getValue());
40836         },
40837         
40838         onKeyPress : function(e){
40839             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40840                 e.stopEvent();
40841             }
40842         }
40843         
40844 });
40845 /**
40846  * @class Roo.bootstrap.MoneyField
40847  * @extends Roo.bootstrap.ComboBox
40848  * Bootstrap MoneyField class
40849  * 
40850  * @constructor
40851  * Create a new MoneyField.
40852  * @param {Object} config Configuration options
40853  */
40854
40855 Roo.bootstrap.MoneyField = function(config) {
40856     
40857     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40858     
40859 };
40860
40861 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40862     
40863     /**
40864      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40865      */
40866     allowDecimals : true,
40867     /**
40868      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40869      */
40870     decimalSeparator : ".",
40871     /**
40872      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40873      */
40874     decimalPrecision : 0,
40875     /**
40876      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40877      */
40878     allowNegative : true,
40879     /**
40880      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40881      */
40882     allowZero: true,
40883     /**
40884      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40885      */
40886     minValue : Number.NEGATIVE_INFINITY,
40887     /**
40888      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40889      */
40890     maxValue : Number.MAX_VALUE,
40891     /**
40892      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40893      */
40894     minText : "The minimum value for this field is {0}",
40895     /**
40896      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40897      */
40898     maxText : "The maximum value for this field is {0}",
40899     /**
40900      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40901      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40902      */
40903     nanText : "{0} is not a valid number",
40904     /**
40905      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40906      */
40907     castInt : true,
40908     /**
40909      * @cfg {String} defaults currency of the MoneyField
40910      * value should be in lkey
40911      */
40912     defaultCurrency : false,
40913     /**
40914      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40915      */
40916     thousandsDelimiter : false,
40917     /**
40918      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40919      */
40920     max_length: false,
40921     
40922     inputlg : 9,
40923     inputmd : 9,
40924     inputsm : 9,
40925     inputxs : 6,
40926     
40927     store : false,
40928     
40929     getAutoCreate : function()
40930     {
40931         var align = this.labelAlign || this.parentLabelAlign();
40932         
40933         var id = Roo.id();
40934
40935         var cfg = {
40936             cls: 'form-group',
40937             cn: []
40938         };
40939
40940         var input =  {
40941             tag: 'input',
40942             id : id,
40943             cls : 'form-control roo-money-amount-input',
40944             autocomplete: 'new-password'
40945         };
40946         
40947         var hiddenInput = {
40948             tag: 'input',
40949             type: 'hidden',
40950             id: Roo.id(),
40951             cls: 'hidden-number-input'
40952         };
40953         
40954         if(this.max_length) {
40955             input.maxlength = this.max_length; 
40956         }
40957         
40958         if (this.name) {
40959             hiddenInput.name = this.name;
40960         }
40961
40962         if (this.disabled) {
40963             input.disabled = true;
40964         }
40965
40966         var clg = 12 - this.inputlg;
40967         var cmd = 12 - this.inputmd;
40968         var csm = 12 - this.inputsm;
40969         var cxs = 12 - this.inputxs;
40970         
40971         var container = {
40972             tag : 'div',
40973             cls : 'row roo-money-field',
40974             cn : [
40975                 {
40976                     tag : 'div',
40977                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40978                     cn : [
40979                         {
40980                             tag : 'div',
40981                             cls: 'roo-select2-container input-group',
40982                             cn: [
40983                                 {
40984                                     tag : 'input',
40985                                     cls : 'form-control roo-money-currency-input',
40986                                     autocomplete: 'new-password',
40987                                     readOnly : 1,
40988                                     name : this.currencyName
40989                                 },
40990                                 {
40991                                     tag :'span',
40992                                     cls : 'input-group-addon',
40993                                     cn : [
40994                                         {
40995                                             tag: 'span',
40996                                             cls: 'caret'
40997                                         }
40998                                     ]
40999                                 }
41000                             ]
41001                         }
41002                     ]
41003                 },
41004                 {
41005                     tag : 'div',
41006                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41007                     cn : [
41008                         {
41009                             tag: 'div',
41010                             cls: this.hasFeedback ? 'has-feedback' : '',
41011                             cn: [
41012                                 input
41013                             ]
41014                         }
41015                     ]
41016                 }
41017             ]
41018             
41019         };
41020         
41021         if (this.fieldLabel.length) {
41022             var indicator = {
41023                 tag: 'i',
41024                 tooltip: 'This field is required'
41025             };
41026
41027             var label = {
41028                 tag: 'label',
41029                 'for':  id,
41030                 cls: 'control-label',
41031                 cn: []
41032             };
41033
41034             var label_text = {
41035                 tag: 'span',
41036                 html: this.fieldLabel
41037             };
41038
41039             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41040             label.cn = [
41041                 indicator,
41042                 label_text
41043             ];
41044
41045             if(this.indicatorpos == 'right') {
41046                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41047                 label.cn = [
41048                     label_text,
41049                     indicator
41050                 ];
41051             }
41052
41053             if(align == 'left') {
41054                 container = {
41055                     tag: 'div',
41056                     cn: [
41057                         container
41058                     ]
41059                 };
41060
41061                 if(this.labelWidth > 12){
41062                     label.style = "width: " + this.labelWidth + 'px';
41063                 }
41064                 if(this.labelWidth < 13 && this.labelmd == 0){
41065                     this.labelmd = this.labelWidth;
41066                 }
41067                 if(this.labellg > 0){
41068                     label.cls += ' col-lg-' + this.labellg;
41069                     input.cls += ' col-lg-' + (12 - this.labellg);
41070                 }
41071                 if(this.labelmd > 0){
41072                     label.cls += ' col-md-' + this.labelmd;
41073                     container.cls += ' col-md-' + (12 - this.labelmd);
41074                 }
41075                 if(this.labelsm > 0){
41076                     label.cls += ' col-sm-' + this.labelsm;
41077                     container.cls += ' col-sm-' + (12 - this.labelsm);
41078                 }
41079                 if(this.labelxs > 0){
41080                     label.cls += ' col-xs-' + this.labelxs;
41081                     container.cls += ' col-xs-' + (12 - this.labelxs);
41082                 }
41083             }
41084         }
41085
41086         cfg.cn = [
41087             label,
41088             container,
41089             hiddenInput
41090         ];
41091         
41092         var settings = this;
41093
41094         ['xs','sm','md','lg'].map(function(size){
41095             if (settings[size]) {
41096                 cfg.cls += ' col-' + size + '-' + settings[size];
41097             }
41098         });
41099         
41100         return cfg;
41101     },
41102     
41103     initEvents : function()
41104     {
41105         this.indicator = this.indicatorEl();
41106         
41107         this.initCurrencyEvent();
41108         
41109         this.initNumberEvent();
41110     },
41111     
41112     initCurrencyEvent : function()
41113     {
41114         if (!this.store) {
41115             throw "can not find store for combo";
41116         }
41117         
41118         this.store = Roo.factory(this.store, Roo.data);
41119         this.store.parent = this;
41120         
41121         this.createList();
41122         
41123         this.triggerEl = this.el.select('.input-group-addon', true).first();
41124         
41125         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41126         
41127         var _this = this;
41128         
41129         (function(){
41130             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41131             _this.list.setWidth(lw);
41132         }).defer(100);
41133         
41134         this.list.on('mouseover', this.onViewOver, this);
41135         this.list.on('mousemove', this.onViewMove, this);
41136         this.list.on('scroll', this.onViewScroll, this);
41137         
41138         if(!this.tpl){
41139             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41140         }
41141         
41142         this.view = new Roo.View(this.list, this.tpl, {
41143             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41144         });
41145         
41146         this.view.on('click', this.onViewClick, this);
41147         
41148         this.store.on('beforeload', this.onBeforeLoad, this);
41149         this.store.on('load', this.onLoad, this);
41150         this.store.on('loadexception', this.onLoadException, this);
41151         
41152         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41153             "up" : function(e){
41154                 this.inKeyMode = true;
41155                 this.selectPrev();
41156             },
41157
41158             "down" : function(e){
41159                 if(!this.isExpanded()){
41160                     this.onTriggerClick();
41161                 }else{
41162                     this.inKeyMode = true;
41163                     this.selectNext();
41164                 }
41165             },
41166
41167             "enter" : function(e){
41168                 this.collapse();
41169                 
41170                 if(this.fireEvent("specialkey", this, e)){
41171                     this.onViewClick(false);
41172                 }
41173                 
41174                 return true;
41175             },
41176
41177             "esc" : function(e){
41178                 this.collapse();
41179             },
41180
41181             "tab" : function(e){
41182                 this.collapse();
41183                 
41184                 if(this.fireEvent("specialkey", this, e)){
41185                     this.onViewClick(false);
41186                 }
41187                 
41188                 return true;
41189             },
41190
41191             scope : this,
41192
41193             doRelay : function(foo, bar, hname){
41194                 if(hname == 'down' || this.scope.isExpanded()){
41195                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41196                 }
41197                 return true;
41198             },
41199
41200             forceKeyDown: true
41201         });
41202         
41203         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41204         
41205     },
41206     
41207     initNumberEvent : function(e)
41208     {
41209         this.inputEl().on("keydown" , this.fireKey,  this);
41210         this.inputEl().on("focus", this.onFocus,  this);
41211         this.inputEl().on("blur", this.onBlur,  this);
41212         
41213         this.inputEl().relayEvent('keyup', this);
41214         
41215         if(this.indicator){
41216             this.indicator.addClass('invisible');
41217         }
41218  
41219         this.originalValue = this.getValue();
41220         
41221         if(this.validationEvent == 'keyup'){
41222             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41223             this.inputEl().on('keyup', this.filterValidation, this);
41224         }
41225         else if(this.validationEvent !== false){
41226             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41227         }
41228         
41229         if(this.selectOnFocus){
41230             this.on("focus", this.preFocus, this);
41231             
41232         }
41233         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41234             this.inputEl().on("keypress", this.filterKeys, this);
41235         } else {
41236             this.inputEl().relayEvent('keypress', this);
41237         }
41238         
41239         var allowed = "0123456789";
41240         
41241         if(this.allowDecimals){
41242             allowed += this.decimalSeparator;
41243         }
41244         
41245         if(this.allowNegative){
41246             allowed += "-";
41247         }
41248         
41249         if(this.thousandsDelimiter) {
41250             allowed += ",";
41251         }
41252         
41253         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41254         
41255         var keyPress = function(e){
41256             
41257             var k = e.getKey();
41258             
41259             var c = e.getCharCode();
41260             
41261             if(
41262                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41263                     allowed.indexOf(String.fromCharCode(c)) === -1
41264             ){
41265                 e.stopEvent();
41266                 return;
41267             }
41268             
41269             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41270                 return;
41271             }
41272             
41273             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41274                 e.stopEvent();
41275             }
41276         };
41277         
41278         this.inputEl().on("keypress", keyPress, this);
41279         
41280     },
41281     
41282     onTriggerClick : function(e)
41283     {   
41284         if(this.disabled){
41285             return;
41286         }
41287         
41288         this.page = 0;
41289         this.loadNext = false;
41290         
41291         if(this.isExpanded()){
41292             this.collapse();
41293             return;
41294         }
41295         
41296         this.hasFocus = true;
41297         
41298         if(this.triggerAction == 'all') {
41299             this.doQuery(this.allQuery, true);
41300             return;
41301         }
41302         
41303         this.doQuery(this.getRawValue());
41304     },
41305     
41306     getCurrency : function()
41307     {   
41308         var v = this.currencyEl().getValue();
41309         
41310         return v;
41311     },
41312     
41313     restrictHeight : function()
41314     {
41315         this.list.alignTo(this.currencyEl(), this.listAlign);
41316         this.list.alignTo(this.currencyEl(), this.listAlign);
41317     },
41318     
41319     onViewClick : function(view, doFocus, el, e)
41320     {
41321         var index = this.view.getSelectedIndexes()[0];
41322         
41323         var r = this.store.getAt(index);
41324         
41325         if(r){
41326             this.onSelect(r, index);
41327         }
41328     },
41329     
41330     onSelect : function(record, index){
41331         
41332         if(this.fireEvent('beforeselect', this, record, index) !== false){
41333         
41334             this.setFromCurrencyData(index > -1 ? record.data : false);
41335             
41336             this.collapse();
41337             
41338             this.fireEvent('select', this, record, index);
41339         }
41340     },
41341     
41342     setFromCurrencyData : function(o)
41343     {
41344         var currency = '';
41345         
41346         this.lastCurrency = o;
41347         
41348         if (this.currencyField) {
41349             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41350         } else {
41351             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41352         }
41353         
41354         this.lastSelectionText = currency;
41355         
41356         //setting default currency
41357         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41358             this.setCurrency(this.defaultCurrency);
41359             return;
41360         }
41361         
41362         this.setCurrency(currency);
41363     },
41364     
41365     setFromData : function(o)
41366     {
41367         var c = {};
41368         
41369         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41370         
41371         this.setFromCurrencyData(c);
41372         
41373         var value = '';
41374         
41375         if (this.name) {
41376             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41377         } else {
41378             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41379         }
41380         
41381         this.setValue(value);
41382         
41383     },
41384     
41385     setCurrency : function(v)
41386     {   
41387         this.currencyValue = v;
41388         
41389         if(this.rendered){
41390             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41391             this.validate();
41392         }
41393     },
41394     
41395     setValue : function(v)
41396     {
41397         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41398         
41399         this.value = v;
41400         
41401         if(this.rendered){
41402             
41403             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41404             
41405             this.inputEl().dom.value = (v == '') ? '' :
41406                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41407             
41408             if(!this.allowZero && v === '0') {
41409                 this.hiddenEl().dom.value = '';
41410                 this.inputEl().dom.value = '';
41411             }
41412             
41413             this.validate();
41414         }
41415     },
41416     
41417     getRawValue : function()
41418     {
41419         var v = this.inputEl().getValue();
41420         
41421         return v;
41422     },
41423     
41424     getValue : function()
41425     {
41426         return this.fixPrecision(this.parseValue(this.getRawValue()));
41427     },
41428     
41429     parseValue : function(value)
41430     {
41431         if(this.thousandsDelimiter) {
41432             value += "";
41433             r = new RegExp(",", "g");
41434             value = value.replace(r, "");
41435         }
41436         
41437         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41438         return isNaN(value) ? '' : value;
41439         
41440     },
41441     
41442     fixPrecision : function(value)
41443     {
41444         if(this.thousandsDelimiter) {
41445             value += "";
41446             r = new RegExp(",", "g");
41447             value = value.replace(r, "");
41448         }
41449         
41450         var nan = isNaN(value);
41451         
41452         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41453             return nan ? '' : value;
41454         }
41455         return parseFloat(value).toFixed(this.decimalPrecision);
41456     },
41457     
41458     decimalPrecisionFcn : function(v)
41459     {
41460         return Math.floor(v);
41461     },
41462     
41463     validateValue : function(value)
41464     {
41465         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41466             return false;
41467         }
41468         
41469         var num = this.parseValue(value);
41470         
41471         if(isNaN(num)){
41472             this.markInvalid(String.format(this.nanText, value));
41473             return false;
41474         }
41475         
41476         if(num < this.minValue){
41477             this.markInvalid(String.format(this.minText, this.minValue));
41478             return false;
41479         }
41480         
41481         if(num > this.maxValue){
41482             this.markInvalid(String.format(this.maxText, this.maxValue));
41483             return false;
41484         }
41485         
41486         return true;
41487     },
41488     
41489     validate : function()
41490     {
41491         if(this.disabled || this.allowBlank){
41492             this.markValid();
41493             return true;
41494         }
41495         
41496         var currency = this.getCurrency();
41497         
41498         if(this.validateValue(this.getRawValue()) && currency.length){
41499             this.markValid();
41500             return true;
41501         }
41502         
41503         this.markInvalid();
41504         return false;
41505     },
41506     
41507     getName: function()
41508     {
41509         return this.name;
41510     },
41511     
41512     beforeBlur : function()
41513     {
41514         if(!this.castInt){
41515             return;
41516         }
41517         
41518         var v = this.parseValue(this.getRawValue());
41519         
41520         if(v || v == 0){
41521             this.setValue(v);
41522         }
41523     },
41524     
41525     onBlur : function()
41526     {
41527         this.beforeBlur();
41528         
41529         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41530             //this.el.removeClass(this.focusClass);
41531         }
41532         
41533         this.hasFocus = false;
41534         
41535         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41536             this.validate();
41537         }
41538         
41539         var v = this.getValue();
41540         
41541         if(String(v) !== String(this.startValue)){
41542             this.fireEvent('change', this, v, this.startValue);
41543         }
41544         
41545         this.fireEvent("blur", this);
41546     },
41547     
41548     inputEl : function()
41549     {
41550         return this.el.select('.roo-money-amount-input', true).first();
41551     },
41552     
41553     currencyEl : function()
41554     {
41555         return this.el.select('.roo-money-currency-input', true).first();
41556     },
41557     
41558     hiddenEl : function()
41559     {
41560         return this.el.select('input.hidden-number-input',true).first();
41561     }
41562     
41563 });/**
41564  * @class Roo.bootstrap.BezierSignature
41565  * @extends Roo.bootstrap.Component
41566  * Bootstrap BezierSignature class
41567  * This script refer to:
41568  *    Title: Signature Pad
41569  *    Author: szimek
41570  *    Availability: https://github.com/szimek/signature_pad
41571  *
41572  * @constructor
41573  * Create a new BezierSignature
41574  * @param {Object} config The config object
41575  */
41576
41577 Roo.bootstrap.BezierSignature = function(config){
41578     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41579     this.addEvents({
41580         "resize" : true
41581     });
41582 };
41583
41584 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41585 {
41586      
41587     curve_data: [],
41588     
41589     is_empty: true,
41590     
41591     mouse_btn_down: true,
41592     
41593     /**
41594      * @cfg {int} canvas height
41595      */
41596     canvas_height: '200px',
41597     
41598     /**
41599      * @cfg {float|function} Radius of a single dot.
41600      */ 
41601     dot_size: false,
41602     
41603     /**
41604      * @cfg {float} Minimum width of a line. Defaults to 0.5.
41605      */
41606     min_width: 0.5,
41607     
41608     /**
41609      * @cfg {float} Maximum width of a line. Defaults to 2.5.
41610      */
41611     max_width: 2.5,
41612     
41613     /**
41614      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41615      */
41616     throttle: 16,
41617     
41618     /**
41619      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41620      */
41621     min_distance: 5,
41622     
41623     /**
41624      * @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.
41625      */
41626     bg_color: 'rgba(0, 0, 0, 0)',
41627     
41628     /**
41629      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41630      */
41631     dot_color: 'black',
41632     
41633     /**
41634      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41635      */ 
41636     velocity_filter_weight: 0.7,
41637     
41638     /**
41639      * @cfg {function} Callback when stroke begin. 
41640      */
41641     onBegin: false,
41642     
41643     /**
41644      * @cfg {function} Callback when stroke end.
41645      */
41646     onEnd: false,
41647     
41648     getAutoCreate : function()
41649     {
41650         var cls = 'roo-signature column';
41651         
41652         if(this.cls){
41653             cls += ' ' + this.cls;
41654         }
41655         
41656         var col_sizes = [
41657             'lg',
41658             'md',
41659             'sm',
41660             'xs'
41661         ];
41662         
41663         for(var i = 0; i < col_sizes.length; i++) {
41664             if(this[col_sizes[i]]) {
41665                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41666             }
41667         }
41668         
41669         var cfg = {
41670             tag: 'div',
41671             cls: cls,
41672             cn: [
41673                 {
41674                     tag: 'div',
41675                     cls: 'roo-signature-body',
41676                     cn: [
41677                         {
41678                             tag: 'canvas',
41679                             cls: 'roo-signature-body-canvas',
41680                             height: this.canvas_height,
41681                             width: this.canvas_width
41682                         }
41683                     ]
41684                 },
41685                 {
41686                     tag: 'input',
41687                     type: 'file',
41688                     style: 'display: none'
41689                 }
41690             ]
41691         };
41692         
41693         return cfg;
41694     },
41695     
41696     initEvents: function() 
41697     {
41698         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41699         
41700         var canvas = this.canvasEl();
41701         
41702         // mouse && touch event swapping...
41703         canvas.dom.style.touchAction = 'none';
41704         canvas.dom.style.msTouchAction = 'none';
41705         
41706         this.mouse_btn_down = false;
41707         canvas.on('mousedown', this._handleMouseDown, this);
41708         canvas.on('mousemove', this._handleMouseMove, this);
41709         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41710         
41711         if (window.PointerEvent) {
41712             canvas.on('pointerdown', this._handleMouseDown, this);
41713             canvas.on('pointermove', this._handleMouseMove, this);
41714             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41715         }
41716         
41717         if ('ontouchstart' in window) {
41718             canvas.on('touchstart', this._handleTouchStart, this);
41719             canvas.on('touchmove', this._handleTouchMove, this);
41720             canvas.on('touchend', this._handleTouchEnd, this);
41721         }
41722         
41723         Roo.EventManager.onWindowResize(this.resize, this, true);
41724         
41725         // file input event
41726         this.fileEl().on('change', this.uploadImage, this);
41727         
41728         this.clear();
41729         
41730         this.resize();
41731     },
41732     
41733     resize: function(){
41734         
41735         var canvas = this.canvasEl().dom;
41736         var ctx = this.canvasElCtx();
41737         var img_data = false;
41738         
41739         if(canvas.width > 0) {
41740             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41741         }
41742         // setting canvas width will clean img data
41743         canvas.width = 0;
41744         
41745         var style = window.getComputedStyle ? 
41746             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41747             
41748         var padding_left = parseInt(style.paddingLeft) || 0;
41749         var padding_right = parseInt(style.paddingRight) || 0;
41750         
41751         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41752         
41753         if(img_data) {
41754             ctx.putImageData(img_data, 0, 0);
41755         }
41756     },
41757     
41758     _handleMouseDown: function(e)
41759     {
41760         if (e.browserEvent.which === 1) {
41761             this.mouse_btn_down = true;
41762             this.strokeBegin(e);
41763         }
41764     },
41765     
41766     _handleMouseMove: function (e)
41767     {
41768         if (this.mouse_btn_down) {
41769             this.strokeMoveUpdate(e);
41770         }
41771     },
41772     
41773     _handleMouseUp: function (e)
41774     {
41775         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41776             this.mouse_btn_down = false;
41777             this.strokeEnd(e);
41778         }
41779     },
41780     
41781     _handleTouchStart: function (e) {
41782         
41783         e.preventDefault();
41784         if (e.browserEvent.targetTouches.length === 1) {
41785             // var touch = e.browserEvent.changedTouches[0];
41786             // this.strokeBegin(touch);
41787             
41788              this.strokeBegin(e); // assume e catching the correct xy...
41789         }
41790     },
41791     
41792     _handleTouchMove: function (e) {
41793         e.preventDefault();
41794         // var touch = event.targetTouches[0];
41795         // _this._strokeMoveUpdate(touch);
41796         this.strokeMoveUpdate(e);
41797     },
41798     
41799     _handleTouchEnd: function (e) {
41800         var wasCanvasTouched = e.target === this.canvasEl().dom;
41801         if (wasCanvasTouched) {
41802             e.preventDefault();
41803             // var touch = event.changedTouches[0];
41804             // _this._strokeEnd(touch);
41805             this.strokeEnd(e);
41806         }
41807     },
41808     
41809     reset: function () {
41810         this._lastPoints = [];
41811         this._lastVelocity = 0;
41812         this._lastWidth = (this.min_width + this.max_width) / 2;
41813         this.canvasElCtx().fillStyle = this.dot_color;
41814     },
41815     
41816     strokeMoveUpdate: function(e)
41817     {
41818         this.strokeUpdate(e);
41819         
41820         if (this.throttle) {
41821             this.throttleStroke(this.strokeUpdate, this.throttle);
41822         }
41823         else {
41824             this.strokeUpdate(e);
41825         }
41826     },
41827     
41828     strokeBegin: function(e)
41829     {
41830         var newPointGroup = {
41831             color: this.dot_color,
41832             points: []
41833         };
41834         
41835         if (typeof this.onBegin === 'function') {
41836             this.onBegin(e);
41837         }
41838         
41839         this.curve_data.push(newPointGroup);
41840         this.reset();
41841         this.strokeUpdate(e);
41842     },
41843     
41844     strokeUpdate: function(e)
41845     {
41846         var rect = this.canvasEl().dom.getBoundingClientRect();
41847         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41848         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41849         var lastPoints = lastPointGroup.points;
41850         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41851         var isLastPointTooClose = lastPoint
41852             ? point.distanceTo(lastPoint) <= this.min_distance
41853             : false;
41854         var color = lastPointGroup.color;
41855         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41856             var curve = this.addPoint(point);
41857             if (!lastPoint) {
41858                 this.drawDot({color: color, point: point});
41859             }
41860             else if (curve) {
41861                 this.drawCurve({color: color, curve: curve});
41862             }
41863             lastPoints.push({
41864                 time: point.time,
41865                 x: point.x,
41866                 y: point.y
41867             });
41868         }
41869     },
41870     
41871     strokeEnd: function(e)
41872     {
41873         this.strokeUpdate(e);
41874         if (typeof this.onEnd === 'function') {
41875             this.onEnd(e);
41876         }
41877     },
41878     
41879     addPoint:  function (point) {
41880         var _lastPoints = this._lastPoints;
41881         _lastPoints.push(point);
41882         if (_lastPoints.length > 2) {
41883             if (_lastPoints.length === 3) {
41884                 _lastPoints.unshift(_lastPoints[0]);
41885             }
41886             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41887             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41888             _lastPoints.shift();
41889             return curve;
41890         }
41891         return null;
41892     },
41893     
41894     calculateCurveWidths: function (startPoint, endPoint) {
41895         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
41896             (1 - this.velocity_filter_weight) * this._lastVelocity;
41897
41898         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
41899         var widths = {
41900             end: newWidth,
41901             start: this._lastWidth
41902         };
41903         
41904         this._lastVelocity = velocity;
41905         this._lastWidth = newWidth;
41906         return widths;
41907     },
41908     
41909     drawDot: function (_a) {
41910         var color = _a.color, point = _a.point;
41911         var ctx = this.canvasElCtx();
41912         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
41913         ctx.beginPath();
41914         this.drawCurveSegment(point.x, point.y, width);
41915         ctx.closePath();
41916         ctx.fillStyle = color;
41917         ctx.fill();
41918     },
41919     
41920     drawCurve: function (_a) {
41921         var color = _a.color, curve = _a.curve;
41922         var ctx = this.canvasElCtx();
41923         var widthDelta = curve.endWidth - curve.startWidth;
41924         var drawSteps = Math.floor(curve.length()) * 2;
41925         ctx.beginPath();
41926         ctx.fillStyle = color;
41927         for (var i = 0; i < drawSteps; i += 1) {
41928         var t = i / drawSteps;
41929         var tt = t * t;
41930         var ttt = tt * t;
41931         var u = 1 - t;
41932         var uu = u * u;
41933         var uuu = uu * u;
41934         var x = uuu * curve.startPoint.x;
41935         x += 3 * uu * t * curve.control1.x;
41936         x += 3 * u * tt * curve.control2.x;
41937         x += ttt * curve.endPoint.x;
41938         var y = uuu * curve.startPoint.y;
41939         y += 3 * uu * t * curve.control1.y;
41940         y += 3 * u * tt * curve.control2.y;
41941         y += ttt * curve.endPoint.y;
41942         var width = curve.startWidth + ttt * widthDelta;
41943         this.drawCurveSegment(x, y, width);
41944         }
41945         ctx.closePath();
41946         ctx.fill();
41947     },
41948     
41949     drawCurveSegment: function (x, y, width) {
41950         var ctx = this.canvasElCtx();
41951         ctx.moveTo(x, y);
41952         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
41953         this.is_empty = false;
41954     },
41955     
41956     clear: function()
41957     {
41958         var ctx = this.canvasElCtx();
41959         var canvas = this.canvasEl().dom;
41960         ctx.fillStyle = this.bg_color;
41961         ctx.clearRect(0, 0, canvas.width, canvas.height);
41962         ctx.fillRect(0, 0, canvas.width, canvas.height);
41963         this.curve_data = [];
41964         this.reset();
41965         this.is_empty = true;
41966     },
41967     
41968     fileEl: function()
41969     {
41970         return  this.el.select('input',true).first();
41971     },
41972     
41973     canvasEl: function()
41974     {
41975         return this.el.select('canvas',true).first();
41976     },
41977     
41978     canvasElCtx: function()
41979     {
41980         return this.el.select('canvas',true).first().dom.getContext('2d');
41981     },
41982     
41983     getImage: function(type)
41984     {
41985         if(this.is_empty) {
41986             return false;
41987         }
41988         
41989         // encryption ?
41990         return this.canvasEl().dom.toDataURL('image/'+type, 1);
41991     },
41992     
41993     drawFromImage: function(img_src)
41994     {
41995         var img = new Image();
41996         
41997         img.onload = function(){
41998             this.canvasElCtx().drawImage(img, 0, 0);
41999         }.bind(this);
42000         
42001         img.src = img_src;
42002         
42003         this.is_empty = false;
42004     },
42005     
42006     selectImage: function()
42007     {
42008         this.fileEl().dom.click();
42009     },
42010     
42011     uploadImage: function(e)
42012     {
42013         var reader = new FileReader();
42014         
42015         reader.onload = function(e){
42016             var img = new Image();
42017             img.onload = function(){
42018                 this.reset();
42019                 this.canvasElCtx().drawImage(img, 0, 0);
42020             }.bind(this);
42021             img.src = e.target.result;
42022         }.bind(this);
42023         
42024         reader.readAsDataURL(e.target.files[0]);
42025     },
42026     
42027     // Bezier Point Constructor
42028     Point: (function () {
42029         function Point(x, y, time) {
42030             this.x = x;
42031             this.y = y;
42032             this.time = time || Date.now();
42033         }
42034         Point.prototype.distanceTo = function (start) {
42035             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42036         };
42037         Point.prototype.equals = function (other) {
42038             return this.x === other.x && this.y === other.y && this.time === other.time;
42039         };
42040         Point.prototype.velocityFrom = function (start) {
42041             return this.time !== start.time
42042             ? this.distanceTo(start) / (this.time - start.time)
42043             : 0;
42044         };
42045         return Point;
42046     }()),
42047     
42048     
42049     // Bezier Constructor
42050     Bezier: (function () {
42051         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42052             this.startPoint = startPoint;
42053             this.control2 = control2;
42054             this.control1 = control1;
42055             this.endPoint = endPoint;
42056             this.startWidth = startWidth;
42057             this.endWidth = endWidth;
42058         }
42059         Bezier.fromPoints = function (points, widths, scope) {
42060             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42061             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42062             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42063         };
42064         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42065             var dx1 = s1.x - s2.x;
42066             var dy1 = s1.y - s2.y;
42067             var dx2 = s2.x - s3.x;
42068             var dy2 = s2.y - s3.y;
42069             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42070             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42071             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42072             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42073             var dxm = m1.x - m2.x;
42074             var dym = m1.y - m2.y;
42075             var k = l2 / (l1 + l2);
42076             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42077             var tx = s2.x - cm.x;
42078             var ty = s2.y - cm.y;
42079             return {
42080                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42081                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42082             };
42083         };
42084         Bezier.prototype.length = function () {
42085             var steps = 10;
42086             var length = 0;
42087             var px;
42088             var py;
42089             for (var i = 0; i <= steps; i += 1) {
42090                 var t = i / steps;
42091                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42092                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42093                 if (i > 0) {
42094                     var xdiff = cx - px;
42095                     var ydiff = cy - py;
42096                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42097                 }
42098                 px = cx;
42099                 py = cy;
42100             }
42101             return length;
42102         };
42103         Bezier.prototype.point = function (t, start, c1, c2, end) {
42104             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42105             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42106             + (3.0 * c2 * (1.0 - t) * t * t)
42107             + (end * t * t * t);
42108         };
42109         return Bezier;
42110     }()),
42111     
42112     throttleStroke: function(fn, wait) {
42113       if (wait === void 0) { wait = 250; }
42114       var previous = 0;
42115       var timeout = null;
42116       var result;
42117       var storedContext;
42118       var storedArgs;
42119       var later = function () {
42120           previous = Date.now();
42121           timeout = null;
42122           result = fn.apply(storedContext, storedArgs);
42123           if (!timeout) {
42124               storedContext = null;
42125               storedArgs = [];
42126           }
42127       };
42128       return function wrapper() {
42129           var args = [];
42130           for (var _i = 0; _i < arguments.length; _i++) {
42131               args[_i] = arguments[_i];
42132           }
42133           var now = Date.now();
42134           var remaining = wait - (now - previous);
42135           storedContext = this;
42136           storedArgs = args;
42137           if (remaining <= 0 || remaining > wait) {
42138               if (timeout) {
42139                   clearTimeout(timeout);
42140                   timeout = null;
42141               }
42142               previous = now;
42143               result = fn.apply(storedContext, storedArgs);
42144               if (!timeout) {
42145                   storedContext = null;
42146                   storedArgs = [];
42147               }
42148           }
42149           else if (!timeout) {
42150               timeout = window.setTimeout(later, remaining);
42151           }
42152           return result;
42153       };
42154   }
42155   
42156 });
42157
42158  
42159
42160