Fix #6430 - messing arouds with cards BS4
[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  && 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 {Number} width fixed width - usefull for chrome extension only really.
2655  * @cfg {Number} height fixed height - usefull for chrome extension only really.
2656  * @cfg {String} size (sm|lg) default empty
2657  * @cfg {Number} max_width set the max width of modal
2658  *
2659  *
2660  * @constructor
2661  * Create a new Modal Dialog
2662  * @param {Object} config The config object
2663  */
2664
2665 Roo.bootstrap.Modal = function(config){
2666     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2667     this.addEvents({
2668         // raw events
2669         /**
2670          * @event btnclick
2671          * The raw btnclick event for the button
2672          * @param {Roo.EventObject} e
2673          */
2674         "btnclick" : true,
2675         /**
2676          * @event resize
2677          * Fire when dialog resize
2678          * @param {Roo.bootstrap.Modal} this
2679          * @param {Roo.EventObject} e
2680          */
2681         "resize" : true
2682     });
2683     this.buttons = this.buttons || [];
2684
2685     if (this.tmpl) {
2686         this.tmpl = Roo.factory(this.tmpl);
2687     }
2688
2689 };
2690
2691 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2692
2693     title : 'test dialog',
2694
2695     buttons : false,
2696
2697     // set on load...
2698
2699     html: false,
2700
2701     tmp: false,
2702
2703     specificTitle: false,
2704
2705     buttonPosition: 'right',
2706
2707     allow_close : true,
2708
2709     animate : true,
2710
2711     fitwindow: false,
2712     
2713      // private
2714     dialogEl: false,
2715     bodyEl:  false,
2716     footerEl:  false,
2717     titleEl:  false,
2718     closeEl:  false,
2719
2720     size: '',
2721     
2722     max_width: 0,
2723     
2724     max_height: 0,
2725     
2726     fit_content: false,
2727
2728     onRender : function(ct, position)
2729     {
2730         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2731
2732         if(!this.el){
2733             var cfg = Roo.apply({},  this.getAutoCreate());
2734             cfg.id = Roo.id();
2735             //if(!cfg.name){
2736             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2737             //}
2738             //if (!cfg.name.length) {
2739             //    delete cfg.name;
2740            // }
2741             if (this.cls) {
2742                 cfg.cls += ' ' + this.cls;
2743             }
2744             if (this.style) {
2745                 cfg.style = this.style;
2746             }
2747             this.el = Roo.get(document.body).createChild(cfg, position);
2748         }
2749         //var type = this.el.dom.type;
2750
2751
2752         if(this.tabIndex !== undefined){
2753             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2754         }
2755
2756         this.dialogEl = this.el.select('.modal-dialog',true).first();
2757         this.bodyEl = this.el.select('.modal-body',true).first();
2758         this.closeEl = this.el.select('.modal-header .close', true).first();
2759         this.headerEl = this.el.select('.modal-header',true).first();
2760         this.titleEl = this.el.select('.modal-title',true).first();
2761         this.footerEl = this.el.select('.modal-footer',true).first();
2762
2763         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2764         
2765         //this.el.addClass("x-dlg-modal");
2766
2767         if (this.buttons.length) {
2768             Roo.each(this.buttons, function(bb) {
2769                 var b = Roo.apply({}, bb);
2770                 b.xns = b.xns || Roo.bootstrap;
2771                 b.xtype = b.xtype || 'Button';
2772                 if (typeof(b.listeners) == 'undefined') {
2773                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2774                 }
2775
2776                 var btn = Roo.factory(b);
2777
2778                 btn.render(this.getButtonContainer());
2779
2780             },this);
2781         }
2782         // render the children.
2783         var nitems = [];
2784
2785         if(typeof(this.items) != 'undefined'){
2786             var items = this.items;
2787             delete this.items;
2788
2789             for(var i =0;i < items.length;i++) {
2790                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2791             }
2792         }
2793
2794         this.items = nitems;
2795
2796         // where are these used - they used to be body/close/footer
2797
2798
2799         this.initEvents();
2800         //this.el.addClass([this.fieldClass, this.cls]);
2801
2802     },
2803
2804     getAutoCreate : function()
2805     {
2806         // we will default to modal-body-overflow - might need to remove or make optional later.
2807         var bdy = {
2808                 cls : 'modal-body enable-modal-body-overflow ', 
2809                 html : this.html || ''
2810         };
2811
2812         var title = {
2813             tag: 'h4',
2814             cls : 'modal-title',
2815             html : this.title
2816         };
2817
2818         if(this.specificTitle){
2819             title = this.title;
2820
2821         }
2822
2823         var header = [];
2824         if (this.allow_close && Roo.bootstrap.version == 3) {
2825             header.push({
2826                 tag: 'button',
2827                 cls : 'close',
2828                 html : '&times'
2829             });
2830         }
2831
2832         header.push(title);
2833
2834         if (this.allow_close && Roo.bootstrap.version == 4) {
2835             header.push({
2836                 tag: 'button',
2837                 cls : 'close',
2838                 html : '&times'
2839             });
2840         }
2841         
2842         var size = '';
2843
2844         if(this.size.length){
2845             size = 'modal-' + this.size;
2846         }
2847         
2848         var footer = Roo.bootstrap.version == 3 ?
2849             {
2850                 cls : 'modal-footer',
2851                 cn : [
2852                     {
2853                         tag: 'div',
2854                         cls: 'btn-' + this.buttonPosition
2855                     }
2856                 ]
2857
2858             } :
2859             {  // BS4 uses mr-auto on left buttons....
2860                 cls : 'modal-footer'
2861             };
2862
2863             
2864
2865         
2866         
2867         var modal = {
2868             cls: "modal",
2869              cn : [
2870                 {
2871                     cls: "modal-dialog " + size,
2872                     cn : [
2873                         {
2874                             cls : "modal-content",
2875                             cn : [
2876                                 {
2877                                     cls : 'modal-header',
2878                                     cn : header
2879                                 },
2880                                 bdy,
2881                                 footer
2882                             ]
2883
2884                         }
2885                     ]
2886
2887                 }
2888             ]
2889         };
2890
2891         if(this.animate){
2892             modal.cls += ' fade';
2893         }
2894
2895         return modal;
2896
2897     },
2898     getChildContainer : function() {
2899
2900          return this.bodyEl;
2901
2902     },
2903     getButtonContainer : function() {
2904         
2905          return Roo.bootstrap.version == 4 ?
2906             this.el.select('.modal-footer',true).first()
2907             : this.el.select('.modal-footer div',true).first();
2908
2909     },
2910     initEvents : function()
2911     {
2912         if (this.allow_close) {
2913             this.closeEl.on('click', this.hide, this);
2914         }
2915         Roo.EventManager.onWindowResize(this.resize, this, true);
2916
2917
2918     },
2919   
2920
2921     resize : function()
2922     {
2923         this.maskEl.setSize(
2924             Roo.lib.Dom.getViewWidth(true),
2925             Roo.lib.Dom.getViewHeight(true)
2926         );
2927         
2928         if (this.fitwindow) {
2929             
2930            
2931             this.setSize(
2932                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2933                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
2934             );
2935             return;
2936         }
2937         
2938         if(this.max_width !== 0) {
2939             
2940             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2941             
2942             if(this.height) {
2943                 this.setSize(w, this.height);
2944                 return;
2945             }
2946             
2947             if(this.max_height) {
2948                 this.setSize(w,Math.min(
2949                     this.max_height,
2950                     Roo.lib.Dom.getViewportHeight(true) - 60
2951                 ));
2952                 
2953                 return;
2954             }
2955             
2956             if(!this.fit_content) {
2957                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2958                 return;
2959             }
2960             
2961             this.setSize(w, Math.min(
2962                 60 +
2963                 this.headerEl.getHeight() + 
2964                 this.footerEl.getHeight() + 
2965                 this.getChildHeight(this.bodyEl.dom.childNodes),
2966                 Roo.lib.Dom.getViewportHeight(true) - 60)
2967             );
2968         }
2969         
2970     },
2971
2972     setSize : function(w,h)
2973     {
2974         if (!w && !h) {
2975             return;
2976         }
2977         
2978         this.resizeTo(w,h);
2979     },
2980
2981     show : function() {
2982
2983         if (!this.rendered) {
2984             this.render();
2985         }
2986
2987         //this.el.setStyle('display', 'block');
2988         this.el.removeClass('hideing');
2989         this.el.dom.style.display='block';
2990         
2991         Roo.get(document.body).addClass('modal-open');
2992  
2993         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2994             
2995             (function(){
2996                 this.el.addClass('show');
2997                 this.el.addClass('in');
2998             }).defer(50, this);
2999         }else{
3000             this.el.addClass('show');
3001             this.el.addClass('in');
3002         }
3003
3004         // not sure how we can show data in here..
3005         //if (this.tmpl) {
3006         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3007         //}
3008
3009         Roo.get(document.body).addClass("x-body-masked");
3010         
3011         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3012         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3013         this.maskEl.dom.style.display = 'block';
3014         this.maskEl.addClass('show');
3015         
3016         
3017         this.resize();
3018         
3019         this.fireEvent('show', this);
3020
3021         // set zindex here - otherwise it appears to be ignored...
3022         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3023
3024         (function () {
3025             this.items.forEach( function(e) {
3026                 e.layout ? e.layout() : false;
3027
3028             });
3029         }).defer(100,this);
3030
3031     },
3032     hide : function()
3033     {
3034         if(this.fireEvent("beforehide", this) !== false){
3035             
3036             this.maskEl.removeClass('show');
3037             
3038             this.maskEl.dom.style.display = '';
3039             Roo.get(document.body).removeClass("x-body-masked");
3040             this.el.removeClass('in');
3041             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3042
3043             if(this.animate){ // why
3044                 this.el.addClass('hideing');
3045                 this.el.removeClass('show');
3046                 (function(){
3047                     if (!this.el.hasClass('hideing')) {
3048                         return; // it's been shown again...
3049                     }
3050                     
3051                     this.el.dom.style.display='';
3052
3053                     Roo.get(document.body).removeClass('modal-open');
3054                     this.el.removeClass('hideing');
3055                 }).defer(150,this);
3056                 
3057             }else{
3058                 this.el.removeClass('show');
3059                 this.el.dom.style.display='';
3060                 Roo.get(document.body).removeClass('modal-open');
3061
3062             }
3063             this.fireEvent('hide', this);
3064         }
3065     },
3066     isVisible : function()
3067     {
3068         
3069         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3070         
3071     },
3072
3073     addButton : function(str, cb)
3074     {
3075
3076
3077         var b = Roo.apply({}, { html : str } );
3078         b.xns = b.xns || Roo.bootstrap;
3079         b.xtype = b.xtype || 'Button';
3080         if (typeof(b.listeners) == 'undefined') {
3081             b.listeners = { click : cb.createDelegate(this)  };
3082         }
3083
3084         var btn = Roo.factory(b);
3085
3086         btn.render(this.getButtonContainer());
3087
3088         return btn;
3089
3090     },
3091
3092     setDefaultButton : function(btn)
3093     {
3094         //this.el.select('.modal-footer').()
3095     },
3096
3097     resizeTo: function(w,h)
3098     {
3099         this.dialogEl.setWidth(w);
3100         
3101         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3102
3103         this.bodyEl.setHeight(h - diff);
3104         
3105         this.fireEvent('resize', this);
3106     },
3107     
3108     setContentSize  : function(w, h)
3109     {
3110
3111     },
3112     onButtonClick: function(btn,e)
3113     {
3114         //Roo.log([a,b,c]);
3115         this.fireEvent('btnclick', btn.name, e);
3116     },
3117      /**
3118      * Set the title of the Dialog
3119      * @param {String} str new Title
3120      */
3121     setTitle: function(str) {
3122         this.titleEl.dom.innerHTML = str;
3123     },
3124     /**
3125      * Set the body of the Dialog
3126      * @param {String} str new Title
3127      */
3128     setBody: function(str) {
3129         this.bodyEl.dom.innerHTML = str;
3130     },
3131     /**
3132      * Set the body of the Dialog using the template
3133      * @param {Obj} data - apply this data to the template and replace the body contents.
3134      */
3135     applyBody: function(obj)
3136     {
3137         if (!this.tmpl) {
3138             Roo.log("Error - using apply Body without a template");
3139             //code
3140         }
3141         this.tmpl.overwrite(this.bodyEl, obj);
3142     },
3143     
3144     getChildHeight : function(child_nodes)
3145     {
3146         if(
3147             !child_nodes ||
3148             child_nodes.length == 0
3149         ) {
3150             return;
3151         }
3152         
3153         var child_height = 0;
3154         
3155         for(var i = 0; i < child_nodes.length; i++) {
3156             
3157             /*
3158             * for modal with tabs...
3159             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3160                 
3161                 var layout_childs = child_nodes[i].childNodes;
3162                 
3163                 for(var j = 0; j < layout_childs.length; j++) {
3164                     
3165                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3166                         
3167                         var layout_body_childs = layout_childs[j].childNodes;
3168                         
3169                         for(var k = 0; k < layout_body_childs.length; k++) {
3170                             
3171                             if(layout_body_childs[k].classList.contains('navbar')) {
3172                                 child_height += layout_body_childs[k].offsetHeight;
3173                                 continue;
3174                             }
3175                             
3176                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3177                                 
3178                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3179                                 
3180                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3181                                     
3182                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3183                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3184                                         continue;
3185                                     }
3186                                     
3187                                 }
3188                                 
3189                             }
3190                             
3191                         }
3192                     }
3193                 }
3194                 continue;
3195             }
3196             */
3197             
3198             child_height += child_nodes[i].offsetHeight;
3199             // Roo.log(child_nodes[i].offsetHeight);
3200         }
3201         
3202         return child_height;
3203     }
3204
3205 });
3206
3207
3208 Roo.apply(Roo.bootstrap.Modal,  {
3209     /**
3210          * Button config that displays a single OK button
3211          * @type Object
3212          */
3213         OK :  [{
3214             name : 'ok',
3215             weight : 'primary',
3216             html : 'OK'
3217         }],
3218         /**
3219          * Button config that displays Yes and No buttons
3220          * @type Object
3221          */
3222         YESNO : [
3223             {
3224                 name  : 'no',
3225                 html : 'No'
3226             },
3227             {
3228                 name  :'yes',
3229                 weight : 'primary',
3230                 html : 'Yes'
3231             }
3232         ],
3233
3234         /**
3235          * Button config that displays OK and Cancel buttons
3236          * @type Object
3237          */
3238         OKCANCEL : [
3239             {
3240                name : 'cancel',
3241                 html : 'Cancel'
3242             },
3243             {
3244                 name : 'ok',
3245                 weight : 'primary',
3246                 html : 'OK'
3247             }
3248         ],
3249         /**
3250          * Button config that displays Yes, No and Cancel buttons
3251          * @type Object
3252          */
3253         YESNOCANCEL : [
3254             {
3255                 name : 'yes',
3256                 weight : 'primary',
3257                 html : 'Yes'
3258             },
3259             {
3260                 name : 'no',
3261                 html : 'No'
3262             },
3263             {
3264                 name : 'cancel',
3265                 html : 'Cancel'
3266             }
3267         ],
3268         
3269         zIndex : 10001
3270 });
3271 /*
3272  * - LGPL
3273  *
3274  * messagebox - can be used as a replace
3275  * 
3276  */
3277 /**
3278  * @class Roo.MessageBox
3279  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3280  * Example usage:
3281  *<pre><code>
3282 // Basic alert:
3283 Roo.Msg.alert('Status', 'Changes saved successfully.');
3284
3285 // Prompt for user data:
3286 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3287     if (btn == 'ok'){
3288         // process text value...
3289     }
3290 });
3291
3292 // Show a dialog using config options:
3293 Roo.Msg.show({
3294    title:'Save Changes?',
3295    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3296    buttons: Roo.Msg.YESNOCANCEL,
3297    fn: processResult,
3298    animEl: 'elId'
3299 });
3300 </code></pre>
3301  * @singleton
3302  */
3303 Roo.bootstrap.MessageBox = function(){
3304     var dlg, opt, mask, waitTimer;
3305     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3306     var buttons, activeTextEl, bwidth;
3307
3308     
3309     // private
3310     var handleButton = function(button){
3311         dlg.hide();
3312         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3313     };
3314
3315     // private
3316     var handleHide = function(){
3317         if(opt && opt.cls){
3318             dlg.el.removeClass(opt.cls);
3319         }
3320         //if(waitTimer){
3321         //    Roo.TaskMgr.stop(waitTimer);
3322         //    waitTimer = null;
3323         //}
3324     };
3325
3326     // private
3327     var updateButtons = function(b){
3328         var width = 0;
3329         if(!b){
3330             buttons["ok"].hide();
3331             buttons["cancel"].hide();
3332             buttons["yes"].hide();
3333             buttons["no"].hide();
3334             dlg.footerEl.hide();
3335             
3336             return width;
3337         }
3338         dlg.footerEl.show();
3339         for(var k in buttons){
3340             if(typeof buttons[k] != "function"){
3341                 if(b[k]){
3342                     buttons[k].show();
3343                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3344                     width += buttons[k].el.getWidth()+15;
3345                 }else{
3346                     buttons[k].hide();
3347                 }
3348             }
3349         }
3350         return width;
3351     };
3352
3353     // private
3354     var handleEsc = function(d, k, e){
3355         if(opt && opt.closable !== false){
3356             dlg.hide();
3357         }
3358         if(e){
3359             e.stopEvent();
3360         }
3361     };
3362
3363     return {
3364         /**
3365          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3366          * @return {Roo.BasicDialog} The BasicDialog element
3367          */
3368         getDialog : function(){
3369            if(!dlg){
3370                 dlg = new Roo.bootstrap.Modal( {
3371                     //draggable: true,
3372                     //resizable:false,
3373                     //constraintoviewport:false,
3374                     //fixedcenter:true,
3375                     //collapsible : false,
3376                     //shim:true,
3377                     //modal: true,
3378                 //    width: 'auto',
3379                   //  height:100,
3380                     //buttonAlign:"center",
3381                     closeClick : function(){
3382                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3383                             handleButton("no");
3384                         }else{
3385                             handleButton("cancel");
3386                         }
3387                     }
3388                 });
3389                 dlg.render();
3390                 dlg.on("hide", handleHide);
3391                 mask = dlg.mask;
3392                 //dlg.addKeyListener(27, handleEsc);
3393                 buttons = {};
3394                 this.buttons = buttons;
3395                 var bt = this.buttonText;
3396                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3397                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3398                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3399                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3400                 //Roo.log(buttons);
3401                 bodyEl = dlg.bodyEl.createChild({
3402
3403                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3404                         '<textarea class="roo-mb-textarea"></textarea>' +
3405                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3406                 });
3407                 msgEl = bodyEl.dom.firstChild;
3408                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3409                 textboxEl.enableDisplayMode();
3410                 textboxEl.addKeyListener([10,13], function(){
3411                     if(dlg.isVisible() && opt && opt.buttons){
3412                         if(opt.buttons.ok){
3413                             handleButton("ok");
3414                         }else if(opt.buttons.yes){
3415                             handleButton("yes");
3416                         }
3417                     }
3418                 });
3419                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3420                 textareaEl.enableDisplayMode();
3421                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3422                 progressEl.enableDisplayMode();
3423                 
3424                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3425                 var pf = progressEl.dom.firstChild;
3426                 if (pf) {
3427                     pp = Roo.get(pf.firstChild);
3428                     pp.setHeight(pf.offsetHeight);
3429                 }
3430                 
3431             }
3432             return dlg;
3433         },
3434
3435         /**
3436          * Updates the message box body text
3437          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3438          * the XHTML-compliant non-breaking space character '&amp;#160;')
3439          * @return {Roo.MessageBox} This message box
3440          */
3441         updateText : function(text)
3442         {
3443             if(!dlg.isVisible() && !opt.width){
3444                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3445                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3446             }
3447             msgEl.innerHTML = text || '&#160;';
3448       
3449             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3450             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3451             var w = Math.max(
3452                     Math.min(opt.width || cw , this.maxWidth), 
3453                     Math.max(opt.minWidth || this.minWidth, bwidth)
3454             );
3455             if(opt.prompt){
3456                 activeTextEl.setWidth(w);
3457             }
3458             if(dlg.isVisible()){
3459                 dlg.fixedcenter = false;
3460             }
3461             // to big, make it scroll. = But as usual stupid IE does not support
3462             // !important..
3463             
3464             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3465                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3466                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3467             } else {
3468                 bodyEl.dom.style.height = '';
3469                 bodyEl.dom.style.overflowY = '';
3470             }
3471             if (cw > w) {
3472                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3473             } else {
3474                 bodyEl.dom.style.overflowX = '';
3475             }
3476             
3477             dlg.setContentSize(w, bodyEl.getHeight());
3478             if(dlg.isVisible()){
3479                 dlg.fixedcenter = true;
3480             }
3481             return this;
3482         },
3483
3484         /**
3485          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3486          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3487          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3488          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3489          * @return {Roo.MessageBox} This message box
3490          */
3491         updateProgress : function(value, text){
3492             if(text){
3493                 this.updateText(text);
3494             }
3495             
3496             if (pp) { // weird bug on my firefox - for some reason this is not defined
3497                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3498                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3499             }
3500             return this;
3501         },        
3502
3503         /**
3504          * Returns true if the message box is currently displayed
3505          * @return {Boolean} True if the message box is visible, else false
3506          */
3507         isVisible : function(){
3508             return dlg && dlg.isVisible();  
3509         },
3510
3511         /**
3512          * Hides the message box if it is displayed
3513          */
3514         hide : function(){
3515             if(this.isVisible()){
3516                 dlg.hide();
3517             }  
3518         },
3519
3520         /**
3521          * Displays a new message box, or reinitializes an existing message box, based on the config options
3522          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3523          * The following config object properties are supported:
3524          * <pre>
3525 Property    Type             Description
3526 ----------  ---------------  ------------------------------------------------------------------------------------
3527 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3528                                    closes (defaults to undefined)
3529 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3530                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3531 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3532                                    progress and wait dialogs will ignore this property and always hide the
3533                                    close button as they can only be closed programmatically.
3534 cls               String           A custom CSS class to apply to the message box element
3535 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3536                                    displayed (defaults to 75)
3537 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3538                                    function will be btn (the name of the button that was clicked, if applicable,
3539                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3540                                    Progress and wait dialogs will ignore this option since they do not respond to
3541                                    user actions and can only be closed programmatically, so any required function
3542                                    should be called by the same code after it closes the dialog.
3543 icon              String           A CSS class that provides a background image to be used as an icon for
3544                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3545 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3546 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3547 modal             Boolean          False to allow user interaction with the page while the message box is
3548                                    displayed (defaults to true)
3549 msg               String           A string that will replace the existing message box body text (defaults
3550                                    to the XHTML-compliant non-breaking space character '&#160;')
3551 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3552 progress          Boolean          True to display a progress bar (defaults to false)
3553 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3554 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3555 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3556 title             String           The title text
3557 value             String           The string value to set into the active textbox element if displayed
3558 wait              Boolean          True to display a progress bar (defaults to false)
3559 width             Number           The width of the dialog in pixels
3560 </pre>
3561          *
3562          * Example usage:
3563          * <pre><code>
3564 Roo.Msg.show({
3565    title: 'Address',
3566    msg: 'Please enter your address:',
3567    width: 300,
3568    buttons: Roo.MessageBox.OKCANCEL,
3569    multiline: true,
3570    fn: saveAddress,
3571    animEl: 'addAddressBtn'
3572 });
3573 </code></pre>
3574          * @param {Object} config Configuration options
3575          * @return {Roo.MessageBox} This message box
3576          */
3577         show : function(options)
3578         {
3579             
3580             // this causes nightmares if you show one dialog after another
3581             // especially on callbacks..
3582              
3583             if(this.isVisible()){
3584                 
3585                 this.hide();
3586                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3587                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3588                 Roo.log("New Dialog Message:" +  options.msg )
3589                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3590                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3591                 
3592             }
3593             var d = this.getDialog();
3594             opt = options;
3595             d.setTitle(opt.title || "&#160;");
3596             d.closeEl.setDisplayed(opt.closable !== false);
3597             activeTextEl = textboxEl;
3598             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3599             if(opt.prompt){
3600                 if(opt.multiline){
3601                     textboxEl.hide();
3602                     textareaEl.show();
3603                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3604                         opt.multiline : this.defaultTextHeight);
3605                     activeTextEl = textareaEl;
3606                 }else{
3607                     textboxEl.show();
3608                     textareaEl.hide();
3609                 }
3610             }else{
3611                 textboxEl.hide();
3612                 textareaEl.hide();
3613             }
3614             progressEl.setDisplayed(opt.progress === true);
3615             if (opt.progress) {
3616                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3617             }
3618             this.updateProgress(0);
3619             activeTextEl.dom.value = opt.value || "";
3620             if(opt.prompt){
3621                 dlg.setDefaultButton(activeTextEl);
3622             }else{
3623                 var bs = opt.buttons;
3624                 var db = null;
3625                 if(bs && bs.ok){
3626                     db = buttons["ok"];
3627                 }else if(bs && bs.yes){
3628                     db = buttons["yes"];
3629                 }
3630                 dlg.setDefaultButton(db);
3631             }
3632             bwidth = updateButtons(opt.buttons);
3633             this.updateText(opt.msg);
3634             if(opt.cls){
3635                 d.el.addClass(opt.cls);
3636             }
3637             d.proxyDrag = opt.proxyDrag === true;
3638             d.modal = opt.modal !== false;
3639             d.mask = opt.modal !== false ? mask : false;
3640             if(!d.isVisible()){
3641                 // force it to the end of the z-index stack so it gets a cursor in FF
3642                 document.body.appendChild(dlg.el.dom);
3643                 d.animateTarget = null;
3644                 d.show(options.animEl);
3645             }
3646             return this;
3647         },
3648
3649         /**
3650          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3651          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3652          * and closing the message box when the process is complete.
3653          * @param {String} title The title bar text
3654          * @param {String} msg The message box body text
3655          * @return {Roo.MessageBox} This message box
3656          */
3657         progress : function(title, msg){
3658             this.show({
3659                 title : title,
3660                 msg : msg,
3661                 buttons: false,
3662                 progress:true,
3663                 closable:false,
3664                 minWidth: this.minProgressWidth,
3665                 modal : true
3666             });
3667             return this;
3668         },
3669
3670         /**
3671          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3672          * If a callback function is passed it will be called after the user clicks the button, and the
3673          * id of the button that was clicked will be passed as the only parameter to the callback
3674          * (could also be the top-right close button).
3675          * @param {String} title The title bar text
3676          * @param {String} msg The message box body text
3677          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3678          * @param {Object} scope (optional) The scope of the callback function
3679          * @return {Roo.MessageBox} This message box
3680          */
3681         alert : function(title, msg, fn, scope)
3682         {
3683             this.show({
3684                 title : title,
3685                 msg : msg,
3686                 buttons: this.OK,
3687                 fn: fn,
3688                 closable : false,
3689                 scope : scope,
3690                 modal : true
3691             });
3692             return this;
3693         },
3694
3695         /**
3696          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3697          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3698          * You are responsible for closing the message box when the process is complete.
3699          * @param {String} msg The message box body text
3700          * @param {String} title (optional) The title bar text
3701          * @return {Roo.MessageBox} This message box
3702          */
3703         wait : function(msg, title){
3704             this.show({
3705                 title : title,
3706                 msg : msg,
3707                 buttons: false,
3708                 closable:false,
3709                 progress:true,
3710                 modal:true,
3711                 width:300,
3712                 wait:true
3713             });
3714             waitTimer = Roo.TaskMgr.start({
3715                 run: function(i){
3716                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3717                 },
3718                 interval: 1000
3719             });
3720             return this;
3721         },
3722
3723         /**
3724          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3725          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3726          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3727          * @param {String} title The title bar text
3728          * @param {String} msg The message box body text
3729          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3730          * @param {Object} scope (optional) The scope of the callback function
3731          * @return {Roo.MessageBox} This message box
3732          */
3733         confirm : function(title, msg, fn, scope){
3734             this.show({
3735                 title : title,
3736                 msg : msg,
3737                 buttons: this.YESNO,
3738                 fn: fn,
3739                 scope : scope,
3740                 modal : true
3741             });
3742             return this;
3743         },
3744
3745         /**
3746          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3747          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3748          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3749          * (could also be the top-right close button) and the text that was entered will be passed as the two
3750          * parameters to the callback.
3751          * @param {String} title The title bar text
3752          * @param {String} msg The message box body text
3753          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3754          * @param {Object} scope (optional) The scope of the callback function
3755          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3756          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3757          * @return {Roo.MessageBox} This message box
3758          */
3759         prompt : function(title, msg, fn, scope, multiline){
3760             this.show({
3761                 title : title,
3762                 msg : msg,
3763                 buttons: this.OKCANCEL,
3764                 fn: fn,
3765                 minWidth:250,
3766                 scope : scope,
3767                 prompt:true,
3768                 multiline: multiline,
3769                 modal : true
3770             });
3771             return this;
3772         },
3773
3774         /**
3775          * Button config that displays a single OK button
3776          * @type Object
3777          */
3778         OK : {ok:true},
3779         /**
3780          * Button config that displays Yes and No buttons
3781          * @type Object
3782          */
3783         YESNO : {yes:true, no:true},
3784         /**
3785          * Button config that displays OK and Cancel buttons
3786          * @type Object
3787          */
3788         OKCANCEL : {ok:true, cancel:true},
3789         /**
3790          * Button config that displays Yes, No and Cancel buttons
3791          * @type Object
3792          */
3793         YESNOCANCEL : {yes:true, no:true, cancel:true},
3794
3795         /**
3796          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3797          * @type Number
3798          */
3799         defaultTextHeight : 75,
3800         /**
3801          * The maximum width in pixels of the message box (defaults to 600)
3802          * @type Number
3803          */
3804         maxWidth : 600,
3805         /**
3806          * The minimum width in pixels of the message box (defaults to 100)
3807          * @type Number
3808          */
3809         minWidth : 100,
3810         /**
3811          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3812          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3813          * @type Number
3814          */
3815         minProgressWidth : 250,
3816         /**
3817          * An object containing the default button text strings that can be overriden for localized language support.
3818          * Supported properties are: ok, cancel, yes and no.
3819          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3820          * @type Object
3821          */
3822         buttonText : {
3823             ok : "OK",
3824             cancel : "Cancel",
3825             yes : "Yes",
3826             no : "No"
3827         }
3828     };
3829 }();
3830
3831 /**
3832  * Shorthand for {@link Roo.MessageBox}
3833  */
3834 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3835 Roo.Msg = Roo.Msg || Roo.MessageBox;
3836 /*
3837  * - LGPL
3838  *
3839  * navbar
3840  * 
3841  */
3842
3843 /**
3844  * @class Roo.bootstrap.Navbar
3845  * @extends Roo.bootstrap.Component
3846  * Bootstrap Navbar class
3847
3848  * @constructor
3849  * Create a new Navbar
3850  * @param {Object} config The config object
3851  */
3852
3853
3854 Roo.bootstrap.Navbar = function(config){
3855     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3856     this.addEvents({
3857         // raw events
3858         /**
3859          * @event beforetoggle
3860          * Fire before toggle the menu
3861          * @param {Roo.EventObject} e
3862          */
3863         "beforetoggle" : true
3864     });
3865 };
3866
3867 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3868     
3869     
3870    
3871     // private
3872     navItems : false,
3873     loadMask : false,
3874     
3875     
3876     getAutoCreate : function(){
3877         
3878         
3879         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3880         
3881     },
3882     
3883     initEvents :function ()
3884     {
3885         //Roo.log(this.el.select('.navbar-toggle',true));
3886         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
3887         
3888         var mark = {
3889             tag: "div",
3890             cls:"x-dlg-mask"
3891         };
3892         
3893         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3894         
3895         var size = this.el.getSize();
3896         this.maskEl.setSize(size.width, size.height);
3897         this.maskEl.enableDisplayMode("block");
3898         this.maskEl.hide();
3899         
3900         if(this.loadMask){
3901             this.maskEl.show();
3902         }
3903     },
3904     
3905     
3906     getChildContainer : function()
3907     {
3908         if (this.el && this.el.select('.collapse').getCount()) {
3909             return this.el.select('.collapse',true).first();
3910         }
3911         
3912         return this.el;
3913     },
3914     
3915     mask : function()
3916     {
3917         this.maskEl.show();
3918     },
3919     
3920     unmask : function()
3921     {
3922         this.maskEl.hide();
3923     },
3924     onToggle : function()
3925     {
3926         
3927         if(this.fireEvent('beforetoggle', this) === false){
3928             return;
3929         }
3930         var ce = this.el.select('.navbar-collapse',true).first();
3931       
3932         if (!ce.hasClass('show')) {
3933            this.expand();
3934         } else {
3935             this.collapse();
3936         }
3937         
3938         
3939     
3940     },
3941     /**
3942      * Expand the navbar pulldown 
3943      */
3944     expand : function ()
3945     {
3946        
3947         var ce = this.el.select('.navbar-collapse',true).first();
3948         if (ce.hasClass('collapsing')) {
3949             return;
3950         }
3951         ce.dom.style.height = '';
3952                // show it...
3953         ce.addClass('in'); // old...
3954         ce.removeClass('collapse');
3955         ce.addClass('show');
3956         var h = ce.getHeight();
3957         Roo.log(h);
3958         ce.removeClass('show');
3959         // at this point we should be able to see it..
3960         ce.addClass('collapsing');
3961         
3962         ce.setHeight(0); // resize it ...
3963         ce.on('transitionend', function() {
3964             //Roo.log('done transition');
3965             ce.removeClass('collapsing');
3966             ce.addClass('show');
3967             ce.removeClass('collapse');
3968
3969             ce.dom.style.height = '';
3970         }, this, { single: true} );
3971         ce.setHeight(h);
3972         ce.dom.scrollTop = 0;
3973     },
3974     /**
3975      * Collapse the navbar pulldown 
3976      */
3977     collapse : function()
3978     {
3979          var ce = this.el.select('.navbar-collapse',true).first();
3980        
3981         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
3982             // it's collapsed or collapsing..
3983             return;
3984         }
3985         ce.removeClass('in'); // old...
3986         ce.setHeight(ce.getHeight());
3987         ce.removeClass('show');
3988         ce.addClass('collapsing');
3989         
3990         ce.on('transitionend', function() {
3991             ce.dom.style.height = '';
3992             ce.removeClass('collapsing');
3993             ce.addClass('collapse');
3994         }, this, { single: true} );
3995         ce.setHeight(0);
3996     }
3997     
3998     
3999     
4000 });
4001
4002
4003
4004  
4005
4006  /*
4007  * - LGPL
4008  *
4009  * navbar
4010  * 
4011  */
4012
4013 /**
4014  * @class Roo.bootstrap.NavSimplebar
4015  * @extends Roo.bootstrap.Navbar
4016  * Bootstrap Sidebar class
4017  *
4018  * @cfg {Boolean} inverse is inverted color
4019  * 
4020  * @cfg {String} type (nav | pills | tabs)
4021  * @cfg {Boolean} arrangement stacked | justified
4022  * @cfg {String} align (left | right) alignment
4023  * 
4024  * @cfg {Boolean} main (true|false) main nav bar? default false
4025  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4026  * 
4027  * @cfg {String} tag (header|footer|nav|div) default is nav 
4028
4029  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4030  * 
4031  * 
4032  * @constructor
4033  * Create a new Sidebar
4034  * @param {Object} config The config object
4035  */
4036
4037
4038 Roo.bootstrap.NavSimplebar = function(config){
4039     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4040 };
4041
4042 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4043     
4044     inverse: false,
4045     
4046     type: false,
4047     arrangement: '',
4048     align : false,
4049     
4050     weight : 'light',
4051     
4052     main : false,
4053     
4054     
4055     tag : false,
4056     
4057     
4058     getAutoCreate : function(){
4059         
4060         
4061         var cfg = {
4062             tag : this.tag || 'div',
4063             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4064         };
4065         if (['light','white'].indexOf(this.weight) > -1) {
4066             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4067         }
4068         cfg.cls += ' bg-' + this.weight;
4069         
4070         if (this.inverse) {
4071             cfg.cls += ' navbar-inverse';
4072             
4073         }
4074         
4075         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4076         
4077         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4078             return cfg;
4079         }
4080         
4081         
4082     
4083         
4084         cfg.cn = [
4085             {
4086                 cls: 'nav nav-' + this.xtype,
4087                 tag : 'ul'
4088             }
4089         ];
4090         
4091          
4092         this.type = this.type || 'nav';
4093         if (['tabs','pills'].indexOf(this.type) != -1) {
4094             cfg.cn[0].cls += ' nav-' + this.type
4095         
4096         
4097         } else {
4098             if (this.type!=='nav') {
4099                 Roo.log('nav type must be nav/tabs/pills')
4100             }
4101             cfg.cn[0].cls += ' navbar-nav'
4102         }
4103         
4104         
4105         
4106         
4107         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4108             cfg.cn[0].cls += ' nav-' + this.arrangement;
4109         }
4110         
4111         
4112         if (this.align === 'right') {
4113             cfg.cn[0].cls += ' navbar-right';
4114         }
4115         
4116         
4117         
4118         
4119         return cfg;
4120     
4121         
4122     }
4123     
4124     
4125     
4126 });
4127
4128
4129
4130  
4131
4132  
4133        /*
4134  * - LGPL
4135  *
4136  * navbar
4137  * navbar-fixed-top
4138  * navbar-expand-md  fixed-top 
4139  */
4140
4141 /**
4142  * @class Roo.bootstrap.NavHeaderbar
4143  * @extends Roo.bootstrap.NavSimplebar
4144  * Bootstrap Sidebar class
4145  *
4146  * @cfg {String} brand what is brand
4147  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4148  * @cfg {String} brand_href href of the brand
4149  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4150  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4151  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4152  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4153  * 
4154  * @constructor
4155  * Create a new Sidebar
4156  * @param {Object} config The config object
4157  */
4158
4159
4160 Roo.bootstrap.NavHeaderbar = function(config){
4161     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4162       
4163 };
4164
4165 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4166     
4167     position: '',
4168     brand: '',
4169     brand_href: false,
4170     srButton : true,
4171     autohide : false,
4172     desktopCenter : false,
4173    
4174     
4175     getAutoCreate : function(){
4176         
4177         var   cfg = {
4178             tag: this.nav || 'nav',
4179             cls: 'navbar navbar-expand-md',
4180             role: 'navigation',
4181             cn: []
4182         };
4183         
4184         var cn = cfg.cn;
4185         if (this.desktopCenter) {
4186             cn.push({cls : 'container', cn : []});
4187             cn = cn[0].cn;
4188         }
4189         
4190         if(this.srButton){
4191             var btn = {
4192                 tag: 'button',
4193                 type: 'button',
4194                 cls: 'navbar-toggle navbar-toggler',
4195                 'data-toggle': 'collapse',
4196                 cn: [
4197                     {
4198                         tag: 'span',
4199                         cls: 'sr-only',
4200                         html: 'Toggle navigation'
4201                     },
4202                     {
4203                         tag: 'span',
4204                         cls: 'icon-bar navbar-toggler-icon'
4205                     },
4206                     {
4207                         tag: 'span',
4208                         cls: 'icon-bar'
4209                     },
4210                     {
4211                         tag: 'span',
4212                         cls: 'icon-bar'
4213                     }
4214                 ]
4215             };
4216             
4217             cn.push( Roo.bootstrap.version == 4 ? btn : {
4218                 tag: 'div',
4219                 cls: 'navbar-header',
4220                 cn: [
4221                     btn
4222                 ]
4223             });
4224         }
4225         
4226         cn.push({
4227             tag: 'div',
4228             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4229             cn : []
4230         });
4231         
4232         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4233         
4234         if (['light','white'].indexOf(this.weight) > -1) {
4235             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4236         }
4237         cfg.cls += ' bg-' + this.weight;
4238         
4239         
4240         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4241             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4242             
4243             // tag can override this..
4244             
4245             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4246         }
4247         
4248         if (this.brand !== '') {
4249             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4250             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4251                 tag: 'a',
4252                 href: this.brand_href ? this.brand_href : '#',
4253                 cls: 'navbar-brand',
4254                 cn: [
4255                 this.brand
4256                 ]
4257             });
4258         }
4259         
4260         if(this.main){
4261             cfg.cls += ' main-nav';
4262         }
4263         
4264         
4265         return cfg;
4266
4267         
4268     },
4269     getHeaderChildContainer : function()
4270     {
4271         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4272             return this.el.select('.navbar-header',true).first();
4273         }
4274         
4275         return this.getChildContainer();
4276     },
4277     
4278     
4279     initEvents : function()
4280     {
4281         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4282         
4283         if (this.autohide) {
4284             
4285             var prevScroll = 0;
4286             var ft = this.el;
4287             
4288             Roo.get(document).on('scroll',function(e) {
4289                 var ns = Roo.get(document).getScroll().top;
4290                 var os = prevScroll;
4291                 prevScroll = ns;
4292                 
4293                 if(ns > os){
4294                     ft.removeClass('slideDown');
4295                     ft.addClass('slideUp');
4296                     return;
4297                 }
4298                 ft.removeClass('slideUp');
4299                 ft.addClass('slideDown');
4300                  
4301               
4302           },this);
4303         }
4304     }    
4305     
4306 });
4307
4308
4309
4310  
4311
4312  /*
4313  * - LGPL
4314  *
4315  * navbar
4316  * 
4317  */
4318
4319 /**
4320  * @class Roo.bootstrap.NavSidebar
4321  * @extends Roo.bootstrap.Navbar
4322  * Bootstrap Sidebar class
4323  * 
4324  * @constructor
4325  * Create a new Sidebar
4326  * @param {Object} config The config object
4327  */
4328
4329
4330 Roo.bootstrap.NavSidebar = function(config){
4331     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4332 };
4333
4334 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4335     
4336     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4337     
4338     getAutoCreate : function(){
4339         
4340         
4341         return  {
4342             tag: 'div',
4343             cls: 'sidebar sidebar-nav'
4344         };
4345     
4346         
4347     }
4348     
4349     
4350     
4351 });
4352
4353
4354
4355  
4356
4357  /*
4358  * - LGPL
4359  *
4360  * nav group
4361  * 
4362  */
4363
4364 /**
4365  * @class Roo.bootstrap.NavGroup
4366  * @extends Roo.bootstrap.Component
4367  * Bootstrap NavGroup class
4368  * @cfg {String} align (left|right)
4369  * @cfg {Boolean} inverse
4370  * @cfg {String} type (nav|pills|tab) default nav
4371  * @cfg {String} navId - reference Id for navbar.
4372
4373  * 
4374  * @constructor
4375  * Create a new nav group
4376  * @param {Object} config The config object
4377  */
4378
4379 Roo.bootstrap.NavGroup = function(config){
4380     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4381     this.navItems = [];
4382    
4383     Roo.bootstrap.NavGroup.register(this);
4384      this.addEvents({
4385         /**
4386              * @event changed
4387              * Fires when the active item changes
4388              * @param {Roo.bootstrap.NavGroup} this
4389              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4390              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4391          */
4392         'changed': true
4393      });
4394     
4395 };
4396
4397 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4398     
4399     align: '',
4400     inverse: false,
4401     form: false,
4402     type: 'nav',
4403     navId : '',
4404     // private
4405     
4406     navItems : false, 
4407     
4408     getAutoCreate : function()
4409     {
4410         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4411         
4412         cfg = {
4413             tag : 'ul',
4414             cls: 'nav' 
4415         };
4416         if (Roo.bootstrap.version == 4) {
4417             if (['tabs','pills'].indexOf(this.type) != -1) {
4418                 cfg.cls += ' nav-' + this.type; 
4419             } else {
4420                 // trying to remove so header bar can right align top?
4421                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
4422                     // do not use on header bar... 
4423                     cfg.cls += ' navbar-nav';
4424                 }
4425             }
4426             
4427         } else {
4428             if (['tabs','pills'].indexOf(this.type) != -1) {
4429                 cfg.cls += ' nav-' + this.type
4430             } else {
4431                 if (this.type !== 'nav') {
4432                     Roo.log('nav type must be nav/tabs/pills')
4433                 }
4434                 cfg.cls += ' navbar-nav'
4435             }
4436         }
4437         
4438         if (this.parent() && this.parent().sidebar) {
4439             cfg = {
4440                 tag: 'ul',
4441                 cls: 'dashboard-menu sidebar-menu'
4442             };
4443             
4444             return cfg;
4445         }
4446         
4447         if (this.form === true) {
4448             cfg = {
4449                 tag: 'form',
4450                 cls: 'navbar-form form-inline'
4451             };
4452             //nav navbar-right ml-md-auto
4453             if (this.align === 'right') {
4454                 cfg.cls += ' navbar-right ml-md-auto';
4455             } else {
4456                 cfg.cls += ' navbar-left';
4457             }
4458         }
4459         
4460         if (this.align === 'right') {
4461             cfg.cls += ' navbar-right ml-md-auto';
4462         } else {
4463             cfg.cls += ' mr-auto';
4464         }
4465         
4466         if (this.inverse) {
4467             cfg.cls += ' navbar-inverse';
4468             
4469         }
4470         
4471         
4472         return cfg;
4473     },
4474     /**
4475     * sets the active Navigation item
4476     * @param {Roo.bootstrap.NavItem} the new current navitem
4477     */
4478     setActiveItem : function(item)
4479     {
4480         var prev = false;
4481         Roo.each(this.navItems, function(v){
4482             if (v == item) {
4483                 return ;
4484             }
4485             if (v.isActive()) {
4486                 v.setActive(false, true);
4487                 prev = v;
4488                 
4489             }
4490             
4491         });
4492
4493         item.setActive(true, true);
4494         this.fireEvent('changed', this, item, prev);
4495         
4496         
4497     },
4498     /**
4499     * gets the active Navigation item
4500     * @return {Roo.bootstrap.NavItem} the current navitem
4501     */
4502     getActive : function()
4503     {
4504         
4505         var prev = false;
4506         Roo.each(this.navItems, function(v){
4507             
4508             if (v.isActive()) {
4509                 prev = v;
4510                 
4511             }
4512             
4513         });
4514         return prev;
4515     },
4516     
4517     indexOfNav : function()
4518     {
4519         
4520         var prev = false;
4521         Roo.each(this.navItems, function(v,i){
4522             
4523             if (v.isActive()) {
4524                 prev = i;
4525                 
4526             }
4527             
4528         });
4529         return prev;
4530     },
4531     /**
4532     * adds a Navigation item
4533     * @param {Roo.bootstrap.NavItem} the navitem to add
4534     */
4535     addItem : function(cfg)
4536     {
4537         if (this.form && Roo.bootstrap.version == 4) {
4538             cfg.tag = 'div';
4539         }
4540         var cn = new Roo.bootstrap.NavItem(cfg);
4541         this.register(cn);
4542         cn.parentId = this.id;
4543         cn.onRender(this.el, null);
4544         return cn;
4545     },
4546     /**
4547     * register a Navigation item
4548     * @param {Roo.bootstrap.NavItem} the navitem to add
4549     */
4550     register : function(item)
4551     {
4552         this.navItems.push( item);
4553         item.navId = this.navId;
4554     
4555     },
4556     
4557     /**
4558     * clear all the Navigation item
4559     */
4560    
4561     clearAll : function()
4562     {
4563         this.navItems = [];
4564         this.el.dom.innerHTML = '';
4565     },
4566     
4567     getNavItem: function(tabId)
4568     {
4569         var ret = false;
4570         Roo.each(this.navItems, function(e) {
4571             if (e.tabId == tabId) {
4572                ret =  e;
4573                return false;
4574             }
4575             return true;
4576             
4577         });
4578         return ret;
4579     },
4580     
4581     setActiveNext : function()
4582     {
4583         var i = this.indexOfNav(this.getActive());
4584         if (i > this.navItems.length) {
4585             return;
4586         }
4587         this.setActiveItem(this.navItems[i+1]);
4588     },
4589     setActivePrev : function()
4590     {
4591         var i = this.indexOfNav(this.getActive());
4592         if (i  < 1) {
4593             return;
4594         }
4595         this.setActiveItem(this.navItems[i-1]);
4596     },
4597     clearWasActive : function(except) {
4598         Roo.each(this.navItems, function(e) {
4599             if (e.tabId != except.tabId && e.was_active) {
4600                e.was_active = false;
4601                return false;
4602             }
4603             return true;
4604             
4605         });
4606     },
4607     getWasActive : function ()
4608     {
4609         var r = false;
4610         Roo.each(this.navItems, function(e) {
4611             if (e.was_active) {
4612                r = e;
4613                return false;
4614             }
4615             return true;
4616             
4617         });
4618         return r;
4619     }
4620     
4621     
4622 });
4623
4624  
4625 Roo.apply(Roo.bootstrap.NavGroup, {
4626     
4627     groups: {},
4628      /**
4629     * register a Navigation Group
4630     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4631     */
4632     register : function(navgrp)
4633     {
4634         this.groups[navgrp.navId] = navgrp;
4635         
4636     },
4637     /**
4638     * fetch a Navigation Group based on the navigation ID
4639     * @param {string} the navgroup to add
4640     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4641     */
4642     get: function(navId) {
4643         if (typeof(this.groups[navId]) == 'undefined') {
4644             return false;
4645             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4646         }
4647         return this.groups[navId] ;
4648     }
4649     
4650     
4651     
4652 });
4653
4654  /*
4655  * - LGPL
4656  *
4657  * row
4658  * 
4659  */
4660
4661 /**
4662  * @class Roo.bootstrap.NavItem
4663  * @extends Roo.bootstrap.Component
4664  * Bootstrap Navbar.NavItem class
4665  * @cfg {String} href  link to
4666  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4667
4668  * @cfg {String} html content of button
4669  * @cfg {String} badge text inside badge
4670  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4671  * @cfg {String} glyphicon DEPRICATED - use fa
4672  * @cfg {String} icon DEPRICATED - use fa
4673  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4674  * @cfg {Boolean} active Is item active
4675  * @cfg {Boolean} disabled Is item disabled
4676  
4677  * @cfg {Boolean} preventDefault (true | false) default false
4678  * @cfg {String} tabId the tab that this item activates.
4679  * @cfg {String} tagtype (a|span) render as a href or span?
4680  * @cfg {Boolean} animateRef (true|false) link to element default false  
4681   
4682  * @constructor
4683  * Create a new Navbar Item
4684  * @param {Object} config The config object
4685  */
4686 Roo.bootstrap.NavItem = function(config){
4687     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4688     this.addEvents({
4689         // raw events
4690         /**
4691          * @event click
4692          * The raw click event for the entire grid.
4693          * @param {Roo.EventObject} e
4694          */
4695         "click" : true,
4696          /**
4697             * @event changed
4698             * Fires when the active item active state changes
4699             * @param {Roo.bootstrap.NavItem} this
4700             * @param {boolean} state the new state
4701              
4702          */
4703         'changed': true,
4704         /**
4705             * @event scrollto
4706             * Fires when scroll to element
4707             * @param {Roo.bootstrap.NavItem} this
4708             * @param {Object} options
4709             * @param {Roo.EventObject} e
4710              
4711          */
4712         'scrollto': true
4713     });
4714    
4715 };
4716
4717 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4718     
4719     href: false,
4720     html: '',
4721     badge: '',
4722     icon: false,
4723     fa : false,
4724     glyphicon: false,
4725     active: false,
4726     preventDefault : false,
4727     tabId : false,
4728     tagtype : 'a',
4729     tag: 'li',
4730     disabled : false,
4731     animateRef : false,
4732     was_active : false,
4733     button_weight : '',
4734     button_outline : false,
4735     
4736     navLink: false,
4737     
4738     getAutoCreate : function(){
4739          
4740         var cfg = {
4741             tag: this.tag,
4742             cls: 'nav-item'
4743         };
4744         
4745         if (this.active) {
4746             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4747         }
4748         if (this.disabled) {
4749             cfg.cls += ' disabled';
4750         }
4751         
4752         // BS4 only?
4753         if (this.button_weight.length) {
4754             cfg.tag = this.href ? 'a' : 'button';
4755             cfg.html = this.html || '';
4756             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4757             if (this.href) {
4758                 cfg.href = this.href;
4759             }
4760             if (this.fa) {
4761                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4762             }
4763             
4764             // menu .. should add dropdown-menu class - so no need for carat..
4765             
4766             if (this.badge !== '') {
4767                  
4768                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4769             }
4770             return cfg;
4771         }
4772         
4773         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4774             cfg.cn = [
4775                 {
4776                     tag: this.tagtype,
4777                     href : this.href || "#",
4778                     html: this.html || ''
4779                 }
4780             ];
4781             if (this.tagtype == 'a') {
4782                 cfg.cn[0].cls = 'nav-link';
4783             }
4784             if (this.icon) {
4785                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4786             }
4787             if (this.fa) {
4788                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4789             }
4790             if(this.glyphicon) {
4791                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4792             }
4793             
4794             if (this.menu) {
4795                 
4796                 cfg.cn[0].html += " <span class='caret'></span>";
4797              
4798             }
4799             
4800             if (this.badge !== '') {
4801                  
4802                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4803             }
4804         }
4805         
4806         
4807         
4808         return cfg;
4809     },
4810     onRender : function(ct, position)
4811     {
4812        // Roo.log("Call onRender: " + this.xtype);
4813         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4814             this.tag = 'div';
4815         }
4816         
4817         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4818         this.navLink = this.el.select('.nav-link',true).first();
4819         return ret;
4820     },
4821       
4822     
4823     initEvents: function() 
4824     {
4825         if (typeof (this.menu) != 'undefined') {
4826             this.menu.parentType = this.xtype;
4827             this.menu.triggerEl = this.el;
4828             this.menu = this.addxtype(Roo.apply({}, this.menu));
4829         }
4830         
4831         this.el.select('a',true).on('click', this.onClick, this);
4832         
4833         if(this.tagtype == 'span'){
4834             this.el.select('span',true).on('click', this.onClick, this);
4835         }
4836        
4837         // at this point parent should be available..
4838         this.parent().register(this);
4839     },
4840     
4841     onClick : function(e)
4842     {
4843         if (e.getTarget('.dropdown-menu-item')) {
4844             // did you click on a menu itemm.... - then don't trigger onclick..
4845             return;
4846         }
4847         
4848         if(
4849                 this.preventDefault || 
4850                 this.href == '#' 
4851         ){
4852             Roo.log("NavItem - prevent Default?");
4853             e.preventDefault();
4854         }
4855         
4856         if (this.disabled) {
4857             return;
4858         }
4859         
4860         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4861         if (tg && tg.transition) {
4862             Roo.log("waiting for the transitionend");
4863             return;
4864         }
4865         
4866         
4867         
4868         //Roo.log("fire event clicked");
4869         if(this.fireEvent('click', this, e) === false){
4870             return;
4871         };
4872         
4873         if(this.tagtype == 'span'){
4874             return;
4875         }
4876         
4877         //Roo.log(this.href);
4878         var ael = this.el.select('a',true).first();
4879         //Roo.log(ael);
4880         
4881         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4882             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4883             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4884                 return; // ignore... - it's a 'hash' to another page.
4885             }
4886             Roo.log("NavItem - prevent Default?");
4887             e.preventDefault();
4888             this.scrollToElement(e);
4889         }
4890         
4891         
4892         var p =  this.parent();
4893    
4894         if (['tabs','pills'].indexOf(p.type)!==-1) {
4895             if (typeof(p.setActiveItem) !== 'undefined') {
4896                 p.setActiveItem(this);
4897             }
4898         }
4899         
4900         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4901         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4902             // remove the collapsed menu expand...
4903             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
4904         }
4905     },
4906     
4907     isActive: function () {
4908         return this.active
4909     },
4910     setActive : function(state, fire, is_was_active)
4911     {
4912         if (this.active && !state && this.navId) {
4913             this.was_active = true;
4914             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4915             if (nv) {
4916                 nv.clearWasActive(this);
4917             }
4918             
4919         }
4920         this.active = state;
4921         
4922         if (!state ) {
4923             this.el.removeClass('active');
4924             this.navLink ? this.navLink.removeClass('active') : false;
4925         } else if (!this.el.hasClass('active')) {
4926             
4927             this.el.addClass('active');
4928             if (Roo.bootstrap.version == 4 && this.navLink ) {
4929                 this.navLink.addClass('active');
4930             }
4931             
4932         }
4933         if (fire) {
4934             this.fireEvent('changed', this, state);
4935         }
4936         
4937         // show a panel if it's registered and related..
4938         
4939         if (!this.navId || !this.tabId || !state || is_was_active) {
4940             return;
4941         }
4942         
4943         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4944         if (!tg) {
4945             return;
4946         }
4947         var pan = tg.getPanelByName(this.tabId);
4948         if (!pan) {
4949             return;
4950         }
4951         // if we can not flip to new panel - go back to old nav highlight..
4952         if (false == tg.showPanel(pan)) {
4953             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4954             if (nv) {
4955                 var onav = nv.getWasActive();
4956                 if (onav) {
4957                     onav.setActive(true, false, true);
4958                 }
4959             }
4960             
4961         }
4962         
4963         
4964         
4965     },
4966      // this should not be here...
4967     setDisabled : function(state)
4968     {
4969         this.disabled = state;
4970         if (!state ) {
4971             this.el.removeClass('disabled');
4972         } else if (!this.el.hasClass('disabled')) {
4973             this.el.addClass('disabled');
4974         }
4975         
4976     },
4977     
4978     /**
4979      * Fetch the element to display the tooltip on.
4980      * @return {Roo.Element} defaults to this.el
4981      */
4982     tooltipEl : function()
4983     {
4984         return this.el.select('' + this.tagtype + '', true).first();
4985     },
4986     
4987     scrollToElement : function(e)
4988     {
4989         var c = document.body;
4990         
4991         /*
4992          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4993          */
4994         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4995             c = document.documentElement;
4996         }
4997         
4998         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4999         
5000         if(!target){
5001             return;
5002         }
5003
5004         var o = target.calcOffsetsTo(c);
5005         
5006         var options = {
5007             target : target,
5008             value : o[1]
5009         };
5010         
5011         this.fireEvent('scrollto', this, options, e);
5012         
5013         Roo.get(c).scrollTo('top', options.value, true);
5014         
5015         return;
5016     }
5017 });
5018  
5019
5020  /*
5021  * - LGPL
5022  *
5023  * sidebar item
5024  *
5025  *  li
5026  *    <span> icon </span>
5027  *    <span> text </span>
5028  *    <span>badge </span>
5029  */
5030
5031 /**
5032  * @class Roo.bootstrap.NavSidebarItem
5033  * @extends Roo.bootstrap.NavItem
5034  * Bootstrap Navbar.NavSidebarItem class
5035  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5036  * {Boolean} open is the menu open
5037  * {Boolean} buttonView use button as the tigger el rather that a (default false)
5038  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5039  * {String} buttonSize (sm|md|lg)the extra classes for the button
5040  * {Boolean} showArrow show arrow next to the text (default true)
5041  * @constructor
5042  * Create a new Navbar Button
5043  * @param {Object} config The config object
5044  */
5045 Roo.bootstrap.NavSidebarItem = function(config){
5046     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5047     this.addEvents({
5048         // raw events
5049         /**
5050          * @event click
5051          * The raw click event for the entire grid.
5052          * @param {Roo.EventObject} e
5053          */
5054         "click" : true,
5055          /**
5056             * @event changed
5057             * Fires when the active item active state changes
5058             * @param {Roo.bootstrap.NavSidebarItem} this
5059             * @param {boolean} state the new state
5060              
5061          */
5062         'changed': true
5063     });
5064    
5065 };
5066
5067 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5068     
5069     badgeWeight : 'default',
5070     
5071     open: false,
5072     
5073     buttonView : false,
5074     
5075     buttonWeight : 'default',
5076     
5077     buttonSize : 'md',
5078     
5079     showArrow : true,
5080     
5081     getAutoCreate : function(){
5082         
5083         
5084         var a = {
5085                 tag: 'a',
5086                 href : this.href || '#',
5087                 cls: '',
5088                 html : '',
5089                 cn : []
5090         };
5091         
5092         if(this.buttonView){
5093             a = {
5094                 tag: 'button',
5095                 href : this.href || '#',
5096                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5097                 html : this.html,
5098                 cn : []
5099             };
5100         }
5101         
5102         var cfg = {
5103             tag: 'li',
5104             cls: '',
5105             cn: [ a ]
5106         };
5107         
5108         if (this.active) {
5109             cfg.cls += ' active';
5110         }
5111         
5112         if (this.disabled) {
5113             cfg.cls += ' disabled';
5114         }
5115         if (this.open) {
5116             cfg.cls += ' open x-open';
5117         }
5118         // left icon..
5119         if (this.glyphicon || this.icon) {
5120             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5121             a.cn.push({ tag : 'i', cls : c }) ;
5122         }
5123         
5124         if(!this.buttonView){
5125             var span = {
5126                 tag: 'span',
5127                 html : this.html || ''
5128             };
5129
5130             a.cn.push(span);
5131             
5132         }
5133         
5134         if (this.badge !== '') {
5135             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5136         }
5137         
5138         if (this.menu) {
5139             
5140             if(this.showArrow){
5141                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5142             }
5143             
5144             a.cls += ' dropdown-toggle treeview' ;
5145         }
5146         
5147         return cfg;
5148     },
5149     
5150     initEvents : function()
5151     { 
5152         if (typeof (this.menu) != 'undefined') {
5153             this.menu.parentType = this.xtype;
5154             this.menu.triggerEl = this.el;
5155             this.menu = this.addxtype(Roo.apply({}, this.menu));
5156         }
5157         
5158         this.el.on('click', this.onClick, this);
5159         
5160         if(this.badge !== ''){
5161             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5162         }
5163         
5164     },
5165     
5166     onClick : function(e)
5167     {
5168         if(this.disabled){
5169             e.preventDefault();
5170             return;
5171         }
5172         
5173         if(this.preventDefault){
5174             e.preventDefault();
5175         }
5176         
5177         this.fireEvent('click', this, e);
5178     },
5179     
5180     disable : function()
5181     {
5182         this.setDisabled(true);
5183     },
5184     
5185     enable : function()
5186     {
5187         this.setDisabled(false);
5188     },
5189     
5190     setDisabled : function(state)
5191     {
5192         if(this.disabled == state){
5193             return;
5194         }
5195         
5196         this.disabled = state;
5197         
5198         if (state) {
5199             this.el.addClass('disabled');
5200             return;
5201         }
5202         
5203         this.el.removeClass('disabled');
5204         
5205         return;
5206     },
5207     
5208     setActive : function(state)
5209     {
5210         if(this.active == state){
5211             return;
5212         }
5213         
5214         this.active = state;
5215         
5216         if (state) {
5217             this.el.addClass('active');
5218             return;
5219         }
5220         
5221         this.el.removeClass('active');
5222         
5223         return;
5224     },
5225     
5226     isActive: function () 
5227     {
5228         return this.active;
5229     },
5230     
5231     setBadge : function(str)
5232     {
5233         if(!this.badgeEl){
5234             return;
5235         }
5236         
5237         this.badgeEl.dom.innerHTML = str;
5238     }
5239     
5240    
5241      
5242  
5243 });
5244  
5245
5246  /*
5247  * - LGPL
5248  *
5249  * row
5250  * 
5251  */
5252
5253 /**
5254  * @class Roo.bootstrap.Row
5255  * @extends Roo.bootstrap.Component
5256  * Bootstrap Row class (contains columns...)
5257  * 
5258  * @constructor
5259  * Create a new Row
5260  * @param {Object} config The config object
5261  */
5262
5263 Roo.bootstrap.Row = function(config){
5264     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5265 };
5266
5267 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5268     
5269     getAutoCreate : function(){
5270        return {
5271             cls: 'row clearfix'
5272        };
5273     }
5274     
5275     
5276 });
5277
5278  
5279
5280  /*
5281  * - LGPL
5282  *
5283  * element
5284  * 
5285  */
5286
5287 /**
5288  * @class Roo.bootstrap.Element
5289  * @extends Roo.bootstrap.Component
5290  * Bootstrap Element class
5291  * @cfg {String} html contents of the element
5292  * @cfg {String} tag tag of the element
5293  * @cfg {String} cls class of the element
5294  * @cfg {Boolean} preventDefault (true|false) default false
5295  * @cfg {Boolean} clickable (true|false) default false
5296  * 
5297  * @constructor
5298  * Create a new Element
5299  * @param {Object} config The config object
5300  */
5301
5302 Roo.bootstrap.Element = function(config){
5303     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5304     
5305     this.addEvents({
5306         // raw events
5307         /**
5308          * @event click
5309          * When a element is chick
5310          * @param {Roo.bootstrap.Element} this
5311          * @param {Roo.EventObject} e
5312          */
5313         "click" : true
5314     });
5315 };
5316
5317 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5318     
5319     tag: 'div',
5320     cls: '',
5321     html: '',
5322     preventDefault: false, 
5323     clickable: false,
5324     
5325     getAutoCreate : function(){
5326         
5327         var cfg = {
5328             tag: this.tag,
5329             // cls: this.cls, double assign in parent class Component.js :: onRender
5330             html: this.html
5331         };
5332         
5333         return cfg;
5334     },
5335     
5336     initEvents: function() 
5337     {
5338         Roo.bootstrap.Element.superclass.initEvents.call(this);
5339         
5340         if(this.clickable){
5341             this.el.on('click', this.onClick, this);
5342         }
5343         
5344     },
5345     
5346     onClick : function(e)
5347     {
5348         if(this.preventDefault){
5349             e.preventDefault();
5350         }
5351         
5352         this.fireEvent('click', this, e);
5353     },
5354     
5355     getValue : function()
5356     {
5357         return this.el.dom.innerHTML;
5358     },
5359     
5360     setValue : function(value)
5361     {
5362         this.el.dom.innerHTML = value;
5363     }
5364    
5365 });
5366
5367  
5368
5369  /*
5370  * - LGPL
5371  *
5372  * pagination
5373  * 
5374  */
5375
5376 /**
5377  * @class Roo.bootstrap.Pagination
5378  * @extends Roo.bootstrap.Component
5379  * Bootstrap Pagination class
5380  * @cfg {String} size xs | sm | md | lg
5381  * @cfg {Boolean} inverse false | true
5382  * 
5383  * @constructor
5384  * Create a new Pagination
5385  * @param {Object} config The config object
5386  */
5387
5388 Roo.bootstrap.Pagination = function(config){
5389     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5390 };
5391
5392 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5393     
5394     cls: false,
5395     size: false,
5396     inverse: false,
5397     
5398     getAutoCreate : function(){
5399         var cfg = {
5400             tag: 'ul',
5401                 cls: 'pagination'
5402         };
5403         if (this.inverse) {
5404             cfg.cls += ' inverse';
5405         }
5406         if (this.html) {
5407             cfg.html=this.html;
5408         }
5409         if (this.cls) {
5410             cfg.cls += " " + this.cls;
5411         }
5412         return cfg;
5413     }
5414    
5415 });
5416
5417  
5418
5419  /*
5420  * - LGPL
5421  *
5422  * Pagination item
5423  * 
5424  */
5425
5426
5427 /**
5428  * @class Roo.bootstrap.PaginationItem
5429  * @extends Roo.bootstrap.Component
5430  * Bootstrap PaginationItem class
5431  * @cfg {String} html text
5432  * @cfg {String} href the link
5433  * @cfg {Boolean} preventDefault (true | false) default true
5434  * @cfg {Boolean} active (true | false) default false
5435  * @cfg {Boolean} disabled default false
5436  * 
5437  * 
5438  * @constructor
5439  * Create a new PaginationItem
5440  * @param {Object} config The config object
5441  */
5442
5443
5444 Roo.bootstrap.PaginationItem = function(config){
5445     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5446     this.addEvents({
5447         // raw events
5448         /**
5449          * @event click
5450          * The raw click event for the entire grid.
5451          * @param {Roo.EventObject} e
5452          */
5453         "click" : true
5454     });
5455 };
5456
5457 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5458     
5459     href : false,
5460     html : false,
5461     preventDefault: true,
5462     active : false,
5463     cls : false,
5464     disabled: false,
5465     
5466     getAutoCreate : function(){
5467         var cfg= {
5468             tag: 'li',
5469             cn: [
5470                 {
5471                     tag : 'a',
5472                     href : this.href ? this.href : '#',
5473                     html : this.html ? this.html : ''
5474                 }
5475             ]
5476         };
5477         
5478         if(this.cls){
5479             cfg.cls = this.cls;
5480         }
5481         
5482         if(this.disabled){
5483             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5484         }
5485         
5486         if(this.active){
5487             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5488         }
5489         
5490         return cfg;
5491     },
5492     
5493     initEvents: function() {
5494         
5495         this.el.on('click', this.onClick, this);
5496         
5497     },
5498     onClick : function(e)
5499     {
5500         Roo.log('PaginationItem on click ');
5501         if(this.preventDefault){
5502             e.preventDefault();
5503         }
5504         
5505         if(this.disabled){
5506             return;
5507         }
5508         
5509         this.fireEvent('click', this, e);
5510     }
5511    
5512 });
5513
5514  
5515
5516  /*
5517  * - LGPL
5518  *
5519  * slider
5520  * 
5521  */
5522
5523
5524 /**
5525  * @class Roo.bootstrap.Slider
5526  * @extends Roo.bootstrap.Component
5527  * Bootstrap Slider class
5528  *    
5529  * @constructor
5530  * Create a new Slider
5531  * @param {Object} config The config object
5532  */
5533
5534 Roo.bootstrap.Slider = function(config){
5535     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5536 };
5537
5538 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5539     
5540     getAutoCreate : function(){
5541         
5542         var cfg = {
5543             tag: 'div',
5544             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5545             cn: [
5546                 {
5547                     tag: 'a',
5548                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5549                 }
5550             ]
5551         };
5552         
5553         return cfg;
5554     }
5555    
5556 });
5557
5558  /*
5559  * Based on:
5560  * Ext JS Library 1.1.1
5561  * Copyright(c) 2006-2007, Ext JS, LLC.
5562  *
5563  * Originally Released Under LGPL - original licence link has changed is not relivant.
5564  *
5565  * Fork - LGPL
5566  * <script type="text/javascript">
5567  */
5568  
5569
5570 /**
5571  * @class Roo.grid.ColumnModel
5572  * @extends Roo.util.Observable
5573  * This is the default implementation of a ColumnModel used by the Grid. It defines
5574  * the columns in the grid.
5575  * <br>Usage:<br>
5576  <pre><code>
5577  var colModel = new Roo.grid.ColumnModel([
5578         {header: "Ticker", width: 60, sortable: true, locked: true},
5579         {header: "Company Name", width: 150, sortable: true},
5580         {header: "Market Cap.", width: 100, sortable: true},
5581         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5582         {header: "Employees", width: 100, sortable: true, resizable: false}
5583  ]);
5584  </code></pre>
5585  * <p>
5586  
5587  * The config options listed for this class are options which may appear in each
5588  * individual column definition.
5589  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5590  * @constructor
5591  * @param {Object} config An Array of column config objects. See this class's
5592  * config objects for details.
5593 */
5594 Roo.grid.ColumnModel = function(config){
5595         /**
5596      * The config passed into the constructor
5597      */
5598     this.config = config;
5599     this.lookup = {};
5600
5601     // if no id, create one
5602     // if the column does not have a dataIndex mapping,
5603     // map it to the order it is in the config
5604     for(var i = 0, len = config.length; i < len; i++){
5605         var c = config[i];
5606         if(typeof c.dataIndex == "undefined"){
5607             c.dataIndex = i;
5608         }
5609         if(typeof c.renderer == "string"){
5610             c.renderer = Roo.util.Format[c.renderer];
5611         }
5612         if(typeof c.id == "undefined"){
5613             c.id = Roo.id();
5614         }
5615         if(c.editor && c.editor.xtype){
5616             c.editor  = Roo.factory(c.editor, Roo.grid);
5617         }
5618         if(c.editor && c.editor.isFormField){
5619             c.editor = new Roo.grid.GridEditor(c.editor);
5620         }
5621         this.lookup[c.id] = c;
5622     }
5623
5624     /**
5625      * The width of columns which have no width specified (defaults to 100)
5626      * @type Number
5627      */
5628     this.defaultWidth = 100;
5629
5630     /**
5631      * Default sortable of columns which have no sortable specified (defaults to false)
5632      * @type Boolean
5633      */
5634     this.defaultSortable = false;
5635
5636     this.addEvents({
5637         /**
5638              * @event widthchange
5639              * Fires when the width of a column changes.
5640              * @param {ColumnModel} this
5641              * @param {Number} columnIndex The column index
5642              * @param {Number} newWidth The new width
5643              */
5644             "widthchange": true,
5645         /**
5646              * @event headerchange
5647              * Fires when the text of a header changes.
5648              * @param {ColumnModel} this
5649              * @param {Number} columnIndex The column index
5650              * @param {Number} newText The new header text
5651              */
5652             "headerchange": true,
5653         /**
5654              * @event hiddenchange
5655              * Fires when a column is hidden or "unhidden".
5656              * @param {ColumnModel} this
5657              * @param {Number} columnIndex The column index
5658              * @param {Boolean} hidden true if hidden, false otherwise
5659              */
5660             "hiddenchange": true,
5661             /**
5662          * @event columnmoved
5663          * Fires when a column is moved.
5664          * @param {ColumnModel} this
5665          * @param {Number} oldIndex
5666          * @param {Number} newIndex
5667          */
5668         "columnmoved" : true,
5669         /**
5670          * @event columlockchange
5671          * Fires when a column's locked state is changed
5672          * @param {ColumnModel} this
5673          * @param {Number} colIndex
5674          * @param {Boolean} locked true if locked
5675          */
5676         "columnlockchange" : true
5677     });
5678     Roo.grid.ColumnModel.superclass.constructor.call(this);
5679 };
5680 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5681     /**
5682      * @cfg {String} header The header text to display in the Grid view.
5683      */
5684     /**
5685      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5686      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5687      * specified, the column's index is used as an index into the Record's data Array.
5688      */
5689     /**
5690      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5691      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5692      */
5693     /**
5694      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5695      * Defaults to the value of the {@link #defaultSortable} property.
5696      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5697      */
5698     /**
5699      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5700      */
5701     /**
5702      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5703      */
5704     /**
5705      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5706      */
5707     /**
5708      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5709      */
5710     /**
5711      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5712      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5713      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5714      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5715      */
5716        /**
5717      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5718      */
5719     /**
5720      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5721      */
5722     /**
5723      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5724      */
5725     /**
5726      * @cfg {String} cursor (Optional)
5727      */
5728     /**
5729      * @cfg {String} tooltip (Optional)
5730      */
5731     /**
5732      * @cfg {Number} xs (Optional)
5733      */
5734     /**
5735      * @cfg {Number} sm (Optional)
5736      */
5737     /**
5738      * @cfg {Number} md (Optional)
5739      */
5740     /**
5741      * @cfg {Number} lg (Optional)
5742      */
5743     /**
5744      * Returns the id of the column at the specified index.
5745      * @param {Number} index The column index
5746      * @return {String} the id
5747      */
5748     getColumnId : function(index){
5749         return this.config[index].id;
5750     },
5751
5752     /**
5753      * Returns the column for a specified id.
5754      * @param {String} id The column id
5755      * @return {Object} the column
5756      */
5757     getColumnById : function(id){
5758         return this.lookup[id];
5759     },
5760
5761     
5762     /**
5763      * Returns the column for a specified dataIndex.
5764      * @param {String} dataIndex The column dataIndex
5765      * @return {Object|Boolean} the column or false if not found
5766      */
5767     getColumnByDataIndex: function(dataIndex){
5768         var index = this.findColumnIndex(dataIndex);
5769         return index > -1 ? this.config[index] : false;
5770     },
5771     
5772     /**
5773      * Returns the index for a specified column id.
5774      * @param {String} id The column id
5775      * @return {Number} the index, or -1 if not found
5776      */
5777     getIndexById : function(id){
5778         for(var i = 0, len = this.config.length; i < len; i++){
5779             if(this.config[i].id == id){
5780                 return i;
5781             }
5782         }
5783         return -1;
5784     },
5785     
5786     /**
5787      * Returns the index for a specified column dataIndex.
5788      * @param {String} dataIndex The column dataIndex
5789      * @return {Number} the index, or -1 if not found
5790      */
5791     
5792     findColumnIndex : function(dataIndex){
5793         for(var i = 0, len = this.config.length; i < len; i++){
5794             if(this.config[i].dataIndex == dataIndex){
5795                 return i;
5796             }
5797         }
5798         return -1;
5799     },
5800     
5801     
5802     moveColumn : function(oldIndex, newIndex){
5803         var c = this.config[oldIndex];
5804         this.config.splice(oldIndex, 1);
5805         this.config.splice(newIndex, 0, c);
5806         this.dataMap = null;
5807         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5808     },
5809
5810     isLocked : function(colIndex){
5811         return this.config[colIndex].locked === true;
5812     },
5813
5814     setLocked : function(colIndex, value, suppressEvent){
5815         if(this.isLocked(colIndex) == value){
5816             return;
5817         }
5818         this.config[colIndex].locked = value;
5819         if(!suppressEvent){
5820             this.fireEvent("columnlockchange", this, colIndex, value);
5821         }
5822     },
5823
5824     getTotalLockedWidth : function(){
5825         var totalWidth = 0;
5826         for(var i = 0; i < this.config.length; i++){
5827             if(this.isLocked(i) && !this.isHidden(i)){
5828                 this.totalWidth += this.getColumnWidth(i);
5829             }
5830         }
5831         return totalWidth;
5832     },
5833
5834     getLockedCount : function(){
5835         for(var i = 0, len = this.config.length; i < len; i++){
5836             if(!this.isLocked(i)){
5837                 return i;
5838             }
5839         }
5840         
5841         return this.config.length;
5842     },
5843
5844     /**
5845      * Returns the number of columns.
5846      * @return {Number}
5847      */
5848     getColumnCount : function(visibleOnly){
5849         if(visibleOnly === true){
5850             var c = 0;
5851             for(var i = 0, len = this.config.length; i < len; i++){
5852                 if(!this.isHidden(i)){
5853                     c++;
5854                 }
5855             }
5856             return c;
5857         }
5858         return this.config.length;
5859     },
5860
5861     /**
5862      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5863      * @param {Function} fn
5864      * @param {Object} scope (optional)
5865      * @return {Array} result
5866      */
5867     getColumnsBy : function(fn, scope){
5868         var r = [];
5869         for(var i = 0, len = this.config.length; i < len; i++){
5870             var c = this.config[i];
5871             if(fn.call(scope||this, c, i) === true){
5872                 r[r.length] = c;
5873             }
5874         }
5875         return r;
5876     },
5877
5878     /**
5879      * Returns true if the specified column is sortable.
5880      * @param {Number} col The column index
5881      * @return {Boolean}
5882      */
5883     isSortable : function(col){
5884         if(typeof this.config[col].sortable == "undefined"){
5885             return this.defaultSortable;
5886         }
5887         return this.config[col].sortable;
5888     },
5889
5890     /**
5891      * Returns the rendering (formatting) function defined for the column.
5892      * @param {Number} col The column index.
5893      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5894      */
5895     getRenderer : function(col){
5896         if(!this.config[col].renderer){
5897             return Roo.grid.ColumnModel.defaultRenderer;
5898         }
5899         return this.config[col].renderer;
5900     },
5901
5902     /**
5903      * Sets the rendering (formatting) function for a column.
5904      * @param {Number} col The column index
5905      * @param {Function} fn The function to use to process the cell's raw data
5906      * to return HTML markup for the grid view. The render function is called with
5907      * the following parameters:<ul>
5908      * <li>Data value.</li>
5909      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5910      * <li>css A CSS style string to apply to the table cell.</li>
5911      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5912      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5913      * <li>Row index</li>
5914      * <li>Column index</li>
5915      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5916      */
5917     setRenderer : function(col, fn){
5918         this.config[col].renderer = fn;
5919     },
5920
5921     /**
5922      * Returns the width for the specified column.
5923      * @param {Number} col The column index
5924      * @return {Number}
5925      */
5926     getColumnWidth : function(col){
5927         return this.config[col].width * 1 || this.defaultWidth;
5928     },
5929
5930     /**
5931      * Sets the width for a column.
5932      * @param {Number} col The column index
5933      * @param {Number} width The new width
5934      */
5935     setColumnWidth : function(col, width, suppressEvent){
5936         this.config[col].width = width;
5937         this.totalWidth = null;
5938         if(!suppressEvent){
5939              this.fireEvent("widthchange", this, col, width);
5940         }
5941     },
5942
5943     /**
5944      * Returns the total width of all columns.
5945      * @param {Boolean} includeHidden True to include hidden column widths
5946      * @return {Number}
5947      */
5948     getTotalWidth : function(includeHidden){
5949         if(!this.totalWidth){
5950             this.totalWidth = 0;
5951             for(var i = 0, len = this.config.length; i < len; i++){
5952                 if(includeHidden || !this.isHidden(i)){
5953                     this.totalWidth += this.getColumnWidth(i);
5954                 }
5955             }
5956         }
5957         return this.totalWidth;
5958     },
5959
5960     /**
5961      * Returns the header for the specified column.
5962      * @param {Number} col The column index
5963      * @return {String}
5964      */
5965     getColumnHeader : function(col){
5966         return this.config[col].header;
5967     },
5968
5969     /**
5970      * Sets the header for a column.
5971      * @param {Number} col The column index
5972      * @param {String} header The new header
5973      */
5974     setColumnHeader : function(col, header){
5975         this.config[col].header = header;
5976         this.fireEvent("headerchange", this, col, header);
5977     },
5978
5979     /**
5980      * Returns the tooltip for the specified column.
5981      * @param {Number} col The column index
5982      * @return {String}
5983      */
5984     getColumnTooltip : function(col){
5985             return this.config[col].tooltip;
5986     },
5987     /**
5988      * Sets the tooltip for a column.
5989      * @param {Number} col The column index
5990      * @param {String} tooltip The new tooltip
5991      */
5992     setColumnTooltip : function(col, tooltip){
5993             this.config[col].tooltip = tooltip;
5994     },
5995
5996     /**
5997      * Returns the dataIndex for the specified column.
5998      * @param {Number} col The column index
5999      * @return {Number}
6000      */
6001     getDataIndex : function(col){
6002         return this.config[col].dataIndex;
6003     },
6004
6005     /**
6006      * Sets the dataIndex for a column.
6007      * @param {Number} col The column index
6008      * @param {Number} dataIndex The new dataIndex
6009      */
6010     setDataIndex : function(col, dataIndex){
6011         this.config[col].dataIndex = dataIndex;
6012     },
6013
6014     
6015     
6016     /**
6017      * Returns true if the cell is editable.
6018      * @param {Number} colIndex The column index
6019      * @param {Number} rowIndex The row index - this is nto actually used..?
6020      * @return {Boolean}
6021      */
6022     isCellEditable : function(colIndex, rowIndex){
6023         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6024     },
6025
6026     /**
6027      * Returns the editor defined for the cell/column.
6028      * return false or null to disable editing.
6029      * @param {Number} colIndex The column index
6030      * @param {Number} rowIndex The row index
6031      * @return {Object}
6032      */
6033     getCellEditor : function(colIndex, rowIndex){
6034         return this.config[colIndex].editor;
6035     },
6036
6037     /**
6038      * Sets if a column is editable.
6039      * @param {Number} col The column index
6040      * @param {Boolean} editable True if the column is editable
6041      */
6042     setEditable : function(col, editable){
6043         this.config[col].editable = editable;
6044     },
6045
6046
6047     /**
6048      * Returns true if the column is hidden.
6049      * @param {Number} colIndex The column index
6050      * @return {Boolean}
6051      */
6052     isHidden : function(colIndex){
6053         return this.config[colIndex].hidden;
6054     },
6055
6056
6057     /**
6058      * Returns true if the column width cannot be changed
6059      */
6060     isFixed : function(colIndex){
6061         return this.config[colIndex].fixed;
6062     },
6063
6064     /**
6065      * Returns true if the column can be resized
6066      * @return {Boolean}
6067      */
6068     isResizable : function(colIndex){
6069         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6070     },
6071     /**
6072      * Sets if a column is hidden.
6073      * @param {Number} colIndex The column index
6074      * @param {Boolean} hidden True if the column is hidden
6075      */
6076     setHidden : function(colIndex, hidden){
6077         this.config[colIndex].hidden = hidden;
6078         this.totalWidth = null;
6079         this.fireEvent("hiddenchange", this, colIndex, hidden);
6080     },
6081
6082     /**
6083      * Sets the editor for a column.
6084      * @param {Number} col The column index
6085      * @param {Object} editor The editor object
6086      */
6087     setEditor : function(col, editor){
6088         this.config[col].editor = editor;
6089     }
6090 });
6091
6092 Roo.grid.ColumnModel.defaultRenderer = function(value)
6093 {
6094     if(typeof value == "object") {
6095         return value;
6096     }
6097         if(typeof value == "string" && value.length < 1){
6098             return "&#160;";
6099         }
6100     
6101         return String.format("{0}", value);
6102 };
6103
6104 // Alias for backwards compatibility
6105 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6106 /*
6107  * Based on:
6108  * Ext JS Library 1.1.1
6109  * Copyright(c) 2006-2007, Ext JS, LLC.
6110  *
6111  * Originally Released Under LGPL - original licence link has changed is not relivant.
6112  *
6113  * Fork - LGPL
6114  * <script type="text/javascript">
6115  */
6116  
6117 /**
6118  * @class Roo.LoadMask
6119  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6120  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6121  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6122  * element's UpdateManager load indicator and will be destroyed after the initial load.
6123  * @constructor
6124  * Create a new LoadMask
6125  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6126  * @param {Object} config The config object
6127  */
6128 Roo.LoadMask = function(el, config){
6129     this.el = Roo.get(el);
6130     Roo.apply(this, config);
6131     if(this.store){
6132         this.store.on('beforeload', this.onBeforeLoad, this);
6133         this.store.on('load', this.onLoad, this);
6134         this.store.on('loadexception', this.onLoadException, this);
6135         this.removeMask = false;
6136     }else{
6137         var um = this.el.getUpdateManager();
6138         um.showLoadIndicator = false; // disable the default indicator
6139         um.on('beforeupdate', this.onBeforeLoad, this);
6140         um.on('update', this.onLoad, this);
6141         um.on('failure', this.onLoad, this);
6142         this.removeMask = true;
6143     }
6144 };
6145
6146 Roo.LoadMask.prototype = {
6147     /**
6148      * @cfg {Boolean} removeMask
6149      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6150      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6151      */
6152     /**
6153      * @cfg {String} msg
6154      * The text to display in a centered loading message box (defaults to 'Loading...')
6155      */
6156     msg : 'Loading...',
6157     /**
6158      * @cfg {String} msgCls
6159      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6160      */
6161     msgCls : 'x-mask-loading',
6162
6163     /**
6164      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6165      * @type Boolean
6166      */
6167     disabled: false,
6168
6169     /**
6170      * Disables the mask to prevent it from being displayed
6171      */
6172     disable : function(){
6173        this.disabled = true;
6174     },
6175
6176     /**
6177      * Enables the mask so that it can be displayed
6178      */
6179     enable : function(){
6180         this.disabled = false;
6181     },
6182     
6183     onLoadException : function()
6184     {
6185         Roo.log(arguments);
6186         
6187         if (typeof(arguments[3]) != 'undefined') {
6188             Roo.MessageBox.alert("Error loading",arguments[3]);
6189         } 
6190         /*
6191         try {
6192             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6193                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6194             }   
6195         } catch(e) {
6196             
6197         }
6198         */
6199     
6200         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6201     },
6202     // private
6203     onLoad : function()
6204     {
6205         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6206     },
6207
6208     // private
6209     onBeforeLoad : function(){
6210         if(!this.disabled){
6211             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6212         }
6213     },
6214
6215     // private
6216     destroy : function(){
6217         if(this.store){
6218             this.store.un('beforeload', this.onBeforeLoad, this);
6219             this.store.un('load', this.onLoad, this);
6220             this.store.un('loadexception', this.onLoadException, this);
6221         }else{
6222             var um = this.el.getUpdateManager();
6223             um.un('beforeupdate', this.onBeforeLoad, this);
6224             um.un('update', this.onLoad, this);
6225             um.un('failure', this.onLoad, this);
6226         }
6227     }
6228 };/*
6229  * - LGPL
6230  *
6231  * table
6232  * 
6233  */
6234
6235 /**
6236  * @class Roo.bootstrap.Table
6237  * @extends Roo.bootstrap.Component
6238  * Bootstrap Table class
6239  * @cfg {String} cls table class
6240  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6241  * @cfg {String} bgcolor Specifies the background color for a table
6242  * @cfg {Number} border Specifies whether the table cells should have borders or not
6243  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6244  * @cfg {Number} cellspacing Specifies the space between cells
6245  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6246  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6247  * @cfg {String} sortable Specifies that the table should be sortable
6248  * @cfg {String} summary Specifies a summary of the content of a table
6249  * @cfg {Number} width Specifies the width of a table
6250  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6251  * 
6252  * @cfg {boolean} striped Should the rows be alternative striped
6253  * @cfg {boolean} bordered Add borders to the table
6254  * @cfg {boolean} hover Add hover highlighting
6255  * @cfg {boolean} condensed Format condensed
6256  * @cfg {boolean} responsive Format condensed
6257  * @cfg {Boolean} loadMask (true|false) default false
6258  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6259  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6260  * @cfg {Boolean} rowSelection (true|false) default false
6261  * @cfg {Boolean} cellSelection (true|false) default false
6262  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6263  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6264  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6265  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6266  
6267  * 
6268  * @constructor
6269  * Create a new Table
6270  * @param {Object} config The config object
6271  */
6272
6273 Roo.bootstrap.Table = function(config){
6274     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6275     
6276   
6277     
6278     // BC...
6279     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6280     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6281     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6282     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6283     
6284     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6285     if (this.sm) {
6286         this.sm.grid = this;
6287         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6288         this.sm = this.selModel;
6289         this.sm.xmodule = this.xmodule || false;
6290     }
6291     
6292     if (this.cm && typeof(this.cm.config) == 'undefined') {
6293         this.colModel = new Roo.grid.ColumnModel(this.cm);
6294         this.cm = this.colModel;
6295         this.cm.xmodule = this.xmodule || false;
6296     }
6297     if (this.store) {
6298         this.store= Roo.factory(this.store, Roo.data);
6299         this.ds = this.store;
6300         this.ds.xmodule = this.xmodule || false;
6301          
6302     }
6303     if (this.footer && this.store) {
6304         this.footer.dataSource = this.ds;
6305         this.footer = Roo.factory(this.footer);
6306     }
6307     
6308     /** @private */
6309     this.addEvents({
6310         /**
6311          * @event cellclick
6312          * Fires when a cell is clicked
6313          * @param {Roo.bootstrap.Table} this
6314          * @param {Roo.Element} el
6315          * @param {Number} rowIndex
6316          * @param {Number} columnIndex
6317          * @param {Roo.EventObject} e
6318          */
6319         "cellclick" : true,
6320         /**
6321          * @event celldblclick
6322          * Fires when a cell is double clicked
6323          * @param {Roo.bootstrap.Table} this
6324          * @param {Roo.Element} el
6325          * @param {Number} rowIndex
6326          * @param {Number} columnIndex
6327          * @param {Roo.EventObject} e
6328          */
6329         "celldblclick" : true,
6330         /**
6331          * @event rowclick
6332          * Fires when a row is clicked
6333          * @param {Roo.bootstrap.Table} this
6334          * @param {Roo.Element} el
6335          * @param {Number} rowIndex
6336          * @param {Roo.EventObject} e
6337          */
6338         "rowclick" : true,
6339         /**
6340          * @event rowdblclick
6341          * Fires when a row is double clicked
6342          * @param {Roo.bootstrap.Table} this
6343          * @param {Roo.Element} el
6344          * @param {Number} rowIndex
6345          * @param {Roo.EventObject} e
6346          */
6347         "rowdblclick" : true,
6348         /**
6349          * @event mouseover
6350          * Fires when a mouseover occur
6351          * @param {Roo.bootstrap.Table} this
6352          * @param {Roo.Element} el
6353          * @param {Number} rowIndex
6354          * @param {Number} columnIndex
6355          * @param {Roo.EventObject} e
6356          */
6357         "mouseover" : true,
6358         /**
6359          * @event mouseout
6360          * Fires when a mouseout occur
6361          * @param {Roo.bootstrap.Table} this
6362          * @param {Roo.Element} el
6363          * @param {Number} rowIndex
6364          * @param {Number} columnIndex
6365          * @param {Roo.EventObject} e
6366          */
6367         "mouseout" : true,
6368         /**
6369          * @event rowclass
6370          * Fires when a row is rendered, so you can change add a style to it.
6371          * @param {Roo.bootstrap.Table} this
6372          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6373          */
6374         'rowclass' : true,
6375           /**
6376          * @event rowsrendered
6377          * Fires when all the  rows have been rendered
6378          * @param {Roo.bootstrap.Table} this
6379          */
6380         'rowsrendered' : true,
6381         /**
6382          * @event contextmenu
6383          * The raw contextmenu event for the entire grid.
6384          * @param {Roo.EventObject} e
6385          */
6386         "contextmenu" : true,
6387         /**
6388          * @event rowcontextmenu
6389          * Fires when a row is right clicked
6390          * @param {Roo.bootstrap.Table} this
6391          * @param {Number} rowIndex
6392          * @param {Roo.EventObject} e
6393          */
6394         "rowcontextmenu" : true,
6395         /**
6396          * @event cellcontextmenu
6397          * Fires when a cell is right clicked
6398          * @param {Roo.bootstrap.Table} this
6399          * @param {Number} rowIndex
6400          * @param {Number} cellIndex
6401          * @param {Roo.EventObject} e
6402          */
6403          "cellcontextmenu" : true,
6404          /**
6405          * @event headercontextmenu
6406          * Fires when a header is right clicked
6407          * @param {Roo.bootstrap.Table} this
6408          * @param {Number} columnIndex
6409          * @param {Roo.EventObject} e
6410          */
6411         "headercontextmenu" : true
6412     });
6413 };
6414
6415 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6416     
6417     cls: false,
6418     align: false,
6419     bgcolor: false,
6420     border: false,
6421     cellpadding: false,
6422     cellspacing: false,
6423     frame: false,
6424     rules: false,
6425     sortable: false,
6426     summary: false,
6427     width: false,
6428     striped : false,
6429     scrollBody : false,
6430     bordered: false,
6431     hover:  false,
6432     condensed : false,
6433     responsive : false,
6434     sm : false,
6435     cm : false,
6436     store : false,
6437     loadMask : false,
6438     footerShow : true,
6439     headerShow : true,
6440   
6441     rowSelection : false,
6442     cellSelection : false,
6443     layout : false,
6444     
6445     // Roo.Element - the tbody
6446     mainBody: false,
6447     // Roo.Element - thead element
6448     mainHead: false,
6449     
6450     container: false, // used by gridpanel...
6451     
6452     lazyLoad : false,
6453     
6454     CSS : Roo.util.CSS,
6455     
6456     auto_hide_footer : false,
6457     
6458     getAutoCreate : function()
6459     {
6460         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6461         
6462         cfg = {
6463             tag: 'table',
6464             cls : 'table',
6465             cn : []
6466         };
6467         if (this.scrollBody) {
6468             cfg.cls += ' table-body-fixed';
6469         }    
6470         if (this.striped) {
6471             cfg.cls += ' table-striped';
6472         }
6473         
6474         if (this.hover) {
6475             cfg.cls += ' table-hover';
6476         }
6477         if (this.bordered) {
6478             cfg.cls += ' table-bordered';
6479         }
6480         if (this.condensed) {
6481             cfg.cls += ' table-condensed';
6482         }
6483         if (this.responsive) {
6484             cfg.cls += ' table-responsive';
6485         }
6486         
6487         if (this.cls) {
6488             cfg.cls+=  ' ' +this.cls;
6489         }
6490         
6491         // this lot should be simplifed...
6492         var _t = this;
6493         var cp = [
6494             'align',
6495             'bgcolor',
6496             'border',
6497             'cellpadding',
6498             'cellspacing',
6499             'frame',
6500             'rules',
6501             'sortable',
6502             'summary',
6503             'width'
6504         ].forEach(function(k) {
6505             if (_t[k]) {
6506                 cfg[k] = _t[k];
6507             }
6508         });
6509         
6510         
6511         if (this.layout) {
6512             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6513         }
6514         
6515         if(this.store || this.cm){
6516             if(this.headerShow){
6517                 cfg.cn.push(this.renderHeader());
6518             }
6519             
6520             cfg.cn.push(this.renderBody());
6521             
6522             if(this.footerShow){
6523                 cfg.cn.push(this.renderFooter());
6524             }
6525             // where does this come from?
6526             //cfg.cls+=  ' TableGrid';
6527         }
6528         
6529         return { cn : [ cfg ] };
6530     },
6531     
6532     initEvents : function()
6533     {   
6534         if(!this.store || !this.cm){
6535             return;
6536         }
6537         if (this.selModel) {
6538             this.selModel.initEvents();
6539         }
6540         
6541         
6542         //Roo.log('initEvents with ds!!!!');
6543         
6544         this.mainBody = this.el.select('tbody', true).first();
6545         this.mainHead = this.el.select('thead', true).first();
6546         this.mainFoot = this.el.select('tfoot', true).first();
6547         
6548         
6549         
6550         var _this = this;
6551         
6552         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6553             e.on('click', _this.sort, _this);
6554         });
6555         
6556         this.mainBody.on("click", this.onClick, this);
6557         this.mainBody.on("dblclick", this.onDblClick, this);
6558         
6559         // why is this done????? = it breaks dialogs??
6560         //this.parent().el.setStyle('position', 'relative');
6561         
6562         
6563         if (this.footer) {
6564             this.footer.parentId = this.id;
6565             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6566             
6567             if(this.lazyLoad){
6568                 this.el.select('tfoot tr td').first().addClass('hide');
6569             }
6570         } 
6571         
6572         if(this.loadMask) {
6573             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6574         }
6575         
6576         this.store.on('load', this.onLoad, this);
6577         this.store.on('beforeload', this.onBeforeLoad, this);
6578         this.store.on('update', this.onUpdate, this);
6579         this.store.on('add', this.onAdd, this);
6580         this.store.on("clear", this.clear, this);
6581         
6582         this.el.on("contextmenu", this.onContextMenu, this);
6583         
6584         this.mainBody.on('scroll', this.onBodyScroll, this);
6585         
6586         this.cm.on("headerchange", this.onHeaderChange, this);
6587         
6588         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6589         
6590     },
6591     
6592     onContextMenu : function(e, t)
6593     {
6594         this.processEvent("contextmenu", e);
6595     },
6596     
6597     processEvent : function(name, e)
6598     {
6599         if (name != 'touchstart' ) {
6600             this.fireEvent(name, e);    
6601         }
6602         
6603         var t = e.getTarget();
6604         
6605         var cell = Roo.get(t);
6606         
6607         if(!cell){
6608             return;
6609         }
6610         
6611         if(cell.findParent('tfoot', false, true)){
6612             return;
6613         }
6614         
6615         if(cell.findParent('thead', false, true)){
6616             
6617             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6618                 cell = Roo.get(t).findParent('th', false, true);
6619                 if (!cell) {
6620                     Roo.log("failed to find th in thead?");
6621                     Roo.log(e.getTarget());
6622                     return;
6623                 }
6624             }
6625             
6626             var cellIndex = cell.dom.cellIndex;
6627             
6628             var ename = name == 'touchstart' ? 'click' : name;
6629             this.fireEvent("header" + ename, this, cellIndex, e);
6630             
6631             return;
6632         }
6633         
6634         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6635             cell = Roo.get(t).findParent('td', false, true);
6636             if (!cell) {
6637                 Roo.log("failed to find th in tbody?");
6638                 Roo.log(e.getTarget());
6639                 return;
6640             }
6641         }
6642         
6643         var row = cell.findParent('tr', false, true);
6644         var cellIndex = cell.dom.cellIndex;
6645         var rowIndex = row.dom.rowIndex - 1;
6646         
6647         if(row !== false){
6648             
6649             this.fireEvent("row" + name, this, rowIndex, e);
6650             
6651             if(cell !== false){
6652             
6653                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6654             }
6655         }
6656         
6657     },
6658     
6659     onMouseover : function(e, el)
6660     {
6661         var cell = Roo.get(el);
6662         
6663         if(!cell){
6664             return;
6665         }
6666         
6667         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6668             cell = cell.findParent('td', false, true);
6669         }
6670         
6671         var row = cell.findParent('tr', false, true);
6672         var cellIndex = cell.dom.cellIndex;
6673         var rowIndex = row.dom.rowIndex - 1; // start from 0
6674         
6675         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6676         
6677     },
6678     
6679     onMouseout : function(e, el)
6680     {
6681         var cell = Roo.get(el);
6682         
6683         if(!cell){
6684             return;
6685         }
6686         
6687         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6688             cell = cell.findParent('td', false, true);
6689         }
6690         
6691         var row = cell.findParent('tr', false, true);
6692         var cellIndex = cell.dom.cellIndex;
6693         var rowIndex = row.dom.rowIndex - 1; // start from 0
6694         
6695         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6696         
6697     },
6698     
6699     onClick : function(e, el)
6700     {
6701         var cell = Roo.get(el);
6702         
6703         if(!cell || (!this.cellSelection && !this.rowSelection)){
6704             return;
6705         }
6706         
6707         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6708             cell = cell.findParent('td', false, true);
6709         }
6710         
6711         if(!cell || typeof(cell) == 'undefined'){
6712             return;
6713         }
6714         
6715         var row = cell.findParent('tr', false, true);
6716         
6717         if(!row || typeof(row) == 'undefined'){
6718             return;
6719         }
6720         
6721         var cellIndex = cell.dom.cellIndex;
6722         var rowIndex = this.getRowIndex(row);
6723         
6724         // why??? - should these not be based on SelectionModel?
6725         if(this.cellSelection){
6726             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6727         }
6728         
6729         if(this.rowSelection){
6730             this.fireEvent('rowclick', this, row, rowIndex, e);
6731         }
6732         
6733         
6734     },
6735         
6736     onDblClick : function(e,el)
6737     {
6738         var cell = Roo.get(el);
6739         
6740         if(!cell || (!this.cellSelection && !this.rowSelection)){
6741             return;
6742         }
6743         
6744         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6745             cell = cell.findParent('td', false, true);
6746         }
6747         
6748         if(!cell || typeof(cell) == 'undefined'){
6749             return;
6750         }
6751         
6752         var row = cell.findParent('tr', false, true);
6753         
6754         if(!row || typeof(row) == 'undefined'){
6755             return;
6756         }
6757         
6758         var cellIndex = cell.dom.cellIndex;
6759         var rowIndex = this.getRowIndex(row);
6760         
6761         if(this.cellSelection){
6762             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6763         }
6764         
6765         if(this.rowSelection){
6766             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6767         }
6768     },
6769     
6770     sort : function(e,el)
6771     {
6772         var col = Roo.get(el);
6773         
6774         if(!col.hasClass('sortable')){
6775             return;
6776         }
6777         
6778         var sort = col.attr('sort');
6779         var dir = 'ASC';
6780         
6781         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6782             dir = 'DESC';
6783         }
6784         
6785         this.store.sortInfo = {field : sort, direction : dir};
6786         
6787         if (this.footer) {
6788             Roo.log("calling footer first");
6789             this.footer.onClick('first');
6790         } else {
6791         
6792             this.store.load({ params : { start : 0 } });
6793         }
6794     },
6795     
6796     renderHeader : function()
6797     {
6798         var header = {
6799             tag: 'thead',
6800             cn : []
6801         };
6802         
6803         var cm = this.cm;
6804         this.totalWidth = 0;
6805         
6806         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6807             
6808             var config = cm.config[i];
6809             
6810             var c = {
6811                 tag: 'th',
6812                 cls : 'x-hcol-' + i,
6813                 style : '',
6814                 html: cm.getColumnHeader(i)
6815             };
6816             
6817             var hh = '';
6818             
6819             if(typeof(config.sortable) != 'undefined' && config.sortable){
6820                 c.cls = 'sortable';
6821                 c.html = '<i class="glyphicon"></i>' + c.html;
6822             }
6823             
6824             // could use BS4 hidden-..-down 
6825             
6826             if(typeof(config.lgHeader) != 'undefined'){
6827                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
6828             }
6829             
6830             if(typeof(config.mdHeader) != 'undefined'){
6831                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6832             }
6833             
6834             if(typeof(config.smHeader) != 'undefined'){
6835                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6836             }
6837             
6838             if(typeof(config.xsHeader) != 'undefined'){
6839                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6840             }
6841             
6842             if(hh.length){
6843                 c.html = hh;
6844             }
6845             
6846             if(typeof(config.tooltip) != 'undefined'){
6847                 c.tooltip = config.tooltip;
6848             }
6849             
6850             if(typeof(config.colspan) != 'undefined'){
6851                 c.colspan = config.colspan;
6852             }
6853             
6854             if(typeof(config.hidden) != 'undefined' && config.hidden){
6855                 c.style += ' display:none;';
6856             }
6857             
6858             if(typeof(config.dataIndex) != 'undefined'){
6859                 c.sort = config.dataIndex;
6860             }
6861             
6862            
6863             
6864             if(typeof(config.align) != 'undefined' && config.align.length){
6865                 c.style += ' text-align:' + config.align + ';';
6866             }
6867             
6868             if(typeof(config.width) != 'undefined'){
6869                 c.style += ' width:' + config.width + 'px;';
6870                 this.totalWidth += config.width;
6871             } else {
6872                 this.totalWidth += 100; // assume minimum of 100 per column?
6873             }
6874             
6875             if(typeof(config.cls) != 'undefined'){
6876                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6877             }
6878             
6879             ['xs','sm','md','lg'].map(function(size){
6880                 
6881                 if(typeof(config[size]) == 'undefined'){
6882                     return;
6883                 }
6884                  
6885                 if (!config[size]) { // 0 = hidden
6886                     // BS 4 '0' is treated as hide that column and below.
6887                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
6888                     return;
6889                 }
6890                 
6891                 c.cls += ' col-' + size + '-' + config[size] + (
6892                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
6893                 );
6894                 
6895                 
6896             });
6897             
6898             header.cn.push(c)
6899         }
6900         
6901         return header;
6902     },
6903     
6904     renderBody : function()
6905     {
6906         var body = {
6907             tag: 'tbody',
6908             cn : [
6909                 {
6910                     tag: 'tr',
6911                     cn : [
6912                         {
6913                             tag : 'td',
6914                             colspan :  this.cm.getColumnCount()
6915                         }
6916                     ]
6917                 }
6918             ]
6919         };
6920         
6921         return body;
6922     },
6923     
6924     renderFooter : function()
6925     {
6926         var footer = {
6927             tag: 'tfoot',
6928             cn : [
6929                 {
6930                     tag: 'tr',
6931                     cn : [
6932                         {
6933                             tag : 'td',
6934                             colspan :  this.cm.getColumnCount()
6935                         }
6936                     ]
6937                 }
6938             ]
6939         };
6940         
6941         return footer;
6942     },
6943     
6944     
6945     
6946     onLoad : function()
6947     {
6948 //        Roo.log('ds onload');
6949         this.clear();
6950         
6951         var _this = this;
6952         var cm = this.cm;
6953         var ds = this.store;
6954         
6955         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6956             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6957             if (_this.store.sortInfo) {
6958                     
6959                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6960                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6961                 }
6962                 
6963                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6964                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6965                 }
6966             }
6967         });
6968         
6969         var tbody =  this.mainBody;
6970               
6971         if(ds.getCount() > 0){
6972             ds.data.each(function(d,rowIndex){
6973                 var row =  this.renderRow(cm, ds, rowIndex);
6974                 
6975                 tbody.createChild(row);
6976                 
6977                 var _this = this;
6978                 
6979                 if(row.cellObjects.length){
6980                     Roo.each(row.cellObjects, function(r){
6981                         _this.renderCellObject(r);
6982                     })
6983                 }
6984                 
6985             }, this);
6986         }
6987         
6988         var tfoot = this.el.select('tfoot', true).first();
6989         
6990         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6991             
6992             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6993             
6994             var total = this.ds.getTotalCount();
6995             
6996             if(this.footer.pageSize < total){
6997                 this.mainFoot.show();
6998             }
6999         }
7000         
7001         Roo.each(this.el.select('tbody td', true).elements, function(e){
7002             e.on('mouseover', _this.onMouseover, _this);
7003         });
7004         
7005         Roo.each(this.el.select('tbody td', true).elements, function(e){
7006             e.on('mouseout', _this.onMouseout, _this);
7007         });
7008         this.fireEvent('rowsrendered', this);
7009         
7010         this.autoSize();
7011     },
7012     
7013     
7014     onUpdate : function(ds,record)
7015     {
7016         this.refreshRow(record);
7017         this.autoSize();
7018     },
7019     
7020     onRemove : function(ds, record, index, isUpdate){
7021         if(isUpdate !== true){
7022             this.fireEvent("beforerowremoved", this, index, record);
7023         }
7024         var bt = this.mainBody.dom;
7025         
7026         var rows = this.el.select('tbody > tr', true).elements;
7027         
7028         if(typeof(rows[index]) != 'undefined'){
7029             bt.removeChild(rows[index].dom);
7030         }
7031         
7032 //        if(bt.rows[index]){
7033 //            bt.removeChild(bt.rows[index]);
7034 //        }
7035         
7036         if(isUpdate !== true){
7037             //this.stripeRows(index);
7038             //this.syncRowHeights(index, index);
7039             //this.layout();
7040             this.fireEvent("rowremoved", this, index, record);
7041         }
7042     },
7043     
7044     onAdd : function(ds, records, rowIndex)
7045     {
7046         //Roo.log('on Add called');
7047         // - note this does not handle multiple adding very well..
7048         var bt = this.mainBody.dom;
7049         for (var i =0 ; i < records.length;i++) {
7050             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7051             //Roo.log(records[i]);
7052             //Roo.log(this.store.getAt(rowIndex+i));
7053             this.insertRow(this.store, rowIndex + i, false);
7054             return;
7055         }
7056         
7057     },
7058     
7059     
7060     refreshRow : function(record){
7061         var ds = this.store, index;
7062         if(typeof record == 'number'){
7063             index = record;
7064             record = ds.getAt(index);
7065         }else{
7066             index = ds.indexOf(record);
7067         }
7068         this.insertRow(ds, index, true);
7069         this.autoSize();
7070         this.onRemove(ds, record, index+1, true);
7071         this.autoSize();
7072         //this.syncRowHeights(index, index);
7073         //this.layout();
7074         this.fireEvent("rowupdated", this, index, record);
7075     },
7076     
7077     insertRow : function(dm, rowIndex, isUpdate){
7078         
7079         if(!isUpdate){
7080             this.fireEvent("beforerowsinserted", this, rowIndex);
7081         }
7082             //var s = this.getScrollState();
7083         var row = this.renderRow(this.cm, this.store, rowIndex);
7084         // insert before rowIndex..
7085         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7086         
7087         var _this = this;
7088                 
7089         if(row.cellObjects.length){
7090             Roo.each(row.cellObjects, function(r){
7091                 _this.renderCellObject(r);
7092             })
7093         }
7094             
7095         if(!isUpdate){
7096             this.fireEvent("rowsinserted", this, rowIndex);
7097             //this.syncRowHeights(firstRow, lastRow);
7098             //this.stripeRows(firstRow);
7099             //this.layout();
7100         }
7101         
7102     },
7103     
7104     
7105     getRowDom : function(rowIndex)
7106     {
7107         var rows = this.el.select('tbody > tr', true).elements;
7108         
7109         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7110         
7111     },
7112     // returns the object tree for a tr..
7113   
7114     
7115     renderRow : function(cm, ds, rowIndex) 
7116     {
7117         var d = ds.getAt(rowIndex);
7118         
7119         var row = {
7120             tag : 'tr',
7121             cls : 'x-row-' + rowIndex,
7122             cn : []
7123         };
7124             
7125         var cellObjects = [];
7126         
7127         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7128             var config = cm.config[i];
7129             
7130             var renderer = cm.getRenderer(i);
7131             var value = '';
7132             var id = false;
7133             
7134             if(typeof(renderer) !== 'undefined'){
7135                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7136             }
7137             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7138             // and are rendered into the cells after the row is rendered - using the id for the element.
7139             
7140             if(typeof(value) === 'object'){
7141                 id = Roo.id();
7142                 cellObjects.push({
7143                     container : id,
7144                     cfg : value 
7145                 })
7146             }
7147             
7148             var rowcfg = {
7149                 record: d,
7150                 rowIndex : rowIndex,
7151                 colIndex : i,
7152                 rowClass : ''
7153             };
7154
7155             this.fireEvent('rowclass', this, rowcfg);
7156             
7157             var td = {
7158                 tag: 'td',
7159                 cls : rowcfg.rowClass + ' x-col-' + i,
7160                 style: '',
7161                 html: (typeof(value) === 'object') ? '' : value
7162             };
7163             
7164             if (id) {
7165                 td.id = id;
7166             }
7167             
7168             if(typeof(config.colspan) != 'undefined'){
7169                 td.colspan = config.colspan;
7170             }
7171             
7172             if(typeof(config.hidden) != 'undefined' && config.hidden){
7173                 td.style += ' display:none;';
7174             }
7175             
7176             if(typeof(config.align) != 'undefined' && config.align.length){
7177                 td.style += ' text-align:' + config.align + ';';
7178             }
7179             if(typeof(config.valign) != 'undefined' && config.valign.length){
7180                 td.style += ' vertical-align:' + config.valign + ';';
7181             }
7182             
7183             if(typeof(config.width) != 'undefined'){
7184                 td.style += ' width:' +  config.width + 'px;';
7185             }
7186             
7187             if(typeof(config.cursor) != 'undefined'){
7188                 td.style += ' cursor:' +  config.cursor + ';';
7189             }
7190             
7191             if(typeof(config.cls) != 'undefined'){
7192                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7193             }
7194             
7195             ['xs','sm','md','lg'].map(function(size){
7196                 
7197                 if(typeof(config[size]) == 'undefined'){
7198                     return;
7199                 }
7200                 
7201                 
7202                   
7203                 if (!config[size]) { // 0 = hidden
7204                     // BS 4 '0' is treated as hide that column and below.
7205                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7206                     return;
7207                 }
7208                 
7209                 td.cls += ' col-' + size + '-' + config[size] + (
7210                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
7211                 );
7212                  
7213
7214             });
7215             
7216             row.cn.push(td);
7217            
7218         }
7219         
7220         row.cellObjects = cellObjects;
7221         
7222         return row;
7223           
7224     },
7225     
7226     
7227     
7228     onBeforeLoad : function()
7229     {
7230         
7231     },
7232      /**
7233      * Remove all rows
7234      */
7235     clear : function()
7236     {
7237         this.el.select('tbody', true).first().dom.innerHTML = '';
7238     },
7239     /**
7240      * Show or hide a row.
7241      * @param {Number} rowIndex to show or hide
7242      * @param {Boolean} state hide
7243      */
7244     setRowVisibility : function(rowIndex, state)
7245     {
7246         var bt = this.mainBody.dom;
7247         
7248         var rows = this.el.select('tbody > tr', true).elements;
7249         
7250         if(typeof(rows[rowIndex]) == 'undefined'){
7251             return;
7252         }
7253         rows[rowIndex].dom.style.display = state ? '' : 'none';
7254     },
7255     
7256     
7257     getSelectionModel : function(){
7258         if(!this.selModel){
7259             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7260         }
7261         return this.selModel;
7262     },
7263     /*
7264      * Render the Roo.bootstrap object from renderder
7265      */
7266     renderCellObject : function(r)
7267     {
7268         var _this = this;
7269         
7270         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7271         
7272         var t = r.cfg.render(r.container);
7273         
7274         if(r.cfg.cn){
7275             Roo.each(r.cfg.cn, function(c){
7276                 var child = {
7277                     container: t.getChildContainer(),
7278                     cfg: c
7279                 };
7280                 _this.renderCellObject(child);
7281             })
7282         }
7283     },
7284     
7285     getRowIndex : function(row)
7286     {
7287         var rowIndex = -1;
7288         
7289         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7290             if(el != row){
7291                 return;
7292             }
7293             
7294             rowIndex = index;
7295         });
7296         
7297         return rowIndex;
7298     },
7299      /**
7300      * Returns the grid's underlying element = used by panel.Grid
7301      * @return {Element} The element
7302      */
7303     getGridEl : function(){
7304         return this.el;
7305     },
7306      /**
7307      * Forces a resize - used by panel.Grid
7308      * @return {Element} The element
7309      */
7310     autoSize : function()
7311     {
7312         //var ctr = Roo.get(this.container.dom.parentElement);
7313         var ctr = Roo.get(this.el.dom);
7314         
7315         var thd = this.getGridEl().select('thead',true).first();
7316         var tbd = this.getGridEl().select('tbody', true).first();
7317         var tfd = this.getGridEl().select('tfoot', true).first();
7318         
7319         var cw = ctr.getWidth();
7320         
7321         if (tbd) {
7322             
7323             tbd.setWidth(ctr.getWidth());
7324             // if the body has a max height - and then scrolls - we should perhaps set up the height here
7325             // this needs fixing for various usage - currently only hydra job advers I think..
7326             //tdb.setHeight(
7327             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7328             //); 
7329             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7330             cw -= barsize;
7331         }
7332         cw = Math.max(cw, this.totalWidth);
7333         this.getGridEl().select('tr',true).setWidth(cw);
7334         // resize 'expandable coloumn?
7335         
7336         return; // we doe not have a view in this design..
7337         
7338     },
7339     onBodyScroll: function()
7340     {
7341         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7342         if(this.mainHead){
7343             this.mainHead.setStyle({
7344                 'position' : 'relative',
7345                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7346             });
7347         }
7348         
7349         if(this.lazyLoad){
7350             
7351             var scrollHeight = this.mainBody.dom.scrollHeight;
7352             
7353             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7354             
7355             var height = this.mainBody.getHeight();
7356             
7357             if(scrollHeight - height == scrollTop) {
7358                 
7359                 var total = this.ds.getTotalCount();
7360                 
7361                 if(this.footer.cursor + this.footer.pageSize < total){
7362                     
7363                     this.footer.ds.load({
7364                         params : {
7365                             start : this.footer.cursor + this.footer.pageSize,
7366                             limit : this.footer.pageSize
7367                         },
7368                         add : true
7369                     });
7370                 }
7371             }
7372             
7373         }
7374     },
7375     
7376     onHeaderChange : function()
7377     {
7378         var header = this.renderHeader();
7379         var table = this.el.select('table', true).first();
7380         
7381         this.mainHead.remove();
7382         this.mainHead = table.createChild(header, this.mainBody, false);
7383     },
7384     
7385     onHiddenChange : function(colModel, colIndex, hidden)
7386     {
7387         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7388         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7389         
7390         this.CSS.updateRule(thSelector, "display", "");
7391         this.CSS.updateRule(tdSelector, "display", "");
7392         
7393         if(hidden){
7394             this.CSS.updateRule(thSelector, "display", "none");
7395             this.CSS.updateRule(tdSelector, "display", "none");
7396         }
7397         
7398         this.onHeaderChange();
7399         this.onLoad();
7400     },
7401     
7402     setColumnWidth: function(col_index, width)
7403     {
7404         // width = "md-2 xs-2..."
7405         if(!this.colModel.config[col_index]) {
7406             return;
7407         }
7408         
7409         var w = width.split(" ");
7410         
7411         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7412         
7413         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7414         
7415         
7416         for(var j = 0; j < w.length; j++) {
7417             
7418             if(!w[j]) {
7419                 continue;
7420             }
7421             
7422             var size_cls = w[j].split("-");
7423             
7424             if(!Number.isInteger(size_cls[1] * 1)) {
7425                 continue;
7426             }
7427             
7428             if(!this.colModel.config[col_index][size_cls[0]]) {
7429                 continue;
7430             }
7431             
7432             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7433                 continue;
7434             }
7435             
7436             h_row[0].classList.replace(
7437                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7438                 "col-"+size_cls[0]+"-"+size_cls[1]
7439             );
7440             
7441             for(var i = 0; i < rows.length; i++) {
7442                 
7443                 var size_cls = w[j].split("-");
7444                 
7445                 if(!Number.isInteger(size_cls[1] * 1)) {
7446                     continue;
7447                 }
7448                 
7449                 if(!this.colModel.config[col_index][size_cls[0]]) {
7450                     continue;
7451                 }
7452                 
7453                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7454                     continue;
7455                 }
7456                 
7457                 rows[i].classList.replace(
7458                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7459                     "col-"+size_cls[0]+"-"+size_cls[1]
7460                 );
7461             }
7462             
7463             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7464         }
7465     }
7466 });
7467
7468  
7469
7470  /*
7471  * - LGPL
7472  *
7473  * table cell
7474  * 
7475  */
7476
7477 /**
7478  * @class Roo.bootstrap.TableCell
7479  * @extends Roo.bootstrap.Component
7480  * Bootstrap TableCell class
7481  * @cfg {String} html cell contain text
7482  * @cfg {String} cls cell class
7483  * @cfg {String} tag cell tag (td|th) default td
7484  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7485  * @cfg {String} align Aligns the content in a cell
7486  * @cfg {String} axis Categorizes cells
7487  * @cfg {String} bgcolor Specifies the background color of a cell
7488  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7489  * @cfg {Number} colspan Specifies the number of columns a cell should span
7490  * @cfg {String} headers Specifies one or more header cells a cell is related to
7491  * @cfg {Number} height Sets the height of a cell
7492  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7493  * @cfg {Number} rowspan Sets the number of rows a cell should span
7494  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7495  * @cfg {String} valign Vertical aligns the content in a cell
7496  * @cfg {Number} width Specifies the width of a cell
7497  * 
7498  * @constructor
7499  * Create a new TableCell
7500  * @param {Object} config The config object
7501  */
7502
7503 Roo.bootstrap.TableCell = function(config){
7504     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7505 };
7506
7507 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7508     
7509     html: false,
7510     cls: false,
7511     tag: false,
7512     abbr: false,
7513     align: false,
7514     axis: false,
7515     bgcolor: false,
7516     charoff: false,
7517     colspan: false,
7518     headers: false,
7519     height: false,
7520     nowrap: false,
7521     rowspan: false,
7522     scope: false,
7523     valign: false,
7524     width: false,
7525     
7526     
7527     getAutoCreate : function(){
7528         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7529         
7530         cfg = {
7531             tag: 'td'
7532         };
7533         
7534         if(this.tag){
7535             cfg.tag = this.tag;
7536         }
7537         
7538         if (this.html) {
7539             cfg.html=this.html
7540         }
7541         if (this.cls) {
7542             cfg.cls=this.cls
7543         }
7544         if (this.abbr) {
7545             cfg.abbr=this.abbr
7546         }
7547         if (this.align) {
7548             cfg.align=this.align
7549         }
7550         if (this.axis) {
7551             cfg.axis=this.axis
7552         }
7553         if (this.bgcolor) {
7554             cfg.bgcolor=this.bgcolor
7555         }
7556         if (this.charoff) {
7557             cfg.charoff=this.charoff
7558         }
7559         if (this.colspan) {
7560             cfg.colspan=this.colspan
7561         }
7562         if (this.headers) {
7563             cfg.headers=this.headers
7564         }
7565         if (this.height) {
7566             cfg.height=this.height
7567         }
7568         if (this.nowrap) {
7569             cfg.nowrap=this.nowrap
7570         }
7571         if (this.rowspan) {
7572             cfg.rowspan=this.rowspan
7573         }
7574         if (this.scope) {
7575             cfg.scope=this.scope
7576         }
7577         if (this.valign) {
7578             cfg.valign=this.valign
7579         }
7580         if (this.width) {
7581             cfg.width=this.width
7582         }
7583         
7584         
7585         return cfg;
7586     }
7587    
7588 });
7589
7590  
7591
7592  /*
7593  * - LGPL
7594  *
7595  * table row
7596  * 
7597  */
7598
7599 /**
7600  * @class Roo.bootstrap.TableRow
7601  * @extends Roo.bootstrap.Component
7602  * Bootstrap TableRow class
7603  * @cfg {String} cls row class
7604  * @cfg {String} align Aligns the content in a table row
7605  * @cfg {String} bgcolor Specifies a background color for a table row
7606  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7607  * @cfg {String} valign Vertical aligns the content in a table row
7608  * 
7609  * @constructor
7610  * Create a new TableRow
7611  * @param {Object} config The config object
7612  */
7613
7614 Roo.bootstrap.TableRow = function(config){
7615     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7616 };
7617
7618 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7619     
7620     cls: false,
7621     align: false,
7622     bgcolor: false,
7623     charoff: false,
7624     valign: false,
7625     
7626     getAutoCreate : function(){
7627         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7628         
7629         cfg = {
7630             tag: 'tr'
7631         };
7632             
7633         if(this.cls){
7634             cfg.cls = this.cls;
7635         }
7636         if(this.align){
7637             cfg.align = this.align;
7638         }
7639         if(this.bgcolor){
7640             cfg.bgcolor = this.bgcolor;
7641         }
7642         if(this.charoff){
7643             cfg.charoff = this.charoff;
7644         }
7645         if(this.valign){
7646             cfg.valign = this.valign;
7647         }
7648         
7649         return cfg;
7650     }
7651    
7652 });
7653
7654  
7655
7656  /*
7657  * - LGPL
7658  *
7659  * table body
7660  * 
7661  */
7662
7663 /**
7664  * @class Roo.bootstrap.TableBody
7665  * @extends Roo.bootstrap.Component
7666  * Bootstrap TableBody class
7667  * @cfg {String} cls element class
7668  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7669  * @cfg {String} align Aligns the content inside the element
7670  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7671  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7672  * 
7673  * @constructor
7674  * Create a new TableBody
7675  * @param {Object} config The config object
7676  */
7677
7678 Roo.bootstrap.TableBody = function(config){
7679     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7680 };
7681
7682 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7683     
7684     cls: false,
7685     tag: false,
7686     align: false,
7687     charoff: false,
7688     valign: false,
7689     
7690     getAutoCreate : function(){
7691         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7692         
7693         cfg = {
7694             tag: 'tbody'
7695         };
7696             
7697         if (this.cls) {
7698             cfg.cls=this.cls
7699         }
7700         if(this.tag){
7701             cfg.tag = this.tag;
7702         }
7703         
7704         if(this.align){
7705             cfg.align = this.align;
7706         }
7707         if(this.charoff){
7708             cfg.charoff = this.charoff;
7709         }
7710         if(this.valign){
7711             cfg.valign = this.valign;
7712         }
7713         
7714         return cfg;
7715     }
7716     
7717     
7718 //    initEvents : function()
7719 //    {
7720 //        
7721 //        if(!this.store){
7722 //            return;
7723 //        }
7724 //        
7725 //        this.store = Roo.factory(this.store, Roo.data);
7726 //        this.store.on('load', this.onLoad, this);
7727 //        
7728 //        this.store.load();
7729 //        
7730 //    },
7731 //    
7732 //    onLoad: function () 
7733 //    {   
7734 //        this.fireEvent('load', this);
7735 //    }
7736 //    
7737 //   
7738 });
7739
7740  
7741
7742  /*
7743  * Based on:
7744  * Ext JS Library 1.1.1
7745  * Copyright(c) 2006-2007, Ext JS, LLC.
7746  *
7747  * Originally Released Under LGPL - original licence link has changed is not relivant.
7748  *
7749  * Fork - LGPL
7750  * <script type="text/javascript">
7751  */
7752
7753 // as we use this in bootstrap.
7754 Roo.namespace('Roo.form');
7755  /**
7756  * @class Roo.form.Action
7757  * Internal Class used to handle form actions
7758  * @constructor
7759  * @param {Roo.form.BasicForm} el The form element or its id
7760  * @param {Object} config Configuration options
7761  */
7762
7763  
7764  
7765 // define the action interface
7766 Roo.form.Action = function(form, options){
7767     this.form = form;
7768     this.options = options || {};
7769 };
7770 /**
7771  * Client Validation Failed
7772  * @const 
7773  */
7774 Roo.form.Action.CLIENT_INVALID = 'client';
7775 /**
7776  * Server Validation Failed
7777  * @const 
7778  */
7779 Roo.form.Action.SERVER_INVALID = 'server';
7780  /**
7781  * Connect to Server Failed
7782  * @const 
7783  */
7784 Roo.form.Action.CONNECT_FAILURE = 'connect';
7785 /**
7786  * Reading Data from Server Failed
7787  * @const 
7788  */
7789 Roo.form.Action.LOAD_FAILURE = 'load';
7790
7791 Roo.form.Action.prototype = {
7792     type : 'default',
7793     failureType : undefined,
7794     response : undefined,
7795     result : undefined,
7796
7797     // interface method
7798     run : function(options){
7799
7800     },
7801
7802     // interface method
7803     success : function(response){
7804
7805     },
7806
7807     // interface method
7808     handleResponse : function(response){
7809
7810     },
7811
7812     // default connection failure
7813     failure : function(response){
7814         
7815         this.response = response;
7816         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7817         this.form.afterAction(this, false);
7818     },
7819
7820     processResponse : function(response){
7821         this.response = response;
7822         if(!response.responseText){
7823             return true;
7824         }
7825         this.result = this.handleResponse(response);
7826         return this.result;
7827     },
7828
7829     // utility functions used internally
7830     getUrl : function(appendParams){
7831         var url = this.options.url || this.form.url || this.form.el.dom.action;
7832         if(appendParams){
7833             var p = this.getParams();
7834             if(p){
7835                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7836             }
7837         }
7838         return url;
7839     },
7840
7841     getMethod : function(){
7842         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7843     },
7844
7845     getParams : function(){
7846         var bp = this.form.baseParams;
7847         var p = this.options.params;
7848         if(p){
7849             if(typeof p == "object"){
7850                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7851             }else if(typeof p == 'string' && bp){
7852                 p += '&' + Roo.urlEncode(bp);
7853             }
7854         }else if(bp){
7855             p = Roo.urlEncode(bp);
7856         }
7857         return p;
7858     },
7859
7860     createCallback : function(){
7861         return {
7862             success: this.success,
7863             failure: this.failure,
7864             scope: this,
7865             timeout: (this.form.timeout*1000),
7866             upload: this.form.fileUpload ? this.success : undefined
7867         };
7868     }
7869 };
7870
7871 Roo.form.Action.Submit = function(form, options){
7872     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7873 };
7874
7875 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7876     type : 'submit',
7877
7878     haveProgress : false,
7879     uploadComplete : false,
7880     
7881     // uploadProgress indicator.
7882     uploadProgress : function()
7883     {
7884         if (!this.form.progressUrl) {
7885             return;
7886         }
7887         
7888         if (!this.haveProgress) {
7889             Roo.MessageBox.progress("Uploading", "Uploading");
7890         }
7891         if (this.uploadComplete) {
7892            Roo.MessageBox.hide();
7893            return;
7894         }
7895         
7896         this.haveProgress = true;
7897    
7898         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7899         
7900         var c = new Roo.data.Connection();
7901         c.request({
7902             url : this.form.progressUrl,
7903             params: {
7904                 id : uid
7905             },
7906             method: 'GET',
7907             success : function(req){
7908                //console.log(data);
7909                 var rdata = false;
7910                 var edata;
7911                 try  {
7912                    rdata = Roo.decode(req.responseText)
7913                 } catch (e) {
7914                     Roo.log("Invalid data from server..");
7915                     Roo.log(edata);
7916                     return;
7917                 }
7918                 if (!rdata || !rdata.success) {
7919                     Roo.log(rdata);
7920                     Roo.MessageBox.alert(Roo.encode(rdata));
7921                     return;
7922                 }
7923                 var data = rdata.data;
7924                 
7925                 if (this.uploadComplete) {
7926                    Roo.MessageBox.hide();
7927                    return;
7928                 }
7929                    
7930                 if (data){
7931                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7932                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7933                     );
7934                 }
7935                 this.uploadProgress.defer(2000,this);
7936             },
7937        
7938             failure: function(data) {
7939                 Roo.log('progress url failed ');
7940                 Roo.log(data);
7941             },
7942             scope : this
7943         });
7944            
7945     },
7946     
7947     
7948     run : function()
7949     {
7950         // run get Values on the form, so it syncs any secondary forms.
7951         this.form.getValues();
7952         
7953         var o = this.options;
7954         var method = this.getMethod();
7955         var isPost = method == 'POST';
7956         if(o.clientValidation === false || this.form.isValid()){
7957             
7958             if (this.form.progressUrl) {
7959                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7960                     (new Date() * 1) + '' + Math.random());
7961                     
7962             } 
7963             
7964             
7965             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7966                 form:this.form.el.dom,
7967                 url:this.getUrl(!isPost),
7968                 method: method,
7969                 params:isPost ? this.getParams() : null,
7970                 isUpload: this.form.fileUpload,
7971                 formData : this.form.formData
7972             }));
7973             
7974             this.uploadProgress();
7975
7976         }else if (o.clientValidation !== false){ // client validation failed
7977             this.failureType = Roo.form.Action.CLIENT_INVALID;
7978             this.form.afterAction(this, false);
7979         }
7980     },
7981
7982     success : function(response)
7983     {
7984         this.uploadComplete= true;
7985         if (this.haveProgress) {
7986             Roo.MessageBox.hide();
7987         }
7988         
7989         
7990         var result = this.processResponse(response);
7991         if(result === true || result.success){
7992             this.form.afterAction(this, true);
7993             return;
7994         }
7995         if(result.errors){
7996             this.form.markInvalid(result.errors);
7997             this.failureType = Roo.form.Action.SERVER_INVALID;
7998         }
7999         this.form.afterAction(this, false);
8000     },
8001     failure : function(response)
8002     {
8003         this.uploadComplete= true;
8004         if (this.haveProgress) {
8005             Roo.MessageBox.hide();
8006         }
8007         
8008         this.response = response;
8009         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8010         this.form.afterAction(this, false);
8011     },
8012     
8013     handleResponse : function(response){
8014         if(this.form.errorReader){
8015             var rs = this.form.errorReader.read(response);
8016             var errors = [];
8017             if(rs.records){
8018                 for(var i = 0, len = rs.records.length; i < len; i++) {
8019                     var r = rs.records[i];
8020                     errors[i] = r.data;
8021                 }
8022             }
8023             if(errors.length < 1){
8024                 errors = null;
8025             }
8026             return {
8027                 success : rs.success,
8028                 errors : errors
8029             };
8030         }
8031         var ret = false;
8032         try {
8033             ret = Roo.decode(response.responseText);
8034         } catch (e) {
8035             ret = {
8036                 success: false,
8037                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8038                 errors : []
8039             };
8040         }
8041         return ret;
8042         
8043     }
8044 });
8045
8046
8047 Roo.form.Action.Load = function(form, options){
8048     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8049     this.reader = this.form.reader;
8050 };
8051
8052 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8053     type : 'load',
8054
8055     run : function(){
8056         
8057         Roo.Ajax.request(Roo.apply(
8058                 this.createCallback(), {
8059                     method:this.getMethod(),
8060                     url:this.getUrl(false),
8061                     params:this.getParams()
8062         }));
8063     },
8064
8065     success : function(response){
8066         
8067         var result = this.processResponse(response);
8068         if(result === true || !result.success || !result.data){
8069             this.failureType = Roo.form.Action.LOAD_FAILURE;
8070             this.form.afterAction(this, false);
8071             return;
8072         }
8073         this.form.clearInvalid();
8074         this.form.setValues(result.data);
8075         this.form.afterAction(this, true);
8076     },
8077
8078     handleResponse : function(response){
8079         if(this.form.reader){
8080             var rs = this.form.reader.read(response);
8081             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8082             return {
8083                 success : rs.success,
8084                 data : data
8085             };
8086         }
8087         return Roo.decode(response.responseText);
8088     }
8089 });
8090
8091 Roo.form.Action.ACTION_TYPES = {
8092     'load' : Roo.form.Action.Load,
8093     'submit' : Roo.form.Action.Submit
8094 };/*
8095  * - LGPL
8096  *
8097  * form
8098  *
8099  */
8100
8101 /**
8102  * @class Roo.bootstrap.Form
8103  * @extends Roo.bootstrap.Component
8104  * Bootstrap Form class
8105  * @cfg {String} method  GET | POST (default POST)
8106  * @cfg {String} labelAlign top | left (default top)
8107  * @cfg {String} align left  | right - for navbars
8108  * @cfg {Boolean} loadMask load mask when submit (default true)
8109
8110  *
8111  * @constructor
8112  * Create a new Form
8113  * @param {Object} config The config object
8114  */
8115
8116
8117 Roo.bootstrap.Form = function(config){
8118     
8119     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8120     
8121     Roo.bootstrap.Form.popover.apply();
8122     
8123     this.addEvents({
8124         /**
8125          * @event clientvalidation
8126          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8127          * @param {Form} this
8128          * @param {Boolean} valid true if the form has passed client-side validation
8129          */
8130         clientvalidation: true,
8131         /**
8132          * @event beforeaction
8133          * Fires before any action is performed. Return false to cancel the action.
8134          * @param {Form} this
8135          * @param {Action} action The action to be performed
8136          */
8137         beforeaction: true,
8138         /**
8139          * @event actionfailed
8140          * Fires when an action fails.
8141          * @param {Form} this
8142          * @param {Action} action The action that failed
8143          */
8144         actionfailed : true,
8145         /**
8146          * @event actioncomplete
8147          * Fires when an action is completed.
8148          * @param {Form} this
8149          * @param {Action} action The action that completed
8150          */
8151         actioncomplete : true
8152     });
8153 };
8154
8155 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8156
8157      /**
8158      * @cfg {String} method
8159      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8160      */
8161     method : 'POST',
8162     /**
8163      * @cfg {String} url
8164      * The URL to use for form actions if one isn't supplied in the action options.
8165      */
8166     /**
8167      * @cfg {Boolean} fileUpload
8168      * Set to true if this form is a file upload.
8169      */
8170
8171     /**
8172      * @cfg {Object} baseParams
8173      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8174      */
8175
8176     /**
8177      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8178      */
8179     timeout: 30,
8180     /**
8181      * @cfg {Sting} align (left|right) for navbar forms
8182      */
8183     align : 'left',
8184
8185     // private
8186     activeAction : null,
8187
8188     /**
8189      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8190      * element by passing it or its id or mask the form itself by passing in true.
8191      * @type Mixed
8192      */
8193     waitMsgTarget : false,
8194
8195     loadMask : true,
8196     
8197     /**
8198      * @cfg {Boolean} errorMask (true|false) default false
8199      */
8200     errorMask : false,
8201     
8202     /**
8203      * @cfg {Number} maskOffset Default 100
8204      */
8205     maskOffset : 100,
8206     
8207     /**
8208      * @cfg {Boolean} maskBody
8209      */
8210     maskBody : false,
8211
8212     getAutoCreate : function(){
8213
8214         var cfg = {
8215             tag: 'form',
8216             method : this.method || 'POST',
8217             id : this.id || Roo.id(),
8218             cls : ''
8219         };
8220         if (this.parent().xtype.match(/^Nav/)) {
8221             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8222
8223         }
8224
8225         if (this.labelAlign == 'left' ) {
8226             cfg.cls += ' form-horizontal';
8227         }
8228
8229
8230         return cfg;
8231     },
8232     initEvents : function()
8233     {
8234         this.el.on('submit', this.onSubmit, this);
8235         // this was added as random key presses on the form where triggering form submit.
8236         this.el.on('keypress', function(e) {
8237             if (e.getCharCode() != 13) {
8238                 return true;
8239             }
8240             // we might need to allow it for textareas.. and some other items.
8241             // check e.getTarget().
8242
8243             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8244                 return true;
8245             }
8246
8247             Roo.log("keypress blocked");
8248
8249             e.preventDefault();
8250             return false;
8251         });
8252         
8253     },
8254     // private
8255     onSubmit : function(e){
8256         e.stopEvent();
8257     },
8258
8259      /**
8260      * Returns true if client-side validation on the form is successful.
8261      * @return Boolean
8262      */
8263     isValid : function(){
8264         var items = this.getItems();
8265         var valid = true;
8266         var target = false;
8267         
8268         items.each(function(f){
8269             
8270             if(f.validate()){
8271                 return;
8272             }
8273             
8274             Roo.log('invalid field: ' + f.name);
8275             
8276             valid = false;
8277
8278             if(!target && f.el.isVisible(true)){
8279                 target = f;
8280             }
8281            
8282         });
8283         
8284         if(this.errorMask && !valid){
8285             Roo.bootstrap.Form.popover.mask(this, target);
8286         }
8287         
8288         return valid;
8289     },
8290     
8291     /**
8292      * Returns true if any fields in this form have changed since their original load.
8293      * @return Boolean
8294      */
8295     isDirty : function(){
8296         var dirty = false;
8297         var items = this.getItems();
8298         items.each(function(f){
8299            if(f.isDirty()){
8300                dirty = true;
8301                return false;
8302            }
8303            return true;
8304         });
8305         return dirty;
8306     },
8307      /**
8308      * Performs a predefined action (submit or load) or custom actions you define on this form.
8309      * @param {String} actionName The name of the action type
8310      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8311      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8312      * accept other config options):
8313      * <pre>
8314 Property          Type             Description
8315 ----------------  ---------------  ----------------------------------------------------------------------------------
8316 url               String           The url for the action (defaults to the form's url)
8317 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8318 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8319 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8320                                    validate the form on the client (defaults to false)
8321      * </pre>
8322      * @return {BasicForm} this
8323      */
8324     doAction : function(action, options){
8325         if(typeof action == 'string'){
8326             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8327         }
8328         if(this.fireEvent('beforeaction', this, action) !== false){
8329             this.beforeAction(action);
8330             action.run.defer(100, action);
8331         }
8332         return this;
8333     },
8334
8335     // private
8336     beforeAction : function(action){
8337         var o = action.options;
8338         
8339         if(this.loadMask){
8340             
8341             if(this.maskBody){
8342                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8343             } else {
8344                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8345             }
8346         }
8347         // not really supported yet.. ??
8348
8349         //if(this.waitMsgTarget === true){
8350         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8351         //}else if(this.waitMsgTarget){
8352         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8353         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8354         //}else {
8355         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8356        // }
8357
8358     },
8359
8360     // private
8361     afterAction : function(action, success){
8362         this.activeAction = null;
8363         var o = action.options;
8364
8365         if(this.loadMask){
8366             
8367             if(this.maskBody){
8368                 Roo.get(document.body).unmask();
8369             } else {
8370                 this.el.unmask();
8371             }
8372         }
8373         
8374         //if(this.waitMsgTarget === true){
8375 //            this.el.unmask();
8376         //}else if(this.waitMsgTarget){
8377         //    this.waitMsgTarget.unmask();
8378         //}else{
8379         //    Roo.MessageBox.updateProgress(1);
8380         //    Roo.MessageBox.hide();
8381        // }
8382         //
8383         if(success){
8384             if(o.reset){
8385                 this.reset();
8386             }
8387             Roo.callback(o.success, o.scope, [this, action]);
8388             this.fireEvent('actioncomplete', this, action);
8389
8390         }else{
8391
8392             // failure condition..
8393             // we have a scenario where updates need confirming.
8394             // eg. if a locking scenario exists..
8395             // we look for { errors : { needs_confirm : true }} in the response.
8396             if (
8397                 (typeof(action.result) != 'undefined')  &&
8398                 (typeof(action.result.errors) != 'undefined')  &&
8399                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8400            ){
8401                 var _t = this;
8402                 Roo.log("not supported yet");
8403                  /*
8404
8405                 Roo.MessageBox.confirm(
8406                     "Change requires confirmation",
8407                     action.result.errorMsg,
8408                     function(r) {
8409                         if (r != 'yes') {
8410                             return;
8411                         }
8412                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8413                     }
8414
8415                 );
8416                 */
8417
8418
8419                 return;
8420             }
8421
8422             Roo.callback(o.failure, o.scope, [this, action]);
8423             // show an error message if no failed handler is set..
8424             if (!this.hasListener('actionfailed')) {
8425                 Roo.log("need to add dialog support");
8426                 /*
8427                 Roo.MessageBox.alert("Error",
8428                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8429                         action.result.errorMsg :
8430                         "Saving Failed, please check your entries or try again"
8431                 );
8432                 */
8433             }
8434
8435             this.fireEvent('actionfailed', this, action);
8436         }
8437
8438     },
8439     /**
8440      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8441      * @param {String} id The value to search for
8442      * @return Field
8443      */
8444     findField : function(id){
8445         var items = this.getItems();
8446         var field = items.get(id);
8447         if(!field){
8448              items.each(function(f){
8449                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8450                     field = f;
8451                     return false;
8452                 }
8453                 return true;
8454             });
8455         }
8456         return field || null;
8457     },
8458      /**
8459      * Mark fields in this form invalid in bulk.
8460      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8461      * @return {BasicForm} this
8462      */
8463     markInvalid : function(errors){
8464         if(errors instanceof Array){
8465             for(var i = 0, len = errors.length; i < len; i++){
8466                 var fieldError = errors[i];
8467                 var f = this.findField(fieldError.id);
8468                 if(f){
8469                     f.markInvalid(fieldError.msg);
8470                 }
8471             }
8472         }else{
8473             var field, id;
8474             for(id in errors){
8475                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8476                     field.markInvalid(errors[id]);
8477                 }
8478             }
8479         }
8480         //Roo.each(this.childForms || [], function (f) {
8481         //    f.markInvalid(errors);
8482         //});
8483
8484         return this;
8485     },
8486
8487     /**
8488      * Set values for fields in this form in bulk.
8489      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8490      * @return {BasicForm} this
8491      */
8492     setValues : function(values){
8493         if(values instanceof Array){ // array of objects
8494             for(var i = 0, len = values.length; i < len; i++){
8495                 var v = values[i];
8496                 var f = this.findField(v.id);
8497                 if(f){
8498                     f.setValue(v.value);
8499                     if(this.trackResetOnLoad){
8500                         f.originalValue = f.getValue();
8501                     }
8502                 }
8503             }
8504         }else{ // object hash
8505             var field, id;
8506             for(id in values){
8507                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8508
8509                     if (field.setFromData &&
8510                         field.valueField &&
8511                         field.displayField &&
8512                         // combos' with local stores can
8513                         // be queried via setValue()
8514                         // to set their value..
8515                         (field.store && !field.store.isLocal)
8516                         ) {
8517                         // it's a combo
8518                         var sd = { };
8519                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8520                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8521                         field.setFromData(sd);
8522
8523                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8524                         
8525                         field.setFromData(values);
8526                         
8527                     } else {
8528                         field.setValue(values[id]);
8529                     }
8530
8531
8532                     if(this.trackResetOnLoad){
8533                         field.originalValue = field.getValue();
8534                     }
8535                 }
8536             }
8537         }
8538
8539         //Roo.each(this.childForms || [], function (f) {
8540         //    f.setValues(values);
8541         //});
8542
8543         return this;
8544     },
8545
8546     /**
8547      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8548      * they are returned as an array.
8549      * @param {Boolean} asString
8550      * @return {Object}
8551      */
8552     getValues : function(asString){
8553         //if (this.childForms) {
8554             // copy values from the child forms
8555         //    Roo.each(this.childForms, function (f) {
8556         //        this.setValues(f.getValues());
8557         //    }, this);
8558         //}
8559
8560
8561
8562         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8563         if(asString === true){
8564             return fs;
8565         }
8566         return Roo.urlDecode(fs);
8567     },
8568
8569     /**
8570      * Returns the fields in this form as an object with key/value pairs.
8571      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8572      * @return {Object}
8573      */
8574     getFieldValues : function(with_hidden)
8575     {
8576         var items = this.getItems();
8577         var ret = {};
8578         items.each(function(f){
8579             
8580             if (!f.getName()) {
8581                 return;
8582             }
8583             
8584             var v = f.getValue();
8585             
8586             if (f.inputType =='radio') {
8587                 if (typeof(ret[f.getName()]) == 'undefined') {
8588                     ret[f.getName()] = ''; // empty..
8589                 }
8590
8591                 if (!f.el.dom.checked) {
8592                     return;
8593
8594                 }
8595                 v = f.el.dom.value;
8596
8597             }
8598             
8599             if(f.xtype == 'MoneyField'){
8600                 ret[f.currencyName] = f.getCurrency();
8601             }
8602
8603             // not sure if this supported any more..
8604             if ((typeof(v) == 'object') && f.getRawValue) {
8605                 v = f.getRawValue() ; // dates..
8606             }
8607             // combo boxes where name != hiddenName...
8608             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8609                 ret[f.name] = f.getRawValue();
8610             }
8611             ret[f.getName()] = v;
8612         });
8613
8614         return ret;
8615     },
8616
8617     /**
8618      * Clears all invalid messages in this form.
8619      * @return {BasicForm} this
8620      */
8621     clearInvalid : function(){
8622         var items = this.getItems();
8623
8624         items.each(function(f){
8625            f.clearInvalid();
8626         });
8627
8628         return this;
8629     },
8630
8631     /**
8632      * Resets this form.
8633      * @return {BasicForm} this
8634      */
8635     reset : function(){
8636         var items = this.getItems();
8637         items.each(function(f){
8638             f.reset();
8639         });
8640
8641         Roo.each(this.childForms || [], function (f) {
8642             f.reset();
8643         });
8644
8645
8646         return this;
8647     },
8648     
8649     getItems : function()
8650     {
8651         var r=new Roo.util.MixedCollection(false, function(o){
8652             return o.id || (o.id = Roo.id());
8653         });
8654         var iter = function(el) {
8655             if (el.inputEl) {
8656                 r.add(el);
8657             }
8658             if (!el.items) {
8659                 return;
8660             }
8661             Roo.each(el.items,function(e) {
8662                 iter(e);
8663             });
8664         };
8665
8666         iter(this);
8667         return r;
8668     },
8669     
8670     hideFields : function(items)
8671     {
8672         Roo.each(items, function(i){
8673             
8674             var f = this.findField(i);
8675             
8676             if(!f){
8677                 return;
8678             }
8679             
8680             f.hide();
8681             
8682         }, this);
8683     },
8684     
8685     showFields : function(items)
8686     {
8687         Roo.each(items, function(i){
8688             
8689             var f = this.findField(i);
8690             
8691             if(!f){
8692                 return;
8693             }
8694             
8695             f.show();
8696             
8697         }, this);
8698     }
8699
8700 });
8701
8702 Roo.apply(Roo.bootstrap.Form, {
8703     
8704     popover : {
8705         
8706         padding : 5,
8707         
8708         isApplied : false,
8709         
8710         isMasked : false,
8711         
8712         form : false,
8713         
8714         target : false,
8715         
8716         toolTip : false,
8717         
8718         intervalID : false,
8719         
8720         maskEl : false,
8721         
8722         apply : function()
8723         {
8724             if(this.isApplied){
8725                 return;
8726             }
8727             
8728             this.maskEl = {
8729                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8730                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8731                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8732                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8733             };
8734             
8735             this.maskEl.top.enableDisplayMode("block");
8736             this.maskEl.left.enableDisplayMode("block");
8737             this.maskEl.bottom.enableDisplayMode("block");
8738             this.maskEl.right.enableDisplayMode("block");
8739             
8740             this.toolTip = new Roo.bootstrap.Tooltip({
8741                 cls : 'roo-form-error-popover',
8742                 alignment : {
8743                     'left' : ['r-l', [-2,0], 'right'],
8744                     'right' : ['l-r', [2,0], 'left'],
8745                     'bottom' : ['tl-bl', [0,2], 'top'],
8746                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8747                 }
8748             });
8749             
8750             this.toolTip.render(Roo.get(document.body));
8751
8752             this.toolTip.el.enableDisplayMode("block");
8753             
8754             Roo.get(document.body).on('click', function(){
8755                 this.unmask();
8756             }, this);
8757             
8758             Roo.get(document.body).on('touchstart', function(){
8759                 this.unmask();
8760             }, this);
8761             
8762             this.isApplied = true
8763         },
8764         
8765         mask : function(form, target)
8766         {
8767             this.form = form;
8768             
8769             this.target = target;
8770             
8771             if(!this.form.errorMask || !target.el){
8772                 return;
8773             }
8774             
8775             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8776             
8777             Roo.log(scrollable);
8778             
8779             var ot = this.target.el.calcOffsetsTo(scrollable);
8780             
8781             var scrollTo = ot[1] - this.form.maskOffset;
8782             
8783             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8784             
8785             scrollable.scrollTo('top', scrollTo);
8786             
8787             var box = this.target.el.getBox();
8788             Roo.log(box);
8789             var zIndex = Roo.bootstrap.Modal.zIndex++;
8790
8791             
8792             this.maskEl.top.setStyle('position', 'absolute');
8793             this.maskEl.top.setStyle('z-index', zIndex);
8794             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8795             this.maskEl.top.setLeft(0);
8796             this.maskEl.top.setTop(0);
8797             this.maskEl.top.show();
8798             
8799             this.maskEl.left.setStyle('position', 'absolute');
8800             this.maskEl.left.setStyle('z-index', zIndex);
8801             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8802             this.maskEl.left.setLeft(0);
8803             this.maskEl.left.setTop(box.y - this.padding);
8804             this.maskEl.left.show();
8805
8806             this.maskEl.bottom.setStyle('position', 'absolute');
8807             this.maskEl.bottom.setStyle('z-index', zIndex);
8808             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8809             this.maskEl.bottom.setLeft(0);
8810             this.maskEl.bottom.setTop(box.bottom + this.padding);
8811             this.maskEl.bottom.show();
8812
8813             this.maskEl.right.setStyle('position', 'absolute');
8814             this.maskEl.right.setStyle('z-index', zIndex);
8815             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8816             this.maskEl.right.setLeft(box.right + this.padding);
8817             this.maskEl.right.setTop(box.y - this.padding);
8818             this.maskEl.right.show();
8819
8820             this.toolTip.bindEl = this.target.el;
8821
8822             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8823
8824             var tip = this.target.blankText;
8825
8826             if(this.target.getValue() !== '' ) {
8827                 
8828                 if (this.target.invalidText.length) {
8829                     tip = this.target.invalidText;
8830                 } else if (this.target.regexText.length){
8831                     tip = this.target.regexText;
8832                 }
8833             }
8834
8835             this.toolTip.show(tip);
8836
8837             this.intervalID = window.setInterval(function() {
8838                 Roo.bootstrap.Form.popover.unmask();
8839             }, 10000);
8840
8841             window.onwheel = function(){ return false;};
8842             
8843             (function(){ this.isMasked = true; }).defer(500, this);
8844             
8845         },
8846         
8847         unmask : function()
8848         {
8849             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8850                 return;
8851             }
8852             
8853             this.maskEl.top.setStyle('position', 'absolute');
8854             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8855             this.maskEl.top.hide();
8856
8857             this.maskEl.left.setStyle('position', 'absolute');
8858             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8859             this.maskEl.left.hide();
8860
8861             this.maskEl.bottom.setStyle('position', 'absolute');
8862             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8863             this.maskEl.bottom.hide();
8864
8865             this.maskEl.right.setStyle('position', 'absolute');
8866             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8867             this.maskEl.right.hide();
8868             
8869             this.toolTip.hide();
8870             
8871             this.toolTip.el.hide();
8872             
8873             window.onwheel = function(){ return true;};
8874             
8875             if(this.intervalID){
8876                 window.clearInterval(this.intervalID);
8877                 this.intervalID = false;
8878             }
8879             
8880             this.isMasked = false;
8881             
8882         }
8883         
8884     }
8885     
8886 });
8887
8888 /*
8889  * Based on:
8890  * Ext JS Library 1.1.1
8891  * Copyright(c) 2006-2007, Ext JS, LLC.
8892  *
8893  * Originally Released Under LGPL - original licence link has changed is not relivant.
8894  *
8895  * Fork - LGPL
8896  * <script type="text/javascript">
8897  */
8898 /**
8899  * @class Roo.form.VTypes
8900  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8901  * @singleton
8902  */
8903 Roo.form.VTypes = function(){
8904     // closure these in so they are only created once.
8905     var alpha = /^[a-zA-Z_]+$/;
8906     var alphanum = /^[a-zA-Z0-9_]+$/;
8907     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8908     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8909
8910     // All these messages and functions are configurable
8911     return {
8912         /**
8913          * The function used to validate email addresses
8914          * @param {String} value The email address
8915          */
8916         'email' : function(v){
8917             return email.test(v);
8918         },
8919         /**
8920          * The error text to display when the email validation function returns false
8921          * @type String
8922          */
8923         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8924         /**
8925          * The keystroke filter mask to be applied on email input
8926          * @type RegExp
8927          */
8928         'emailMask' : /[a-z0-9_\.\-@]/i,
8929
8930         /**
8931          * The function used to validate URLs
8932          * @param {String} value The URL
8933          */
8934         'url' : function(v){
8935             return url.test(v);
8936         },
8937         /**
8938          * The error text to display when the url validation function returns false
8939          * @type String
8940          */
8941         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8942         
8943         /**
8944          * The function used to validate alpha values
8945          * @param {String} value The value
8946          */
8947         'alpha' : function(v){
8948             return alpha.test(v);
8949         },
8950         /**
8951          * The error text to display when the alpha validation function returns false
8952          * @type String
8953          */
8954         'alphaText' : 'This field should only contain letters and _',
8955         /**
8956          * The keystroke filter mask to be applied on alpha input
8957          * @type RegExp
8958          */
8959         'alphaMask' : /[a-z_]/i,
8960
8961         /**
8962          * The function used to validate alphanumeric values
8963          * @param {String} value The value
8964          */
8965         'alphanum' : function(v){
8966             return alphanum.test(v);
8967         },
8968         /**
8969          * The error text to display when the alphanumeric validation function returns false
8970          * @type String
8971          */
8972         'alphanumText' : 'This field should only contain letters, numbers and _',
8973         /**
8974          * The keystroke filter mask to be applied on alphanumeric input
8975          * @type RegExp
8976          */
8977         'alphanumMask' : /[a-z0-9_]/i
8978     };
8979 }();/*
8980  * - LGPL
8981  *
8982  * Input
8983  * 
8984  */
8985
8986 /**
8987  * @class Roo.bootstrap.Input
8988  * @extends Roo.bootstrap.Component
8989  * Bootstrap Input class
8990  * @cfg {Boolean} disabled is it disabled
8991  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8992  * @cfg {String} name name of the input
8993  * @cfg {string} fieldLabel - the label associated
8994  * @cfg {string} placeholder - placeholder to put in text.
8995  * @cfg {string}  before - input group add on before
8996  * @cfg {string} after - input group add on after
8997  * @cfg {string} size - (lg|sm) or leave empty..
8998  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8999  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9000  * @cfg {Number} md colspan out of 12 for computer-sized screens
9001  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9002  * @cfg {string} value default value of the input
9003  * @cfg {Number} labelWidth set the width of label 
9004  * @cfg {Number} labellg set the width of label (1-12)
9005  * @cfg {Number} labelmd set the width of label (1-12)
9006  * @cfg {Number} labelsm set the width of label (1-12)
9007  * @cfg {Number} labelxs set the width of label (1-12)
9008  * @cfg {String} labelAlign (top|left)
9009  * @cfg {Boolean} readOnly Specifies that the field should be read-only
9010  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9011  * @cfg {String} indicatorpos (left|right) default left
9012  * @cfg {String} capture (user|camera) use for file input only. (default empty)
9013  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9014
9015  * @cfg {String} align (left|center|right) Default left
9016  * @cfg {Boolean} forceFeedback (true|false) Default false
9017  * 
9018  * @constructor
9019  * Create a new Input
9020  * @param {Object} config The config object
9021  */
9022
9023 Roo.bootstrap.Input = function(config){
9024     
9025     Roo.bootstrap.Input.superclass.constructor.call(this, config);
9026     
9027     this.addEvents({
9028         /**
9029          * @event focus
9030          * Fires when this field receives input focus.
9031          * @param {Roo.form.Field} this
9032          */
9033         focus : true,
9034         /**
9035          * @event blur
9036          * Fires when this field loses input focus.
9037          * @param {Roo.form.Field} this
9038          */
9039         blur : true,
9040         /**
9041          * @event specialkey
9042          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9043          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9044          * @param {Roo.form.Field} this
9045          * @param {Roo.EventObject} e The event object
9046          */
9047         specialkey : true,
9048         /**
9049          * @event change
9050          * Fires just before the field blurs if the field value has changed.
9051          * @param {Roo.form.Field} this
9052          * @param {Mixed} newValue The new value
9053          * @param {Mixed} oldValue The original value
9054          */
9055         change : true,
9056         /**
9057          * @event invalid
9058          * Fires after the field has been marked as invalid.
9059          * @param {Roo.form.Field} this
9060          * @param {String} msg The validation message
9061          */
9062         invalid : true,
9063         /**
9064          * @event valid
9065          * Fires after the field has been validated with no errors.
9066          * @param {Roo.form.Field} this
9067          */
9068         valid : true,
9069          /**
9070          * @event keyup
9071          * Fires after the key up
9072          * @param {Roo.form.Field} this
9073          * @param {Roo.EventObject}  e The event Object
9074          */
9075         keyup : true
9076     });
9077 };
9078
9079 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9080      /**
9081      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9082       automatic validation (defaults to "keyup").
9083      */
9084     validationEvent : "keyup",
9085      /**
9086      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9087      */
9088     validateOnBlur : true,
9089     /**
9090      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9091      */
9092     validationDelay : 250,
9093      /**
9094      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9095      */
9096     focusClass : "x-form-focus",  // not needed???
9097     
9098        
9099     /**
9100      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9101      */
9102     invalidClass : "has-warning",
9103     
9104     /**
9105      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9106      */
9107     validClass : "has-success",
9108     
9109     /**
9110      * @cfg {Boolean} hasFeedback (true|false) default true
9111      */
9112     hasFeedback : true,
9113     
9114     /**
9115      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9116      */
9117     invalidFeedbackClass : "glyphicon-warning-sign",
9118     
9119     /**
9120      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9121      */
9122     validFeedbackClass : "glyphicon-ok",
9123     
9124     /**
9125      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9126      */
9127     selectOnFocus : false,
9128     
9129      /**
9130      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9131      */
9132     maskRe : null,
9133        /**
9134      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9135      */
9136     vtype : null,
9137     
9138       /**
9139      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9140      */
9141     disableKeyFilter : false,
9142     
9143        /**
9144      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9145      */
9146     disabled : false,
9147      /**
9148      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9149      */
9150     allowBlank : true,
9151     /**
9152      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9153      */
9154     blankText : "Please complete this mandatory field",
9155     
9156      /**
9157      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9158      */
9159     minLength : 0,
9160     /**
9161      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9162      */
9163     maxLength : Number.MAX_VALUE,
9164     /**
9165      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9166      */
9167     minLengthText : "The minimum length for this field is {0}",
9168     /**
9169      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9170      */
9171     maxLengthText : "The maximum length for this field is {0}",
9172   
9173     
9174     /**
9175      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9176      * If available, this function will be called only after the basic validators all return true, and will be passed the
9177      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9178      */
9179     validator : null,
9180     /**
9181      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9182      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9183      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9184      */
9185     regex : null,
9186     /**
9187      * @cfg {String} regexText -- Depricated - use Invalid Text
9188      */
9189     regexText : "",
9190     
9191     /**
9192      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9193      */
9194     invalidText : "",
9195     
9196     
9197     
9198     autocomplete: false,
9199     
9200     
9201     fieldLabel : '',
9202     inputType : 'text',
9203     
9204     name : false,
9205     placeholder: false,
9206     before : false,
9207     after : false,
9208     size : false,
9209     hasFocus : false,
9210     preventMark: false,
9211     isFormField : true,
9212     value : '',
9213     labelWidth : 2,
9214     labelAlign : false,
9215     readOnly : false,
9216     align : false,
9217     formatedValue : false,
9218     forceFeedback : false,
9219     
9220     indicatorpos : 'left',
9221     
9222     labellg : 0,
9223     labelmd : 0,
9224     labelsm : 0,
9225     labelxs : 0,
9226     
9227     capture : '',
9228     accept : '',
9229     
9230     parentLabelAlign : function()
9231     {
9232         var parent = this;
9233         while (parent.parent()) {
9234             parent = parent.parent();
9235             if (typeof(parent.labelAlign) !='undefined') {
9236                 return parent.labelAlign;
9237             }
9238         }
9239         return 'left';
9240         
9241     },
9242     
9243     getAutoCreate : function()
9244     {
9245         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9246         
9247         var id = Roo.id();
9248         
9249         var cfg = {};
9250         
9251         if(this.inputType != 'hidden'){
9252             cfg.cls = 'form-group' //input-group
9253         }
9254         
9255         var input =  {
9256             tag: 'input',
9257             id : id,
9258             type : this.inputType,
9259             value : this.value,
9260             cls : 'form-control',
9261             placeholder : this.placeholder || '',
9262             autocomplete : this.autocomplete || 'new-password'
9263         };
9264         
9265         if(this.capture.length){
9266             input.capture = this.capture;
9267         }
9268         
9269         if(this.accept.length){
9270             input.accept = this.accept + "/*";
9271         }
9272         
9273         if(this.align){
9274             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9275         }
9276         
9277         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9278             input.maxLength = this.maxLength;
9279         }
9280         
9281         if (this.disabled) {
9282             input.disabled=true;
9283         }
9284         
9285         if (this.readOnly) {
9286             input.readonly=true;
9287         }
9288         
9289         if (this.name) {
9290             input.name = this.name;
9291         }
9292         
9293         if (this.size) {
9294             input.cls += ' input-' + this.size;
9295         }
9296         
9297         var settings=this;
9298         ['xs','sm','md','lg'].map(function(size){
9299             if (settings[size]) {
9300                 cfg.cls += ' col-' + size + '-' + settings[size];
9301             }
9302         });
9303         
9304         var inputblock = input;
9305         
9306         var feedback = {
9307             tag: 'span',
9308             cls: 'glyphicon form-control-feedback'
9309         };
9310             
9311         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9312             
9313             inputblock = {
9314                 cls : 'has-feedback',
9315                 cn :  [
9316                     input,
9317                     feedback
9318                 ] 
9319             };  
9320         }
9321         
9322         if (this.before || this.after) {
9323             
9324             inputblock = {
9325                 cls : 'input-group',
9326                 cn :  [] 
9327             };
9328             
9329             if (this.before && typeof(this.before) == 'string') {
9330                 
9331                 inputblock.cn.push({
9332                     tag :'span',
9333                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9334                     html : this.before
9335                 });
9336             }
9337             if (this.before && typeof(this.before) == 'object') {
9338                 this.before = Roo.factory(this.before);
9339                 
9340                 inputblock.cn.push({
9341                     tag :'span',
9342                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9343                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9344                 });
9345             }
9346             
9347             inputblock.cn.push(input);
9348             
9349             if (this.after && typeof(this.after) == 'string') {
9350                 inputblock.cn.push({
9351                     tag :'span',
9352                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9353                     html : this.after
9354                 });
9355             }
9356             if (this.after && typeof(this.after) == 'object') {
9357                 this.after = Roo.factory(this.after);
9358                 
9359                 inputblock.cn.push({
9360                     tag :'span',
9361                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9362                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9363                 });
9364             }
9365             
9366             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9367                 inputblock.cls += ' has-feedback';
9368                 inputblock.cn.push(feedback);
9369             }
9370         };
9371         var indicator = {
9372             tag : 'i',
9373             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9374             tooltip : 'This field is required'
9375         };
9376         if (Roo.bootstrap.version == 4) {
9377             indicator = {
9378                 tag : 'i',
9379                 style : 'display-none'
9380             };
9381         }
9382         if (align ==='left' && this.fieldLabel.length) {
9383             
9384             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9385             
9386             cfg.cn = [
9387                 indicator,
9388                 {
9389                     tag: 'label',
9390                     'for' :  id,
9391                     cls : 'control-label col-form-label',
9392                     html : this.fieldLabel
9393
9394                 },
9395                 {
9396                     cls : "", 
9397                     cn: [
9398                         inputblock
9399                     ]
9400                 }
9401             ];
9402             
9403             var labelCfg = cfg.cn[1];
9404             var contentCfg = cfg.cn[2];
9405             
9406             if(this.indicatorpos == 'right'){
9407                 cfg.cn = [
9408                     {
9409                         tag: 'label',
9410                         'for' :  id,
9411                         cls : 'control-label col-form-label',
9412                         cn : [
9413                             {
9414                                 tag : 'span',
9415                                 html : this.fieldLabel
9416                             },
9417                             indicator
9418                         ]
9419                     },
9420                     {
9421                         cls : "",
9422                         cn: [
9423                             inputblock
9424                         ]
9425                     }
9426
9427                 ];
9428                 
9429                 labelCfg = cfg.cn[0];
9430                 contentCfg = cfg.cn[1];
9431             
9432             }
9433             
9434             if(this.labelWidth > 12){
9435                 labelCfg.style = "width: " + this.labelWidth + 'px';
9436             }
9437             
9438             if(this.labelWidth < 13 && this.labelmd == 0){
9439                 this.labelmd = this.labelWidth;
9440             }
9441             
9442             if(this.labellg > 0){
9443                 labelCfg.cls += ' col-lg-' + this.labellg;
9444                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9445             }
9446             
9447             if(this.labelmd > 0){
9448                 labelCfg.cls += ' col-md-' + this.labelmd;
9449                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9450             }
9451             
9452             if(this.labelsm > 0){
9453                 labelCfg.cls += ' col-sm-' + this.labelsm;
9454                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9455             }
9456             
9457             if(this.labelxs > 0){
9458                 labelCfg.cls += ' col-xs-' + this.labelxs;
9459                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9460             }
9461             
9462             
9463         } else if ( this.fieldLabel.length) {
9464                 
9465             cfg.cn = [
9466                 {
9467                     tag : 'i',
9468                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9469                     tooltip : 'This field is required'
9470                 },
9471                 {
9472                     tag: 'label',
9473                    //cls : 'input-group-addon',
9474                     html : this.fieldLabel
9475
9476                 },
9477
9478                inputblock
9479
9480            ];
9481            
9482            if(this.indicatorpos == 'right'){
9483                 
9484                 cfg.cn = [
9485                     {
9486                         tag: 'label',
9487                        //cls : 'input-group-addon',
9488                         html : this.fieldLabel
9489
9490                     },
9491                     {
9492                         tag : 'i',
9493                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9494                         tooltip : 'This field is required'
9495                     },
9496
9497                    inputblock
9498
9499                ];
9500
9501             }
9502
9503         } else {
9504             
9505             cfg.cn = [
9506
9507                     inputblock
9508
9509             ];
9510                 
9511                 
9512         };
9513         
9514         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9515            cfg.cls += ' navbar-form';
9516         }
9517         
9518         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9519             // on BS4 we do this only if not form 
9520             cfg.cls += ' navbar-form';
9521             cfg.tag = 'li';
9522         }
9523         
9524         return cfg;
9525         
9526     },
9527     /**
9528      * return the real input element.
9529      */
9530     inputEl: function ()
9531     {
9532         return this.el.select('input.form-control',true).first();
9533     },
9534     
9535     tooltipEl : function()
9536     {
9537         return this.inputEl();
9538     },
9539     
9540     indicatorEl : function()
9541     {
9542         if (Roo.bootstrap.version == 4) {
9543             return false; // not enabled in v4 yet.
9544         }
9545         
9546         var indicator = this.el.select('i.roo-required-indicator',true).first();
9547         
9548         if(!indicator){
9549             return false;
9550         }
9551         
9552         return indicator;
9553         
9554     },
9555     
9556     setDisabled : function(v)
9557     {
9558         var i  = this.inputEl().dom;
9559         if (!v) {
9560             i.removeAttribute('disabled');
9561             return;
9562             
9563         }
9564         i.setAttribute('disabled','true');
9565     },
9566     initEvents : function()
9567     {
9568           
9569         this.inputEl().on("keydown" , this.fireKey,  this);
9570         this.inputEl().on("focus", this.onFocus,  this);
9571         this.inputEl().on("blur", this.onBlur,  this);
9572         
9573         this.inputEl().relayEvent('keyup', this);
9574         
9575         this.indicator = this.indicatorEl();
9576         
9577         if(this.indicator){
9578             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9579         }
9580  
9581         // reference to original value for reset
9582         this.originalValue = this.getValue();
9583         //Roo.form.TextField.superclass.initEvents.call(this);
9584         if(this.validationEvent == 'keyup'){
9585             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9586             this.inputEl().on('keyup', this.filterValidation, this);
9587         }
9588         else if(this.validationEvent !== false){
9589             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9590         }
9591         
9592         if(this.selectOnFocus){
9593             this.on("focus", this.preFocus, this);
9594             
9595         }
9596         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9597             this.inputEl().on("keypress", this.filterKeys, this);
9598         } else {
9599             this.inputEl().relayEvent('keypress', this);
9600         }
9601        /* if(this.grow){
9602             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9603             this.el.on("click", this.autoSize,  this);
9604         }
9605         */
9606         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9607             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9608         }
9609         
9610         if (typeof(this.before) == 'object') {
9611             this.before.render(this.el.select('.roo-input-before',true).first());
9612         }
9613         if (typeof(this.after) == 'object') {
9614             this.after.render(this.el.select('.roo-input-after',true).first());
9615         }
9616         
9617         this.inputEl().on('change', this.onChange, this);
9618         
9619     },
9620     filterValidation : function(e){
9621         if(!e.isNavKeyPress()){
9622             this.validationTask.delay(this.validationDelay);
9623         }
9624     },
9625      /**
9626      * Validates the field value
9627      * @return {Boolean} True if the value is valid, else false
9628      */
9629     validate : function(){
9630         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9631         if(this.disabled || this.validateValue(this.getRawValue())){
9632             this.markValid();
9633             return true;
9634         }
9635         
9636         this.markInvalid();
9637         return false;
9638     },
9639     
9640     
9641     /**
9642      * Validates a value according to the field's validation rules and marks the field as invalid
9643      * if the validation fails
9644      * @param {Mixed} value The value to validate
9645      * @return {Boolean} True if the value is valid, else false
9646      */
9647     validateValue : function(value)
9648     {
9649         if(this.getVisibilityEl().hasClass('hidden')){
9650             return true;
9651         }
9652         
9653         if(value.length < 1)  { // if it's blank
9654             if(this.allowBlank){
9655                 return true;
9656             }
9657             return false;
9658         }
9659         
9660         if(value.length < this.minLength){
9661             return false;
9662         }
9663         if(value.length > this.maxLength){
9664             return false;
9665         }
9666         if(this.vtype){
9667             var vt = Roo.form.VTypes;
9668             if(!vt[this.vtype](value, this)){
9669                 return false;
9670             }
9671         }
9672         if(typeof this.validator == "function"){
9673             var msg = this.validator(value);
9674             if(msg !== true){
9675                 return false;
9676             }
9677             if (typeof(msg) == 'string') {
9678                 this.invalidText = msg;
9679             }
9680         }
9681         
9682         if(this.regex && !this.regex.test(value)){
9683             return false;
9684         }
9685         
9686         return true;
9687     },
9688     
9689      // private
9690     fireKey : function(e){
9691         //Roo.log('field ' + e.getKey());
9692         if(e.isNavKeyPress()){
9693             this.fireEvent("specialkey", this, e);
9694         }
9695     },
9696     focus : function (selectText){
9697         if(this.rendered){
9698             this.inputEl().focus();
9699             if(selectText === true){
9700                 this.inputEl().dom.select();
9701             }
9702         }
9703         return this;
9704     } ,
9705     
9706     onFocus : function(){
9707         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9708            // this.el.addClass(this.focusClass);
9709         }
9710         if(!this.hasFocus){
9711             this.hasFocus = true;
9712             this.startValue = this.getValue();
9713             this.fireEvent("focus", this);
9714         }
9715     },
9716     
9717     beforeBlur : Roo.emptyFn,
9718
9719     
9720     // private
9721     onBlur : function(){
9722         this.beforeBlur();
9723         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9724             //this.el.removeClass(this.focusClass);
9725         }
9726         this.hasFocus = false;
9727         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9728             this.validate();
9729         }
9730         var v = this.getValue();
9731         if(String(v) !== String(this.startValue)){
9732             this.fireEvent('change', this, v, this.startValue);
9733         }
9734         this.fireEvent("blur", this);
9735     },
9736     
9737     onChange : function(e)
9738     {
9739         var v = this.getValue();
9740         if(String(v) !== String(this.startValue)){
9741             this.fireEvent('change', this, v, this.startValue);
9742         }
9743         
9744     },
9745     
9746     /**
9747      * Resets the current field value to the originally loaded value and clears any validation messages
9748      */
9749     reset : function(){
9750         this.setValue(this.originalValue);
9751         this.validate();
9752     },
9753      /**
9754      * Returns the name of the field
9755      * @return {Mixed} name The name field
9756      */
9757     getName: function(){
9758         return this.name;
9759     },
9760      /**
9761      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9762      * @return {Mixed} value The field value
9763      */
9764     getValue : function(){
9765         
9766         var v = this.inputEl().getValue();
9767         
9768         return v;
9769     },
9770     /**
9771      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9772      * @return {Mixed} value The field value
9773      */
9774     getRawValue : function(){
9775         var v = this.inputEl().getValue();
9776         
9777         return v;
9778     },
9779     
9780     /**
9781      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9782      * @param {Mixed} value The value to set
9783      */
9784     setRawValue : function(v){
9785         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9786     },
9787     
9788     selectText : function(start, end){
9789         var v = this.getRawValue();
9790         if(v.length > 0){
9791             start = start === undefined ? 0 : start;
9792             end = end === undefined ? v.length : end;
9793             var d = this.inputEl().dom;
9794             if(d.setSelectionRange){
9795                 d.setSelectionRange(start, end);
9796             }else if(d.createTextRange){
9797                 var range = d.createTextRange();
9798                 range.moveStart("character", start);
9799                 range.moveEnd("character", v.length-end);
9800                 range.select();
9801             }
9802         }
9803     },
9804     
9805     /**
9806      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9807      * @param {Mixed} value The value to set
9808      */
9809     setValue : function(v){
9810         this.value = v;
9811         if(this.rendered){
9812             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9813             this.validate();
9814         }
9815     },
9816     
9817     /*
9818     processValue : function(value){
9819         if(this.stripCharsRe){
9820             var newValue = value.replace(this.stripCharsRe, '');
9821             if(newValue !== value){
9822                 this.setRawValue(newValue);
9823                 return newValue;
9824             }
9825         }
9826         return value;
9827     },
9828   */
9829     preFocus : function(){
9830         
9831         if(this.selectOnFocus){
9832             this.inputEl().dom.select();
9833         }
9834     },
9835     filterKeys : function(e){
9836         var k = e.getKey();
9837         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9838             return;
9839         }
9840         var c = e.getCharCode(), cc = String.fromCharCode(c);
9841         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9842             return;
9843         }
9844         if(!this.maskRe.test(cc)){
9845             e.stopEvent();
9846         }
9847     },
9848      /**
9849      * Clear any invalid styles/messages for this field
9850      */
9851     clearInvalid : function(){
9852         
9853         if(!this.el || this.preventMark){ // not rendered
9854             return;
9855         }
9856         
9857         
9858         this.el.removeClass([this.invalidClass, 'is-invalid']);
9859         
9860         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9861             
9862             var feedback = this.el.select('.form-control-feedback', true).first();
9863             
9864             if(feedback){
9865                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9866             }
9867             
9868         }
9869         
9870         if(this.indicator){
9871             this.indicator.removeClass('visible');
9872             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9873         }
9874         
9875         this.fireEvent('valid', this);
9876     },
9877     
9878      /**
9879      * Mark this field as valid
9880      */
9881     markValid : function()
9882     {
9883         if(!this.el  || this.preventMark){ // not rendered...
9884             return;
9885         }
9886         
9887         this.el.removeClass([this.invalidClass, this.validClass]);
9888         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9889
9890         var feedback = this.el.select('.form-control-feedback', true).first();
9891             
9892         if(feedback){
9893             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9894         }
9895         
9896         if(this.indicator){
9897             this.indicator.removeClass('visible');
9898             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9899         }
9900         
9901         if(this.disabled){
9902             return;
9903         }
9904         
9905         if(this.allowBlank && !this.getRawValue().length){
9906             return;
9907         }
9908         if (Roo.bootstrap.version == 3) {
9909             this.el.addClass(this.validClass);
9910         } else {
9911             this.inputEl().addClass('is-valid');
9912         }
9913
9914         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9915             
9916             var feedback = this.el.select('.form-control-feedback', true).first();
9917             
9918             if(feedback){
9919                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9920                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9921             }
9922             
9923         }
9924         
9925         this.fireEvent('valid', this);
9926     },
9927     
9928      /**
9929      * Mark this field as invalid
9930      * @param {String} msg The validation message
9931      */
9932     markInvalid : function(msg)
9933     {
9934         if(!this.el  || this.preventMark){ // not rendered
9935             return;
9936         }
9937         
9938         this.el.removeClass([this.invalidClass, this.validClass]);
9939         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9940         
9941         var feedback = this.el.select('.form-control-feedback', true).first();
9942             
9943         if(feedback){
9944             this.el.select('.form-control-feedback', true).first().removeClass(
9945                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9946         }
9947
9948         if(this.disabled){
9949             return;
9950         }
9951         
9952         if(this.allowBlank && !this.getRawValue().length){
9953             return;
9954         }
9955         
9956         if(this.indicator){
9957             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9958             this.indicator.addClass('visible');
9959         }
9960         if (Roo.bootstrap.version == 3) {
9961             this.el.addClass(this.invalidClass);
9962         } else {
9963             this.inputEl().addClass('is-invalid');
9964         }
9965         
9966         
9967         
9968         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9969             
9970             var feedback = this.el.select('.form-control-feedback', true).first();
9971             
9972             if(feedback){
9973                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9974                 
9975                 if(this.getValue().length || this.forceFeedback){
9976                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9977                 }
9978                 
9979             }
9980             
9981         }
9982         
9983         this.fireEvent('invalid', this, msg);
9984     },
9985     // private
9986     SafariOnKeyDown : function(event)
9987     {
9988         // this is a workaround for a password hang bug on chrome/ webkit.
9989         if (this.inputEl().dom.type != 'password') {
9990             return;
9991         }
9992         
9993         var isSelectAll = false;
9994         
9995         if(this.inputEl().dom.selectionEnd > 0){
9996             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9997         }
9998         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9999             event.preventDefault();
10000             this.setValue('');
10001             return;
10002         }
10003         
10004         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10005             
10006             event.preventDefault();
10007             // this is very hacky as keydown always get's upper case.
10008             //
10009             var cc = String.fromCharCode(event.getCharCode());
10010             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
10011             
10012         }
10013     },
10014     adjustWidth : function(tag, w){
10015         tag = tag.toLowerCase();
10016         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10017             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10018                 if(tag == 'input'){
10019                     return w + 2;
10020                 }
10021                 if(tag == 'textarea'){
10022                     return w-2;
10023                 }
10024             }else if(Roo.isOpera){
10025                 if(tag == 'input'){
10026                     return w + 2;
10027                 }
10028                 if(tag == 'textarea'){
10029                     return w-2;
10030                 }
10031             }
10032         }
10033         return w;
10034     },
10035     
10036     setFieldLabel : function(v)
10037     {
10038         if(!this.rendered){
10039             return;
10040         }
10041         
10042         if(this.indicatorEl()){
10043             var ar = this.el.select('label > span',true);
10044             
10045             if (ar.elements.length) {
10046                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10047                 this.fieldLabel = v;
10048                 return;
10049             }
10050             
10051             var br = this.el.select('label',true);
10052             
10053             if(br.elements.length) {
10054                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10055                 this.fieldLabel = v;
10056                 return;
10057             }
10058             
10059             Roo.log('Cannot Found any of label > span || label in input');
10060             return;
10061         }
10062         
10063         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10064         this.fieldLabel = v;
10065         
10066         
10067     }
10068 });
10069
10070  
10071 /*
10072  * - LGPL
10073  *
10074  * Input
10075  * 
10076  */
10077
10078 /**
10079  * @class Roo.bootstrap.TextArea
10080  * @extends Roo.bootstrap.Input
10081  * Bootstrap TextArea class
10082  * @cfg {Number} cols Specifies the visible width of a text area
10083  * @cfg {Number} rows Specifies the visible number of lines in a text area
10084  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10085  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10086  * @cfg {string} html text
10087  * 
10088  * @constructor
10089  * Create a new TextArea
10090  * @param {Object} config The config object
10091  */
10092
10093 Roo.bootstrap.TextArea = function(config){
10094     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10095    
10096 };
10097
10098 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10099      
10100     cols : false,
10101     rows : 5,
10102     readOnly : false,
10103     warp : 'soft',
10104     resize : false,
10105     value: false,
10106     html: false,
10107     
10108     getAutoCreate : function(){
10109         
10110         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10111         
10112         var id = Roo.id();
10113         
10114         var cfg = {};
10115         
10116         if(this.inputType != 'hidden'){
10117             cfg.cls = 'form-group' //input-group
10118         }
10119         
10120         var input =  {
10121             tag: 'textarea',
10122             id : id,
10123             warp : this.warp,
10124             rows : this.rows,
10125             value : this.value || '',
10126             html: this.html || '',
10127             cls : 'form-control',
10128             placeholder : this.placeholder || '' 
10129             
10130         };
10131         
10132         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10133             input.maxLength = this.maxLength;
10134         }
10135         
10136         if(this.resize){
10137             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10138         }
10139         
10140         if(this.cols){
10141             input.cols = this.cols;
10142         }
10143         
10144         if (this.readOnly) {
10145             input.readonly = true;
10146         }
10147         
10148         if (this.name) {
10149             input.name = this.name;
10150         }
10151         
10152         if (this.size) {
10153             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10154         }
10155         
10156         var settings=this;
10157         ['xs','sm','md','lg'].map(function(size){
10158             if (settings[size]) {
10159                 cfg.cls += ' col-' + size + '-' + settings[size];
10160             }
10161         });
10162         
10163         var inputblock = input;
10164         
10165         if(this.hasFeedback && !this.allowBlank){
10166             
10167             var feedback = {
10168                 tag: 'span',
10169                 cls: 'glyphicon form-control-feedback'
10170             };
10171
10172             inputblock = {
10173                 cls : 'has-feedback',
10174                 cn :  [
10175                     input,
10176                     feedback
10177                 ] 
10178             };  
10179         }
10180         
10181         
10182         if (this.before || this.after) {
10183             
10184             inputblock = {
10185                 cls : 'input-group',
10186                 cn :  [] 
10187             };
10188             if (this.before) {
10189                 inputblock.cn.push({
10190                     tag :'span',
10191                     cls : 'input-group-addon',
10192                     html : this.before
10193                 });
10194             }
10195             
10196             inputblock.cn.push(input);
10197             
10198             if(this.hasFeedback && !this.allowBlank){
10199                 inputblock.cls += ' has-feedback';
10200                 inputblock.cn.push(feedback);
10201             }
10202             
10203             if (this.after) {
10204                 inputblock.cn.push({
10205                     tag :'span',
10206                     cls : 'input-group-addon',
10207                     html : this.after
10208                 });
10209             }
10210             
10211         }
10212         
10213         if (align ==='left' && this.fieldLabel.length) {
10214             cfg.cn = [
10215                 {
10216                     tag: 'label',
10217                     'for' :  id,
10218                     cls : 'control-label',
10219                     html : this.fieldLabel
10220                 },
10221                 {
10222                     cls : "",
10223                     cn: [
10224                         inputblock
10225                     ]
10226                 }
10227
10228             ];
10229             
10230             if(this.labelWidth > 12){
10231                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10232             }
10233
10234             if(this.labelWidth < 13 && this.labelmd == 0){
10235                 this.labelmd = this.labelWidth;
10236             }
10237
10238             if(this.labellg > 0){
10239                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10240                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10241             }
10242
10243             if(this.labelmd > 0){
10244                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10245                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10246             }
10247
10248             if(this.labelsm > 0){
10249                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10250                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10251             }
10252
10253             if(this.labelxs > 0){
10254                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10255                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10256             }
10257             
10258         } else if ( this.fieldLabel.length) {
10259             cfg.cn = [
10260
10261                {
10262                    tag: 'label',
10263                    //cls : 'input-group-addon',
10264                    html : this.fieldLabel
10265
10266                },
10267
10268                inputblock
10269
10270            ];
10271
10272         } else {
10273
10274             cfg.cn = [
10275
10276                 inputblock
10277
10278             ];
10279                 
10280         }
10281         
10282         if (this.disabled) {
10283             input.disabled=true;
10284         }
10285         
10286         return cfg;
10287         
10288     },
10289     /**
10290      * return the real textarea element.
10291      */
10292     inputEl: function ()
10293     {
10294         return this.el.select('textarea.form-control',true).first();
10295     },
10296     
10297     /**
10298      * Clear any invalid styles/messages for this field
10299      */
10300     clearInvalid : function()
10301     {
10302         
10303         if(!this.el || this.preventMark){ // not rendered
10304             return;
10305         }
10306         
10307         var label = this.el.select('label', true).first();
10308         var icon = this.el.select('i.fa-star', true).first();
10309         
10310         if(label && icon){
10311             icon.remove();
10312         }
10313         this.el.removeClass( this.validClass);
10314         this.inputEl().removeClass('is-invalid');
10315          
10316         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10317             
10318             var feedback = this.el.select('.form-control-feedback', true).first();
10319             
10320             if(feedback){
10321                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10322             }
10323             
10324         }
10325         
10326         this.fireEvent('valid', this);
10327     },
10328     
10329      /**
10330      * Mark this field as valid
10331      */
10332     markValid : function()
10333     {
10334         if(!this.el  || this.preventMark){ // not rendered
10335             return;
10336         }
10337         
10338         this.el.removeClass([this.invalidClass, this.validClass]);
10339         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10340         
10341         var feedback = this.el.select('.form-control-feedback', true).first();
10342             
10343         if(feedback){
10344             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10345         }
10346
10347         if(this.disabled || this.allowBlank){
10348             return;
10349         }
10350         
10351         var label = this.el.select('label', true).first();
10352         var icon = this.el.select('i.fa-star', true).first();
10353         
10354         if(label && icon){
10355             icon.remove();
10356         }
10357         if (Roo.bootstrap.version == 3) {
10358             this.el.addClass(this.validClass);
10359         } else {
10360             this.inputEl().addClass('is-valid');
10361         }
10362         
10363         
10364         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10365             
10366             var feedback = this.el.select('.form-control-feedback', true).first();
10367             
10368             if(feedback){
10369                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10370                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10371             }
10372             
10373         }
10374         
10375         this.fireEvent('valid', this);
10376     },
10377     
10378      /**
10379      * Mark this field as invalid
10380      * @param {String} msg The validation message
10381      */
10382     markInvalid : function(msg)
10383     {
10384         if(!this.el  || this.preventMark){ // not rendered
10385             return;
10386         }
10387         
10388         this.el.removeClass([this.invalidClass, this.validClass]);
10389         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10390         
10391         var feedback = this.el.select('.form-control-feedback', true).first();
10392             
10393         if(feedback){
10394             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10395         }
10396
10397         if(this.disabled || this.allowBlank){
10398             return;
10399         }
10400         
10401         var label = this.el.select('label', true).first();
10402         var icon = this.el.select('i.fa-star', true).first();
10403         
10404         if(!this.getValue().length && label && !icon){
10405             this.el.createChild({
10406                 tag : 'i',
10407                 cls : 'text-danger fa fa-lg fa-star',
10408                 tooltip : 'This field is required',
10409                 style : 'margin-right:5px;'
10410             }, label, true);
10411         }
10412         
10413         if (Roo.bootstrap.version == 3) {
10414             this.el.addClass(this.invalidClass);
10415         } else {
10416             this.inputEl().addClass('is-invalid');
10417         }
10418         
10419         // fixme ... this may be depricated need to test..
10420         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10421             
10422             var feedback = this.el.select('.form-control-feedback', true).first();
10423             
10424             if(feedback){
10425                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10426                 
10427                 if(this.getValue().length || this.forceFeedback){
10428                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10429                 }
10430                 
10431             }
10432             
10433         }
10434         
10435         this.fireEvent('invalid', this, msg);
10436     }
10437 });
10438
10439  
10440 /*
10441  * - LGPL
10442  *
10443  * trigger field - base class for combo..
10444  * 
10445  */
10446  
10447 /**
10448  * @class Roo.bootstrap.TriggerField
10449  * @extends Roo.bootstrap.Input
10450  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10451  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10452  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10453  * for which you can provide a custom implementation.  For example:
10454  * <pre><code>
10455 var trigger = new Roo.bootstrap.TriggerField();
10456 trigger.onTriggerClick = myTriggerFn;
10457 trigger.applyTo('my-field');
10458 </code></pre>
10459  *
10460  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10461  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10462  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10463  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10464  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10465
10466  * @constructor
10467  * Create a new TriggerField.
10468  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10469  * to the base TextField)
10470  */
10471 Roo.bootstrap.TriggerField = function(config){
10472     this.mimicing = false;
10473     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10474 };
10475
10476 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10477     /**
10478      * @cfg {String} triggerClass A CSS class to apply to the trigger
10479      */
10480      /**
10481      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10482      */
10483     hideTrigger:false,
10484
10485     /**
10486      * @cfg {Boolean} removable (true|false) special filter default false
10487      */
10488     removable : false,
10489     
10490     /** @cfg {Boolean} grow @hide */
10491     /** @cfg {Number} growMin @hide */
10492     /** @cfg {Number} growMax @hide */
10493
10494     /**
10495      * @hide 
10496      * @method
10497      */
10498     autoSize: Roo.emptyFn,
10499     // private
10500     monitorTab : true,
10501     // private
10502     deferHeight : true,
10503
10504     
10505     actionMode : 'wrap',
10506     
10507     caret : false,
10508     
10509     
10510     getAutoCreate : function(){
10511        
10512         var align = this.labelAlign || this.parentLabelAlign();
10513         
10514         var id = Roo.id();
10515         
10516         var cfg = {
10517             cls: 'form-group' //input-group
10518         };
10519         
10520         
10521         var input =  {
10522             tag: 'input',
10523             id : id,
10524             type : this.inputType,
10525             cls : 'form-control',
10526             autocomplete: 'new-password',
10527             placeholder : this.placeholder || '' 
10528             
10529         };
10530         if (this.name) {
10531             input.name = this.name;
10532         }
10533         if (this.size) {
10534             input.cls += ' input-' + this.size;
10535         }
10536         
10537         if (this.disabled) {
10538             input.disabled=true;
10539         }
10540         
10541         var inputblock = input;
10542         
10543         if(this.hasFeedback && !this.allowBlank){
10544             
10545             var feedback = {
10546                 tag: 'span',
10547                 cls: 'glyphicon form-control-feedback'
10548             };
10549             
10550             if(this.removable && !this.editable && !this.tickable){
10551                 inputblock = {
10552                     cls : 'has-feedback',
10553                     cn :  [
10554                         inputblock,
10555                         {
10556                             tag: 'button',
10557                             html : 'x',
10558                             cls : 'roo-combo-removable-btn close'
10559                         },
10560                         feedback
10561                     ] 
10562                 };
10563             } else {
10564                 inputblock = {
10565                     cls : 'has-feedback',
10566                     cn :  [
10567                         inputblock,
10568                         feedback
10569                     ] 
10570                 };
10571             }
10572
10573         } else {
10574             if(this.removable && !this.editable && !this.tickable){
10575                 inputblock = {
10576                     cls : 'roo-removable',
10577                     cn :  [
10578                         inputblock,
10579                         {
10580                             tag: 'button',
10581                             html : 'x',
10582                             cls : 'roo-combo-removable-btn close'
10583                         }
10584                     ] 
10585                 };
10586             }
10587         }
10588         
10589         if (this.before || this.after) {
10590             
10591             inputblock = {
10592                 cls : 'input-group',
10593                 cn :  [] 
10594             };
10595             if (this.before) {
10596                 inputblock.cn.push({
10597                     tag :'span',
10598                     cls : 'input-group-addon input-group-prepend input-group-text',
10599                     html : this.before
10600                 });
10601             }
10602             
10603             inputblock.cn.push(input);
10604             
10605             if(this.hasFeedback && !this.allowBlank){
10606                 inputblock.cls += ' has-feedback';
10607                 inputblock.cn.push(feedback);
10608             }
10609             
10610             if (this.after) {
10611                 inputblock.cn.push({
10612                     tag :'span',
10613                     cls : 'input-group-addon input-group-append input-group-text',
10614                     html : this.after
10615                 });
10616             }
10617             
10618         };
10619         
10620       
10621         
10622         var ibwrap = inputblock;
10623         
10624         if(this.multiple){
10625             ibwrap = {
10626                 tag: 'ul',
10627                 cls: 'roo-select2-choices',
10628                 cn:[
10629                     {
10630                         tag: 'li',
10631                         cls: 'roo-select2-search-field',
10632                         cn: [
10633
10634                             inputblock
10635                         ]
10636                     }
10637                 ]
10638             };
10639                 
10640         }
10641         
10642         var combobox = {
10643             cls: 'roo-select2-container input-group',
10644             cn: [
10645                  {
10646                     tag: 'input',
10647                     type : 'hidden',
10648                     cls: 'form-hidden-field'
10649                 },
10650                 ibwrap
10651             ]
10652         };
10653         
10654         if(!this.multiple && this.showToggleBtn){
10655             
10656             var caret = {
10657                         tag: 'span',
10658                         cls: 'caret'
10659              };
10660             if (this.caret != false) {
10661                 caret = {
10662                      tag: 'i',
10663                      cls: 'fa fa-' + this.caret
10664                 };
10665                 
10666             }
10667             
10668             combobox.cn.push({
10669                 tag :'span',
10670                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10671                 cn : [
10672                     Roo.bootstrap.version == 3 ? caret : '',
10673                     {
10674                         tag: 'span',
10675                         cls: 'combobox-clear',
10676                         cn  : [
10677                             {
10678                                 tag : 'i',
10679                                 cls: 'icon-remove'
10680                             }
10681                         ]
10682                     }
10683                 ]
10684
10685             })
10686         }
10687         
10688         if(this.multiple){
10689             combobox.cls += ' roo-select2-container-multi';
10690         }
10691          var indicator = {
10692             tag : 'i',
10693             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10694             tooltip : 'This field is required'
10695         };
10696         if (Roo.bootstrap.version == 4) {
10697             indicator = {
10698                 tag : 'i',
10699                 style : 'display:none'
10700             };
10701         }
10702         
10703         
10704         if (align ==='left' && this.fieldLabel.length) {
10705             
10706             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10707
10708             cfg.cn = [
10709                 indicator,
10710                 {
10711                     tag: 'label',
10712                     'for' :  id,
10713                     cls : 'control-label',
10714                     html : this.fieldLabel
10715
10716                 },
10717                 {
10718                     cls : "", 
10719                     cn: [
10720                         combobox
10721                     ]
10722                 }
10723
10724             ];
10725             
10726             var labelCfg = cfg.cn[1];
10727             var contentCfg = cfg.cn[2];
10728             
10729             if(this.indicatorpos == 'right'){
10730                 cfg.cn = [
10731                     {
10732                         tag: 'label',
10733                         'for' :  id,
10734                         cls : 'control-label',
10735                         cn : [
10736                             {
10737                                 tag : 'span',
10738                                 html : this.fieldLabel
10739                             },
10740                             indicator
10741                         ]
10742                     },
10743                     {
10744                         cls : "", 
10745                         cn: [
10746                             combobox
10747                         ]
10748                     }
10749
10750                 ];
10751                 
10752                 labelCfg = cfg.cn[0];
10753                 contentCfg = cfg.cn[1];
10754             }
10755             
10756             if(this.labelWidth > 12){
10757                 labelCfg.style = "width: " + this.labelWidth + 'px';
10758             }
10759             
10760             if(this.labelWidth < 13 && this.labelmd == 0){
10761                 this.labelmd = this.labelWidth;
10762             }
10763             
10764             if(this.labellg > 0){
10765                 labelCfg.cls += ' col-lg-' + this.labellg;
10766                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10767             }
10768             
10769             if(this.labelmd > 0){
10770                 labelCfg.cls += ' col-md-' + this.labelmd;
10771                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10772             }
10773             
10774             if(this.labelsm > 0){
10775                 labelCfg.cls += ' col-sm-' + this.labelsm;
10776                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10777             }
10778             
10779             if(this.labelxs > 0){
10780                 labelCfg.cls += ' col-xs-' + this.labelxs;
10781                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10782             }
10783             
10784         } else if ( this.fieldLabel.length) {
10785 //                Roo.log(" label");
10786             cfg.cn = [
10787                 indicator,
10788                {
10789                    tag: 'label',
10790                    //cls : 'input-group-addon',
10791                    html : this.fieldLabel
10792
10793                },
10794
10795                combobox
10796
10797             ];
10798             
10799             if(this.indicatorpos == 'right'){
10800                 
10801                 cfg.cn = [
10802                     {
10803                        tag: 'label',
10804                        cn : [
10805                            {
10806                                tag : 'span',
10807                                html : this.fieldLabel
10808                            },
10809                            indicator
10810                        ]
10811
10812                     },
10813                     combobox
10814
10815                 ];
10816
10817             }
10818
10819         } else {
10820             
10821 //                Roo.log(" no label && no align");
10822                 cfg = combobox
10823                      
10824                 
10825         }
10826         
10827         var settings=this;
10828         ['xs','sm','md','lg'].map(function(size){
10829             if (settings[size]) {
10830                 cfg.cls += ' col-' + size + '-' + settings[size];
10831             }
10832         });
10833         
10834         return cfg;
10835         
10836     },
10837     
10838     
10839     
10840     // private
10841     onResize : function(w, h){
10842 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10843 //        if(typeof w == 'number'){
10844 //            var x = w - this.trigger.getWidth();
10845 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10846 //            this.trigger.setStyle('left', x+'px');
10847 //        }
10848     },
10849
10850     // private
10851     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10852
10853     // private
10854     getResizeEl : function(){
10855         return this.inputEl();
10856     },
10857
10858     // private
10859     getPositionEl : function(){
10860         return this.inputEl();
10861     },
10862
10863     // private
10864     alignErrorIcon : function(){
10865         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10866     },
10867
10868     // private
10869     initEvents : function(){
10870         
10871         this.createList();
10872         
10873         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10874         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10875         if(!this.multiple && this.showToggleBtn){
10876             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10877             if(this.hideTrigger){
10878                 this.trigger.setDisplayed(false);
10879             }
10880             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10881         }
10882         
10883         if(this.multiple){
10884             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10885         }
10886         
10887         if(this.removable && !this.editable && !this.tickable){
10888             var close = this.closeTriggerEl();
10889             
10890             if(close){
10891                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10892                 close.on('click', this.removeBtnClick, this, close);
10893             }
10894         }
10895         
10896         //this.trigger.addClassOnOver('x-form-trigger-over');
10897         //this.trigger.addClassOnClick('x-form-trigger-click');
10898         
10899         //if(!this.width){
10900         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10901         //}
10902     },
10903     
10904     closeTriggerEl : function()
10905     {
10906         var close = this.el.select('.roo-combo-removable-btn', true).first();
10907         return close ? close : false;
10908     },
10909     
10910     removeBtnClick : function(e, h, el)
10911     {
10912         e.preventDefault();
10913         
10914         if(this.fireEvent("remove", this) !== false){
10915             this.reset();
10916             this.fireEvent("afterremove", this)
10917         }
10918     },
10919     
10920     createList : function()
10921     {
10922         this.list = Roo.get(document.body).createChild({
10923             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10924             cls: 'typeahead typeahead-long dropdown-menu',
10925             style: 'display:none'
10926         });
10927         
10928         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10929         
10930     },
10931
10932     // private
10933     initTrigger : function(){
10934        
10935     },
10936
10937     // private
10938     onDestroy : function(){
10939         if(this.trigger){
10940             this.trigger.removeAllListeners();
10941           //  this.trigger.remove();
10942         }
10943         //if(this.wrap){
10944         //    this.wrap.remove();
10945         //}
10946         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10947     },
10948
10949     // private
10950     onFocus : function(){
10951         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10952         /*
10953         if(!this.mimicing){
10954             this.wrap.addClass('x-trigger-wrap-focus');
10955             this.mimicing = true;
10956             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10957             if(this.monitorTab){
10958                 this.el.on("keydown", this.checkTab, this);
10959             }
10960         }
10961         */
10962     },
10963
10964     // private
10965     checkTab : function(e){
10966         if(e.getKey() == e.TAB){
10967             this.triggerBlur();
10968         }
10969     },
10970
10971     // private
10972     onBlur : function(){
10973         // do nothing
10974     },
10975
10976     // private
10977     mimicBlur : function(e, t){
10978         /*
10979         if(!this.wrap.contains(t) && this.validateBlur()){
10980             this.triggerBlur();
10981         }
10982         */
10983     },
10984
10985     // private
10986     triggerBlur : function(){
10987         this.mimicing = false;
10988         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10989         if(this.monitorTab){
10990             this.el.un("keydown", this.checkTab, this);
10991         }
10992         //this.wrap.removeClass('x-trigger-wrap-focus');
10993         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10994     },
10995
10996     // private
10997     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10998     validateBlur : function(e, t){
10999         return true;
11000     },
11001
11002     // private
11003     onDisable : function(){
11004         this.inputEl().dom.disabled = true;
11005         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11006         //if(this.wrap){
11007         //    this.wrap.addClass('x-item-disabled');
11008         //}
11009     },
11010
11011     // private
11012     onEnable : function(){
11013         this.inputEl().dom.disabled = false;
11014         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11015         //if(this.wrap){
11016         //    this.el.removeClass('x-item-disabled');
11017         //}
11018     },
11019
11020     // private
11021     onShow : function(){
11022         var ae = this.getActionEl();
11023         
11024         if(ae){
11025             ae.dom.style.display = '';
11026             ae.dom.style.visibility = 'visible';
11027         }
11028     },
11029
11030     // private
11031     
11032     onHide : function(){
11033         var ae = this.getActionEl();
11034         ae.dom.style.display = 'none';
11035     },
11036
11037     /**
11038      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11039      * by an implementing function.
11040      * @method
11041      * @param {EventObject} e
11042      */
11043     onTriggerClick : Roo.emptyFn
11044 });
11045  /*
11046  * Based on:
11047  * Ext JS Library 1.1.1
11048  * Copyright(c) 2006-2007, Ext JS, LLC.
11049  *
11050  * Originally Released Under LGPL - original licence link has changed is not relivant.
11051  *
11052  * Fork - LGPL
11053  * <script type="text/javascript">
11054  */
11055
11056
11057 /**
11058  * @class Roo.data.SortTypes
11059  * @singleton
11060  * Defines the default sorting (casting?) comparison functions used when sorting data.
11061  */
11062 Roo.data.SortTypes = {
11063     /**
11064      * Default sort that does nothing
11065      * @param {Mixed} s The value being converted
11066      * @return {Mixed} The comparison value
11067      */
11068     none : function(s){
11069         return s;
11070     },
11071     
11072     /**
11073      * The regular expression used to strip tags
11074      * @type {RegExp}
11075      * @property
11076      */
11077     stripTagsRE : /<\/?[^>]+>/gi,
11078     
11079     /**
11080      * Strips all HTML tags to sort on text only
11081      * @param {Mixed} s The value being converted
11082      * @return {String} The comparison value
11083      */
11084     asText : function(s){
11085         return String(s).replace(this.stripTagsRE, "");
11086     },
11087     
11088     /**
11089      * Strips all HTML tags to sort on text only - Case insensitive
11090      * @param {Mixed} s The value being converted
11091      * @return {String} The comparison value
11092      */
11093     asUCText : function(s){
11094         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11095     },
11096     
11097     /**
11098      * Case insensitive string
11099      * @param {Mixed} s The value being converted
11100      * @return {String} The comparison value
11101      */
11102     asUCString : function(s) {
11103         return String(s).toUpperCase();
11104     },
11105     
11106     /**
11107      * Date sorting
11108      * @param {Mixed} s The value being converted
11109      * @return {Number} The comparison value
11110      */
11111     asDate : function(s) {
11112         if(!s){
11113             return 0;
11114         }
11115         if(s instanceof Date){
11116             return s.getTime();
11117         }
11118         return Date.parse(String(s));
11119     },
11120     
11121     /**
11122      * Float sorting
11123      * @param {Mixed} s The value being converted
11124      * @return {Float} The comparison value
11125      */
11126     asFloat : function(s) {
11127         var val = parseFloat(String(s).replace(/,/g, ""));
11128         if(isNaN(val)) {
11129             val = 0;
11130         }
11131         return val;
11132     },
11133     
11134     /**
11135      * Integer sorting
11136      * @param {Mixed} s The value being converted
11137      * @return {Number} The comparison value
11138      */
11139     asInt : function(s) {
11140         var val = parseInt(String(s).replace(/,/g, ""));
11141         if(isNaN(val)) {
11142             val = 0;
11143         }
11144         return val;
11145     }
11146 };/*
11147  * Based on:
11148  * Ext JS Library 1.1.1
11149  * Copyright(c) 2006-2007, Ext JS, LLC.
11150  *
11151  * Originally Released Under LGPL - original licence link has changed is not relivant.
11152  *
11153  * Fork - LGPL
11154  * <script type="text/javascript">
11155  */
11156
11157 /**
11158 * @class Roo.data.Record
11159  * Instances of this class encapsulate both record <em>definition</em> information, and record
11160  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11161  * to access Records cached in an {@link Roo.data.Store} object.<br>
11162  * <p>
11163  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11164  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11165  * objects.<br>
11166  * <p>
11167  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11168  * @constructor
11169  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11170  * {@link #create}. The parameters are the same.
11171  * @param {Array} data An associative Array of data values keyed by the field name.
11172  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11173  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11174  * not specified an integer id is generated.
11175  */
11176 Roo.data.Record = function(data, id){
11177     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11178     this.data = data;
11179 };
11180
11181 /**
11182  * Generate a constructor for a specific record layout.
11183  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11184  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11185  * Each field definition object may contain the following properties: <ul>
11186  * <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,
11187  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11188  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11189  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11190  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11191  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11192  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11193  * this may be omitted.</p></li>
11194  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11195  * <ul><li>auto (Default, implies no conversion)</li>
11196  * <li>string</li>
11197  * <li>int</li>
11198  * <li>float</li>
11199  * <li>boolean</li>
11200  * <li>date</li></ul></p></li>
11201  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11202  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11203  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11204  * by the Reader into an object that will be stored in the Record. It is passed the
11205  * following parameters:<ul>
11206  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11207  * </ul></p></li>
11208  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11209  * </ul>
11210  * <br>usage:<br><pre><code>
11211 var TopicRecord = Roo.data.Record.create(
11212     {name: 'title', mapping: 'topic_title'},
11213     {name: 'author', mapping: 'username'},
11214     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11215     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11216     {name: 'lastPoster', mapping: 'user2'},
11217     {name: 'excerpt', mapping: 'post_text'}
11218 );
11219
11220 var myNewRecord = new TopicRecord({
11221     title: 'Do my job please',
11222     author: 'noobie',
11223     totalPosts: 1,
11224     lastPost: new Date(),
11225     lastPoster: 'Animal',
11226     excerpt: 'No way dude!'
11227 });
11228 myStore.add(myNewRecord);
11229 </code></pre>
11230  * @method create
11231  * @static
11232  */
11233 Roo.data.Record.create = function(o){
11234     var f = function(){
11235         f.superclass.constructor.apply(this, arguments);
11236     };
11237     Roo.extend(f, Roo.data.Record);
11238     var p = f.prototype;
11239     p.fields = new Roo.util.MixedCollection(false, function(field){
11240         return field.name;
11241     });
11242     for(var i = 0, len = o.length; i < len; i++){
11243         p.fields.add(new Roo.data.Field(o[i]));
11244     }
11245     f.getField = function(name){
11246         return p.fields.get(name);  
11247     };
11248     return f;
11249 };
11250
11251 Roo.data.Record.AUTO_ID = 1000;
11252 Roo.data.Record.EDIT = 'edit';
11253 Roo.data.Record.REJECT = 'reject';
11254 Roo.data.Record.COMMIT = 'commit';
11255
11256 Roo.data.Record.prototype = {
11257     /**
11258      * Readonly flag - true if this record has been modified.
11259      * @type Boolean
11260      */
11261     dirty : false,
11262     editing : false,
11263     error: null,
11264     modified: null,
11265
11266     // private
11267     join : function(store){
11268         this.store = store;
11269     },
11270
11271     /**
11272      * Set the named field to the specified value.
11273      * @param {String} name The name of the field to set.
11274      * @param {Object} value The value to set the field to.
11275      */
11276     set : function(name, value){
11277         if(this.data[name] == value){
11278             return;
11279         }
11280         this.dirty = true;
11281         if(!this.modified){
11282             this.modified = {};
11283         }
11284         if(typeof this.modified[name] == 'undefined'){
11285             this.modified[name] = this.data[name];
11286         }
11287         this.data[name] = value;
11288         if(!this.editing && this.store){
11289             this.store.afterEdit(this);
11290         }       
11291     },
11292
11293     /**
11294      * Get the value of the named field.
11295      * @param {String} name The name of the field to get the value of.
11296      * @return {Object} The value of the field.
11297      */
11298     get : function(name){
11299         return this.data[name]; 
11300     },
11301
11302     // private
11303     beginEdit : function(){
11304         this.editing = true;
11305         this.modified = {}; 
11306     },
11307
11308     // private
11309     cancelEdit : function(){
11310         this.editing = false;
11311         delete this.modified;
11312     },
11313
11314     // private
11315     endEdit : function(){
11316         this.editing = false;
11317         if(this.dirty && this.store){
11318             this.store.afterEdit(this);
11319         }
11320     },
11321
11322     /**
11323      * Usually called by the {@link Roo.data.Store} which owns the Record.
11324      * Rejects all changes made to the Record since either creation, or the last commit operation.
11325      * Modified fields are reverted to their original values.
11326      * <p>
11327      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11328      * of reject operations.
11329      */
11330     reject : function(){
11331         var m = this.modified;
11332         for(var n in m){
11333             if(typeof m[n] != "function"){
11334                 this.data[n] = m[n];
11335             }
11336         }
11337         this.dirty = false;
11338         delete this.modified;
11339         this.editing = false;
11340         if(this.store){
11341             this.store.afterReject(this);
11342         }
11343     },
11344
11345     /**
11346      * Usually called by the {@link Roo.data.Store} which owns the Record.
11347      * Commits all changes made to the Record since either creation, or the last commit operation.
11348      * <p>
11349      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11350      * of commit operations.
11351      */
11352     commit : function(){
11353         this.dirty = false;
11354         delete this.modified;
11355         this.editing = false;
11356         if(this.store){
11357             this.store.afterCommit(this);
11358         }
11359     },
11360
11361     // private
11362     hasError : function(){
11363         return this.error != null;
11364     },
11365
11366     // private
11367     clearError : function(){
11368         this.error = null;
11369     },
11370
11371     /**
11372      * Creates a copy of this record.
11373      * @param {String} id (optional) A new record id if you don't want to use this record's id
11374      * @return {Record}
11375      */
11376     copy : function(newId) {
11377         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11378     }
11379 };/*
11380  * Based on:
11381  * Ext JS Library 1.1.1
11382  * Copyright(c) 2006-2007, Ext JS, LLC.
11383  *
11384  * Originally Released Under LGPL - original licence link has changed is not relivant.
11385  *
11386  * Fork - LGPL
11387  * <script type="text/javascript">
11388  */
11389
11390
11391
11392 /**
11393  * @class Roo.data.Store
11394  * @extends Roo.util.Observable
11395  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11396  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11397  * <p>
11398  * 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
11399  * has no knowledge of the format of the data returned by the Proxy.<br>
11400  * <p>
11401  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11402  * instances from the data object. These records are cached and made available through accessor functions.
11403  * @constructor
11404  * Creates a new Store.
11405  * @param {Object} config A config object containing the objects needed for the Store to access data,
11406  * and read the data into Records.
11407  */
11408 Roo.data.Store = function(config){
11409     this.data = new Roo.util.MixedCollection(false);
11410     this.data.getKey = function(o){
11411         return o.id;
11412     };
11413     this.baseParams = {};
11414     // private
11415     this.paramNames = {
11416         "start" : "start",
11417         "limit" : "limit",
11418         "sort" : "sort",
11419         "dir" : "dir",
11420         "multisort" : "_multisort"
11421     };
11422
11423     if(config && config.data){
11424         this.inlineData = config.data;
11425         delete config.data;
11426     }
11427
11428     Roo.apply(this, config);
11429     
11430     if(this.reader){ // reader passed
11431         this.reader = Roo.factory(this.reader, Roo.data);
11432         this.reader.xmodule = this.xmodule || false;
11433         if(!this.recordType){
11434             this.recordType = this.reader.recordType;
11435         }
11436         if(this.reader.onMetaChange){
11437             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11438         }
11439     }
11440
11441     if(this.recordType){
11442         this.fields = this.recordType.prototype.fields;
11443     }
11444     this.modified = [];
11445
11446     this.addEvents({
11447         /**
11448          * @event datachanged
11449          * Fires when the data cache has changed, and a widget which is using this Store
11450          * as a Record cache should refresh its view.
11451          * @param {Store} this
11452          */
11453         datachanged : true,
11454         /**
11455          * @event metachange
11456          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11457          * @param {Store} this
11458          * @param {Object} meta The JSON metadata
11459          */
11460         metachange : true,
11461         /**
11462          * @event add
11463          * Fires when Records have been added to the Store
11464          * @param {Store} this
11465          * @param {Roo.data.Record[]} records The array of Records added
11466          * @param {Number} index The index at which the record(s) were added
11467          */
11468         add : true,
11469         /**
11470          * @event remove
11471          * Fires when a Record has been removed from the Store
11472          * @param {Store} this
11473          * @param {Roo.data.Record} record The Record that was removed
11474          * @param {Number} index The index at which the record was removed
11475          */
11476         remove : true,
11477         /**
11478          * @event update
11479          * Fires when a Record has been updated
11480          * @param {Store} this
11481          * @param {Roo.data.Record} record The Record that was updated
11482          * @param {String} operation The update operation being performed.  Value may be one of:
11483          * <pre><code>
11484  Roo.data.Record.EDIT
11485  Roo.data.Record.REJECT
11486  Roo.data.Record.COMMIT
11487          * </code></pre>
11488          */
11489         update : true,
11490         /**
11491          * @event clear
11492          * Fires when the data cache has been cleared.
11493          * @param {Store} this
11494          */
11495         clear : true,
11496         /**
11497          * @event beforeload
11498          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11499          * the load action will be canceled.
11500          * @param {Store} this
11501          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11502          */
11503         beforeload : true,
11504         /**
11505          * @event beforeloadadd
11506          * Fires after a new set of Records has been loaded.
11507          * @param {Store} this
11508          * @param {Roo.data.Record[]} records The Records that were loaded
11509          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11510          */
11511         beforeloadadd : true,
11512         /**
11513          * @event load
11514          * Fires after a new set of Records has been loaded, before they are added to the store.
11515          * @param {Store} this
11516          * @param {Roo.data.Record[]} records The Records that were loaded
11517          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11518          * @params {Object} return from reader
11519          */
11520         load : true,
11521         /**
11522          * @event loadexception
11523          * Fires if an exception occurs in the Proxy during loading.
11524          * Called with the signature of the Proxy's "loadexception" event.
11525          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11526          * 
11527          * @param {Proxy} 
11528          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11529          * @param {Object} load options 
11530          * @param {Object} jsonData from your request (normally this contains the Exception)
11531          */
11532         loadexception : true
11533     });
11534     
11535     if(this.proxy){
11536         this.proxy = Roo.factory(this.proxy, Roo.data);
11537         this.proxy.xmodule = this.xmodule || false;
11538         this.relayEvents(this.proxy,  ["loadexception"]);
11539     }
11540     this.sortToggle = {};
11541     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11542
11543     Roo.data.Store.superclass.constructor.call(this);
11544
11545     if(this.inlineData){
11546         this.loadData(this.inlineData);
11547         delete this.inlineData;
11548     }
11549 };
11550
11551 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11552      /**
11553     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11554     * without a remote query - used by combo/forms at present.
11555     */
11556     
11557     /**
11558     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11559     */
11560     /**
11561     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11562     */
11563     /**
11564     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11565     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11566     */
11567     /**
11568     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11569     * on any HTTP request
11570     */
11571     /**
11572     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11573     */
11574     /**
11575     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11576     */
11577     multiSort: false,
11578     /**
11579     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11580     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11581     */
11582     remoteSort : false,
11583
11584     /**
11585     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11586      * loaded or when a record is removed. (defaults to false).
11587     */
11588     pruneModifiedRecords : false,
11589
11590     // private
11591     lastOptions : null,
11592
11593     /**
11594      * Add Records to the Store and fires the add event.
11595      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11596      */
11597     add : function(records){
11598         records = [].concat(records);
11599         for(var i = 0, len = records.length; i < len; i++){
11600             records[i].join(this);
11601         }
11602         var index = this.data.length;
11603         this.data.addAll(records);
11604         this.fireEvent("add", this, records, index);
11605     },
11606
11607     /**
11608      * Remove a Record from the Store and fires the remove event.
11609      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11610      */
11611     remove : function(record){
11612         var index = this.data.indexOf(record);
11613         this.data.removeAt(index);
11614  
11615         if(this.pruneModifiedRecords){
11616             this.modified.remove(record);
11617         }
11618         this.fireEvent("remove", this, record, index);
11619     },
11620
11621     /**
11622      * Remove all Records from the Store and fires the clear event.
11623      */
11624     removeAll : function(){
11625         this.data.clear();
11626         if(this.pruneModifiedRecords){
11627             this.modified = [];
11628         }
11629         this.fireEvent("clear", this);
11630     },
11631
11632     /**
11633      * Inserts Records to the Store at the given index and fires the add event.
11634      * @param {Number} index The start index at which to insert the passed Records.
11635      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11636      */
11637     insert : function(index, records){
11638         records = [].concat(records);
11639         for(var i = 0, len = records.length; i < len; i++){
11640             this.data.insert(index, records[i]);
11641             records[i].join(this);
11642         }
11643         this.fireEvent("add", this, records, index);
11644     },
11645
11646     /**
11647      * Get the index within the cache of the passed Record.
11648      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11649      * @return {Number} The index of the passed Record. Returns -1 if not found.
11650      */
11651     indexOf : function(record){
11652         return this.data.indexOf(record);
11653     },
11654
11655     /**
11656      * Get the index within the cache of the Record with the passed id.
11657      * @param {String} id The id of the Record to find.
11658      * @return {Number} The index of the Record. Returns -1 if not found.
11659      */
11660     indexOfId : function(id){
11661         return this.data.indexOfKey(id);
11662     },
11663
11664     /**
11665      * Get the Record with the specified id.
11666      * @param {String} id The id of the Record to find.
11667      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11668      */
11669     getById : function(id){
11670         return this.data.key(id);
11671     },
11672
11673     /**
11674      * Get the Record at the specified index.
11675      * @param {Number} index The index of the Record to find.
11676      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11677      */
11678     getAt : function(index){
11679         return this.data.itemAt(index);
11680     },
11681
11682     /**
11683      * Returns a range of Records between specified indices.
11684      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11685      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11686      * @return {Roo.data.Record[]} An array of Records
11687      */
11688     getRange : function(start, end){
11689         return this.data.getRange(start, end);
11690     },
11691
11692     // private
11693     storeOptions : function(o){
11694         o = Roo.apply({}, o);
11695         delete o.callback;
11696         delete o.scope;
11697         this.lastOptions = o;
11698     },
11699
11700     /**
11701      * Loads the Record cache from the configured Proxy using the configured Reader.
11702      * <p>
11703      * If using remote paging, then the first load call must specify the <em>start</em>
11704      * and <em>limit</em> properties in the options.params property to establish the initial
11705      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11706      * <p>
11707      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11708      * and this call will return before the new data has been loaded. Perform any post-processing
11709      * in a callback function, or in a "load" event handler.</strong>
11710      * <p>
11711      * @param {Object} options An object containing properties which control loading options:<ul>
11712      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11713      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11714      * passed the following arguments:<ul>
11715      * <li>r : Roo.data.Record[]</li>
11716      * <li>options: Options object from the load call</li>
11717      * <li>success: Boolean success indicator</li></ul></li>
11718      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11719      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11720      * </ul>
11721      */
11722     load : function(options){
11723         options = options || {};
11724         if(this.fireEvent("beforeload", this, options) !== false){
11725             this.storeOptions(options);
11726             var p = Roo.apply(options.params || {}, this.baseParams);
11727             // if meta was not loaded from remote source.. try requesting it.
11728             if (!this.reader.metaFromRemote) {
11729                 p._requestMeta = 1;
11730             }
11731             if(this.sortInfo && this.remoteSort){
11732                 var pn = this.paramNames;
11733                 p[pn["sort"]] = this.sortInfo.field;
11734                 p[pn["dir"]] = this.sortInfo.direction;
11735             }
11736             if (this.multiSort) {
11737                 var pn = this.paramNames;
11738                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11739             }
11740             
11741             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11742         }
11743     },
11744
11745     /**
11746      * Reloads the Record cache from the configured Proxy using the configured Reader and
11747      * the options from the last load operation performed.
11748      * @param {Object} options (optional) An object containing properties which may override the options
11749      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11750      * the most recently used options are reused).
11751      */
11752     reload : function(options){
11753         this.load(Roo.applyIf(options||{}, this.lastOptions));
11754     },
11755
11756     // private
11757     // Called as a callback by the Reader during a load operation.
11758     loadRecords : function(o, options, success){
11759         if(!o || success === false){
11760             if(success !== false){
11761                 this.fireEvent("load", this, [], options, o);
11762             }
11763             if(options.callback){
11764                 options.callback.call(options.scope || this, [], options, false);
11765             }
11766             return;
11767         }
11768         // if data returned failure - throw an exception.
11769         if (o.success === false) {
11770             // show a message if no listener is registered.
11771             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11772                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11773             }
11774             // loadmask wil be hooked into this..
11775             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11776             return;
11777         }
11778         var r = o.records, t = o.totalRecords || r.length;
11779         
11780         this.fireEvent("beforeloadadd", this, r, options, o);
11781         
11782         if(!options || options.add !== true){
11783             if(this.pruneModifiedRecords){
11784                 this.modified = [];
11785             }
11786             for(var i = 0, len = r.length; i < len; i++){
11787                 r[i].join(this);
11788             }
11789             if(this.snapshot){
11790                 this.data = this.snapshot;
11791                 delete this.snapshot;
11792             }
11793             this.data.clear();
11794             this.data.addAll(r);
11795             this.totalLength = t;
11796             this.applySort();
11797             this.fireEvent("datachanged", this);
11798         }else{
11799             this.totalLength = Math.max(t, this.data.length+r.length);
11800             this.add(r);
11801         }
11802         
11803         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11804                 
11805             var e = new Roo.data.Record({});
11806
11807             e.set(this.parent.displayField, this.parent.emptyTitle);
11808             e.set(this.parent.valueField, '');
11809
11810             this.insert(0, e);
11811         }
11812             
11813         this.fireEvent("load", this, r, options, o);
11814         if(options.callback){
11815             options.callback.call(options.scope || this, r, options, true);
11816         }
11817     },
11818
11819
11820     /**
11821      * Loads data from a passed data block. A Reader which understands the format of the data
11822      * must have been configured in the constructor.
11823      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11824      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11825      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11826      */
11827     loadData : function(o, append){
11828         var r = this.reader.readRecords(o);
11829         this.loadRecords(r, {add: append}, true);
11830     },
11831     
11832      /**
11833      * using 'cn' the nested child reader read the child array into it's child stores.
11834      * @param {Object} rec The record with a 'children array
11835      */
11836     loadDataFromChildren : function(rec)
11837     {
11838         this.loadData(this.reader.toLoadData(rec));
11839     },
11840     
11841
11842     /**
11843      * Gets the number of cached records.
11844      * <p>
11845      * <em>If using paging, this may not be the total size of the dataset. If the data object
11846      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11847      * the data set size</em>
11848      */
11849     getCount : function(){
11850         return this.data.length || 0;
11851     },
11852
11853     /**
11854      * Gets the total number of records in the dataset as returned by the server.
11855      * <p>
11856      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11857      * the dataset size</em>
11858      */
11859     getTotalCount : function(){
11860         return this.totalLength || 0;
11861     },
11862
11863     /**
11864      * Returns the sort state of the Store as an object with two properties:
11865      * <pre><code>
11866  field {String} The name of the field by which the Records are sorted
11867  direction {String} The sort order, "ASC" or "DESC"
11868      * </code></pre>
11869      */
11870     getSortState : function(){
11871         return this.sortInfo;
11872     },
11873
11874     // private
11875     applySort : function(){
11876         if(this.sortInfo && !this.remoteSort){
11877             var s = this.sortInfo, f = s.field;
11878             var st = this.fields.get(f).sortType;
11879             var fn = function(r1, r2){
11880                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11881                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11882             };
11883             this.data.sort(s.direction, fn);
11884             if(this.snapshot && this.snapshot != this.data){
11885                 this.snapshot.sort(s.direction, fn);
11886             }
11887         }
11888     },
11889
11890     /**
11891      * Sets the default sort column and order to be used by the next load operation.
11892      * @param {String} fieldName The name of the field to sort by.
11893      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11894      */
11895     setDefaultSort : function(field, dir){
11896         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11897     },
11898
11899     /**
11900      * Sort the Records.
11901      * If remote sorting is used, the sort is performed on the server, and the cache is
11902      * reloaded. If local sorting is used, the cache is sorted internally.
11903      * @param {String} fieldName The name of the field to sort by.
11904      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11905      */
11906     sort : function(fieldName, dir){
11907         var f = this.fields.get(fieldName);
11908         if(!dir){
11909             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11910             
11911             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11912                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11913             }else{
11914                 dir = f.sortDir;
11915             }
11916         }
11917         this.sortToggle[f.name] = dir;
11918         this.sortInfo = {field: f.name, direction: dir};
11919         if(!this.remoteSort){
11920             this.applySort();
11921             this.fireEvent("datachanged", this);
11922         }else{
11923             this.load(this.lastOptions);
11924         }
11925     },
11926
11927     /**
11928      * Calls the specified function for each of the Records in the cache.
11929      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11930      * Returning <em>false</em> aborts and exits the iteration.
11931      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11932      */
11933     each : function(fn, scope){
11934         this.data.each(fn, scope);
11935     },
11936
11937     /**
11938      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11939      * (e.g., during paging).
11940      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11941      */
11942     getModifiedRecords : function(){
11943         return this.modified;
11944     },
11945
11946     // private
11947     createFilterFn : function(property, value, anyMatch){
11948         if(!value.exec){ // not a regex
11949             value = String(value);
11950             if(value.length == 0){
11951                 return false;
11952             }
11953             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11954         }
11955         return function(r){
11956             return value.test(r.data[property]);
11957         };
11958     },
11959
11960     /**
11961      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11962      * @param {String} property A field on your records
11963      * @param {Number} start The record index to start at (defaults to 0)
11964      * @param {Number} end The last record index to include (defaults to length - 1)
11965      * @return {Number} The sum
11966      */
11967     sum : function(property, start, end){
11968         var rs = this.data.items, v = 0;
11969         start = start || 0;
11970         end = (end || end === 0) ? end : rs.length-1;
11971
11972         for(var i = start; i <= end; i++){
11973             v += (rs[i].data[property] || 0);
11974         }
11975         return v;
11976     },
11977
11978     /**
11979      * Filter the records by a specified property.
11980      * @param {String} field A field on your records
11981      * @param {String/RegExp} value Either a string that the field
11982      * should start with or a RegExp to test against the field
11983      * @param {Boolean} anyMatch True to match any part not just the beginning
11984      */
11985     filter : function(property, value, anyMatch){
11986         var fn = this.createFilterFn(property, value, anyMatch);
11987         return fn ? this.filterBy(fn) : this.clearFilter();
11988     },
11989
11990     /**
11991      * Filter by a function. The specified function will be called with each
11992      * record in this data source. If the function returns true the record is included,
11993      * otherwise it is filtered.
11994      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11995      * @param {Object} scope (optional) The scope of the function (defaults to this)
11996      */
11997     filterBy : function(fn, scope){
11998         this.snapshot = this.snapshot || this.data;
11999         this.data = this.queryBy(fn, scope||this);
12000         this.fireEvent("datachanged", this);
12001     },
12002
12003     /**
12004      * Query the records by a specified property.
12005      * @param {String} field A field on your records
12006      * @param {String/RegExp} value Either a string that the field
12007      * should start with or a RegExp to test against the field
12008      * @param {Boolean} anyMatch True to match any part not just the beginning
12009      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12010      */
12011     query : function(property, value, anyMatch){
12012         var fn = this.createFilterFn(property, value, anyMatch);
12013         return fn ? this.queryBy(fn) : this.data.clone();
12014     },
12015
12016     /**
12017      * Query by a function. The specified function will be called with each
12018      * record in this data source. If the function returns true the record is included
12019      * in the results.
12020      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12021      * @param {Object} scope (optional) The scope of the function (defaults to this)
12022       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12023      **/
12024     queryBy : function(fn, scope){
12025         var data = this.snapshot || this.data;
12026         return data.filterBy(fn, scope||this);
12027     },
12028
12029     /**
12030      * Collects unique values for a particular dataIndex from this store.
12031      * @param {String} dataIndex The property to collect
12032      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12033      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12034      * @return {Array} An array of the unique values
12035      **/
12036     collect : function(dataIndex, allowNull, bypassFilter){
12037         var d = (bypassFilter === true && this.snapshot) ?
12038                 this.snapshot.items : this.data.items;
12039         var v, sv, r = [], l = {};
12040         for(var i = 0, len = d.length; i < len; i++){
12041             v = d[i].data[dataIndex];
12042             sv = String(v);
12043             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12044                 l[sv] = true;
12045                 r[r.length] = v;
12046             }
12047         }
12048         return r;
12049     },
12050
12051     /**
12052      * Revert to a view of the Record cache with no filtering applied.
12053      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12054      */
12055     clearFilter : function(suppressEvent){
12056         if(this.snapshot && this.snapshot != this.data){
12057             this.data = this.snapshot;
12058             delete this.snapshot;
12059             if(suppressEvent !== true){
12060                 this.fireEvent("datachanged", this);
12061             }
12062         }
12063     },
12064
12065     // private
12066     afterEdit : function(record){
12067         if(this.modified.indexOf(record) == -1){
12068             this.modified.push(record);
12069         }
12070         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12071     },
12072     
12073     // private
12074     afterReject : function(record){
12075         this.modified.remove(record);
12076         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12077     },
12078
12079     // private
12080     afterCommit : function(record){
12081         this.modified.remove(record);
12082         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12083     },
12084
12085     /**
12086      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12087      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12088      */
12089     commitChanges : function(){
12090         var m = this.modified.slice(0);
12091         this.modified = [];
12092         for(var i = 0, len = m.length; i < len; i++){
12093             m[i].commit();
12094         }
12095     },
12096
12097     /**
12098      * Cancel outstanding changes on all changed records.
12099      */
12100     rejectChanges : function(){
12101         var m = this.modified.slice(0);
12102         this.modified = [];
12103         for(var i = 0, len = m.length; i < len; i++){
12104             m[i].reject();
12105         }
12106     },
12107
12108     onMetaChange : function(meta, rtype, o){
12109         this.recordType = rtype;
12110         this.fields = rtype.prototype.fields;
12111         delete this.snapshot;
12112         this.sortInfo = meta.sortInfo || this.sortInfo;
12113         this.modified = [];
12114         this.fireEvent('metachange', this, this.reader.meta);
12115     },
12116     
12117     moveIndex : function(data, type)
12118     {
12119         var index = this.indexOf(data);
12120         
12121         var newIndex = index + type;
12122         
12123         this.remove(data);
12124         
12125         this.insert(newIndex, data);
12126         
12127     }
12128 });/*
12129  * Based on:
12130  * Ext JS Library 1.1.1
12131  * Copyright(c) 2006-2007, Ext JS, LLC.
12132  *
12133  * Originally Released Under LGPL - original licence link has changed is not relivant.
12134  *
12135  * Fork - LGPL
12136  * <script type="text/javascript">
12137  */
12138
12139 /**
12140  * @class Roo.data.SimpleStore
12141  * @extends Roo.data.Store
12142  * Small helper class to make creating Stores from Array data easier.
12143  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12144  * @cfg {Array} fields An array of field definition objects, or field name strings.
12145  * @cfg {Object} an existing reader (eg. copied from another store)
12146  * @cfg {Array} data The multi-dimensional array of data
12147  * @constructor
12148  * @param {Object} config
12149  */
12150 Roo.data.SimpleStore = function(config)
12151 {
12152     Roo.data.SimpleStore.superclass.constructor.call(this, {
12153         isLocal : true,
12154         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
12155                 id: config.id
12156             },
12157             Roo.data.Record.create(config.fields)
12158         ),
12159         proxy : new Roo.data.MemoryProxy(config.data)
12160     });
12161     this.load();
12162 };
12163 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12164  * Based on:
12165  * Ext JS Library 1.1.1
12166  * Copyright(c) 2006-2007, Ext JS, LLC.
12167  *
12168  * Originally Released Under LGPL - original licence link has changed is not relivant.
12169  *
12170  * Fork - LGPL
12171  * <script type="text/javascript">
12172  */
12173
12174 /**
12175 /**
12176  * @extends Roo.data.Store
12177  * @class Roo.data.JsonStore
12178  * Small helper class to make creating Stores for JSON data easier. <br/>
12179 <pre><code>
12180 var store = new Roo.data.JsonStore({
12181     url: 'get-images.php',
12182     root: 'images',
12183     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12184 });
12185 </code></pre>
12186  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12187  * JsonReader and HttpProxy (unless inline data is provided).</b>
12188  * @cfg {Array} fields An array of field definition objects, or field name strings.
12189  * @constructor
12190  * @param {Object} config
12191  */
12192 Roo.data.JsonStore = function(c){
12193     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12194         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12195         reader: new Roo.data.JsonReader(c, c.fields)
12196     }));
12197 };
12198 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12199  * Based on:
12200  * Ext JS Library 1.1.1
12201  * Copyright(c) 2006-2007, Ext JS, LLC.
12202  *
12203  * Originally Released Under LGPL - original licence link has changed is not relivant.
12204  *
12205  * Fork - LGPL
12206  * <script type="text/javascript">
12207  */
12208
12209  
12210 Roo.data.Field = function(config){
12211     if(typeof config == "string"){
12212         config = {name: config};
12213     }
12214     Roo.apply(this, config);
12215     
12216     if(!this.type){
12217         this.type = "auto";
12218     }
12219     
12220     var st = Roo.data.SortTypes;
12221     // named sortTypes are supported, here we look them up
12222     if(typeof this.sortType == "string"){
12223         this.sortType = st[this.sortType];
12224     }
12225     
12226     // set default sortType for strings and dates
12227     if(!this.sortType){
12228         switch(this.type){
12229             case "string":
12230                 this.sortType = st.asUCString;
12231                 break;
12232             case "date":
12233                 this.sortType = st.asDate;
12234                 break;
12235             default:
12236                 this.sortType = st.none;
12237         }
12238     }
12239
12240     // define once
12241     var stripRe = /[\$,%]/g;
12242
12243     // prebuilt conversion function for this field, instead of
12244     // switching every time we're reading a value
12245     if(!this.convert){
12246         var cv, dateFormat = this.dateFormat;
12247         switch(this.type){
12248             case "":
12249             case "auto":
12250             case undefined:
12251                 cv = function(v){ return v; };
12252                 break;
12253             case "string":
12254                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12255                 break;
12256             case "int":
12257                 cv = function(v){
12258                     return v !== undefined && v !== null && v !== '' ?
12259                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12260                     };
12261                 break;
12262             case "float":
12263                 cv = function(v){
12264                     return v !== undefined && v !== null && v !== '' ?
12265                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12266                     };
12267                 break;
12268             case "bool":
12269             case "boolean":
12270                 cv = function(v){ return v === true || v === "true" || v == 1; };
12271                 break;
12272             case "date":
12273                 cv = function(v){
12274                     if(!v){
12275                         return '';
12276                     }
12277                     if(v instanceof Date){
12278                         return v;
12279                     }
12280                     if(dateFormat){
12281                         if(dateFormat == "timestamp"){
12282                             return new Date(v*1000);
12283                         }
12284                         return Date.parseDate(v, dateFormat);
12285                     }
12286                     var parsed = Date.parse(v);
12287                     return parsed ? new Date(parsed) : null;
12288                 };
12289              break;
12290             
12291         }
12292         this.convert = cv;
12293     }
12294 };
12295
12296 Roo.data.Field.prototype = {
12297     dateFormat: null,
12298     defaultValue: "",
12299     mapping: null,
12300     sortType : null,
12301     sortDir : "ASC"
12302 };/*
12303  * Based on:
12304  * Ext JS Library 1.1.1
12305  * Copyright(c) 2006-2007, Ext JS, LLC.
12306  *
12307  * Originally Released Under LGPL - original licence link has changed is not relivant.
12308  *
12309  * Fork - LGPL
12310  * <script type="text/javascript">
12311  */
12312  
12313 // Base class for reading structured data from a data source.  This class is intended to be
12314 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12315
12316 /**
12317  * @class Roo.data.DataReader
12318  * Base class for reading structured data from a data source.  This class is intended to be
12319  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12320  */
12321
12322 Roo.data.DataReader = function(meta, recordType){
12323     
12324     this.meta = meta;
12325     
12326     this.recordType = recordType instanceof Array ? 
12327         Roo.data.Record.create(recordType) : recordType;
12328 };
12329
12330 Roo.data.DataReader.prototype = {
12331     
12332     
12333     readerType : 'Data',
12334      /**
12335      * Create an empty record
12336      * @param {Object} data (optional) - overlay some values
12337      * @return {Roo.data.Record} record created.
12338      */
12339     newRow :  function(d) {
12340         var da =  {};
12341         this.recordType.prototype.fields.each(function(c) {
12342             switch( c.type) {
12343                 case 'int' : da[c.name] = 0; break;
12344                 case 'date' : da[c.name] = new Date(); break;
12345                 case 'float' : da[c.name] = 0.0; break;
12346                 case 'boolean' : da[c.name] = false; break;
12347                 default : da[c.name] = ""; break;
12348             }
12349             
12350         });
12351         return new this.recordType(Roo.apply(da, d));
12352     }
12353     
12354     
12355 };/*
12356  * Based on:
12357  * Ext JS Library 1.1.1
12358  * Copyright(c) 2006-2007, Ext JS, LLC.
12359  *
12360  * Originally Released Under LGPL - original licence link has changed is not relivant.
12361  *
12362  * Fork - LGPL
12363  * <script type="text/javascript">
12364  */
12365
12366 /**
12367  * @class Roo.data.DataProxy
12368  * @extends Roo.data.Observable
12369  * This class is an abstract base class for implementations which provide retrieval of
12370  * unformatted data objects.<br>
12371  * <p>
12372  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12373  * (of the appropriate type which knows how to parse the data object) to provide a block of
12374  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12375  * <p>
12376  * Custom implementations must implement the load method as described in
12377  * {@link Roo.data.HttpProxy#load}.
12378  */
12379 Roo.data.DataProxy = function(){
12380     this.addEvents({
12381         /**
12382          * @event beforeload
12383          * Fires before a network request is made to retrieve a data object.
12384          * @param {Object} This DataProxy object.
12385          * @param {Object} params The params parameter to the load function.
12386          */
12387         beforeload : true,
12388         /**
12389          * @event load
12390          * Fires before the load method's callback is called.
12391          * @param {Object} This DataProxy object.
12392          * @param {Object} o The data object.
12393          * @param {Object} arg The callback argument object passed to the load function.
12394          */
12395         load : true,
12396         /**
12397          * @event loadexception
12398          * Fires if an Exception occurs during data retrieval.
12399          * @param {Object} This DataProxy object.
12400          * @param {Object} o The data object.
12401          * @param {Object} arg The callback argument object passed to the load function.
12402          * @param {Object} e The Exception.
12403          */
12404         loadexception : true
12405     });
12406     Roo.data.DataProxy.superclass.constructor.call(this);
12407 };
12408
12409 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12410
12411     /**
12412      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12413      */
12414 /*
12415  * Based on:
12416  * Ext JS Library 1.1.1
12417  * Copyright(c) 2006-2007, Ext JS, LLC.
12418  *
12419  * Originally Released Under LGPL - original licence link has changed is not relivant.
12420  *
12421  * Fork - LGPL
12422  * <script type="text/javascript">
12423  */
12424 /**
12425  * @class Roo.data.MemoryProxy
12426  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12427  * to the Reader when its load method is called.
12428  * @constructor
12429  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12430  */
12431 Roo.data.MemoryProxy = function(data){
12432     if (data.data) {
12433         data = data.data;
12434     }
12435     Roo.data.MemoryProxy.superclass.constructor.call(this);
12436     this.data = data;
12437 };
12438
12439 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12440     
12441     /**
12442      * Load data from the requested source (in this case an in-memory
12443      * data object passed to the constructor), read the data object into
12444      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12445      * process that block using the passed callback.
12446      * @param {Object} params This parameter is not used by the MemoryProxy class.
12447      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12448      * object into a block of Roo.data.Records.
12449      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12450      * The function must be passed <ul>
12451      * <li>The Record block object</li>
12452      * <li>The "arg" argument from the load function</li>
12453      * <li>A boolean success indicator</li>
12454      * </ul>
12455      * @param {Object} scope The scope in which to call the callback
12456      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12457      */
12458     load : function(params, reader, callback, scope, arg){
12459         params = params || {};
12460         var result;
12461         try {
12462             result = reader.readRecords(params.data ? params.data :this.data);
12463         }catch(e){
12464             this.fireEvent("loadexception", this, arg, null, e);
12465             callback.call(scope, null, arg, false);
12466             return;
12467         }
12468         callback.call(scope, result, arg, true);
12469     },
12470     
12471     // private
12472     update : function(params, records){
12473         
12474     }
12475 });/*
12476  * Based on:
12477  * Ext JS Library 1.1.1
12478  * Copyright(c) 2006-2007, Ext JS, LLC.
12479  *
12480  * Originally Released Under LGPL - original licence link has changed is not relivant.
12481  *
12482  * Fork - LGPL
12483  * <script type="text/javascript">
12484  */
12485 /**
12486  * @class Roo.data.HttpProxy
12487  * @extends Roo.data.DataProxy
12488  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12489  * configured to reference a certain URL.<br><br>
12490  * <p>
12491  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12492  * from which the running page was served.<br><br>
12493  * <p>
12494  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12495  * <p>
12496  * Be aware that to enable the browser to parse an XML document, the server must set
12497  * the Content-Type header in the HTTP response to "text/xml".
12498  * @constructor
12499  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12500  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12501  * will be used to make the request.
12502  */
12503 Roo.data.HttpProxy = function(conn){
12504     Roo.data.HttpProxy.superclass.constructor.call(this);
12505     // is conn a conn config or a real conn?
12506     this.conn = conn;
12507     this.useAjax = !conn || !conn.events;
12508   
12509 };
12510
12511 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12512     // thse are take from connection...
12513     
12514     /**
12515      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12516      */
12517     /**
12518      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12519      * extra parameters to each request made by this object. (defaults to undefined)
12520      */
12521     /**
12522      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12523      *  to each request made by this object. (defaults to undefined)
12524      */
12525     /**
12526      * @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)
12527      */
12528     /**
12529      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12530      */
12531      /**
12532      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12533      * @type Boolean
12534      */
12535   
12536
12537     /**
12538      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12539      * @type Boolean
12540      */
12541     /**
12542      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12543      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12544      * a finer-grained basis than the DataProxy events.
12545      */
12546     getConnection : function(){
12547         return this.useAjax ? Roo.Ajax : this.conn;
12548     },
12549
12550     /**
12551      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12552      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12553      * process that block using the passed callback.
12554      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12555      * for the request to the remote server.
12556      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12557      * object into a block of Roo.data.Records.
12558      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12559      * The function must be passed <ul>
12560      * <li>The Record block object</li>
12561      * <li>The "arg" argument from the load function</li>
12562      * <li>A boolean success indicator</li>
12563      * </ul>
12564      * @param {Object} scope The scope in which to call the callback
12565      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12566      */
12567     load : function(params, reader, callback, scope, arg){
12568         if(this.fireEvent("beforeload", this, params) !== false){
12569             var  o = {
12570                 params : params || {},
12571                 request: {
12572                     callback : callback,
12573                     scope : scope,
12574                     arg : arg
12575                 },
12576                 reader: reader,
12577                 callback : this.loadResponse,
12578                 scope: this
12579             };
12580             if(this.useAjax){
12581                 Roo.applyIf(o, this.conn);
12582                 if(this.activeRequest){
12583                     Roo.Ajax.abort(this.activeRequest);
12584                 }
12585                 this.activeRequest = Roo.Ajax.request(o);
12586             }else{
12587                 this.conn.request(o);
12588             }
12589         }else{
12590             callback.call(scope||this, null, arg, false);
12591         }
12592     },
12593
12594     // private
12595     loadResponse : function(o, success, response){
12596         delete this.activeRequest;
12597         if(!success){
12598             this.fireEvent("loadexception", this, o, response);
12599             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12600             return;
12601         }
12602         var result;
12603         try {
12604             result = o.reader.read(response);
12605         }catch(e){
12606             this.fireEvent("loadexception", this, o, response, e);
12607             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12608             return;
12609         }
12610         
12611         this.fireEvent("load", this, o, o.request.arg);
12612         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12613     },
12614
12615     // private
12616     update : function(dataSet){
12617
12618     },
12619
12620     // private
12621     updateResponse : function(dataSet){
12622
12623     }
12624 });/*
12625  * Based on:
12626  * Ext JS Library 1.1.1
12627  * Copyright(c) 2006-2007, Ext JS, LLC.
12628  *
12629  * Originally Released Under LGPL - original licence link has changed is not relivant.
12630  *
12631  * Fork - LGPL
12632  * <script type="text/javascript">
12633  */
12634
12635 /**
12636  * @class Roo.data.ScriptTagProxy
12637  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12638  * other than the originating domain of the running page.<br><br>
12639  * <p>
12640  * <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
12641  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12642  * <p>
12643  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12644  * source code that is used as the source inside a &lt;script> tag.<br><br>
12645  * <p>
12646  * In order for the browser to process the returned data, the server must wrap the data object
12647  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12648  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12649  * depending on whether the callback name was passed:
12650  * <p>
12651  * <pre><code>
12652 boolean scriptTag = false;
12653 String cb = request.getParameter("callback");
12654 if (cb != null) {
12655     scriptTag = true;
12656     response.setContentType("text/javascript");
12657 } else {
12658     response.setContentType("application/x-json");
12659 }
12660 Writer out = response.getWriter();
12661 if (scriptTag) {
12662     out.write(cb + "(");
12663 }
12664 out.print(dataBlock.toJsonString());
12665 if (scriptTag) {
12666     out.write(");");
12667 }
12668 </pre></code>
12669  *
12670  * @constructor
12671  * @param {Object} config A configuration object.
12672  */
12673 Roo.data.ScriptTagProxy = function(config){
12674     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12675     Roo.apply(this, config);
12676     this.head = document.getElementsByTagName("head")[0];
12677 };
12678
12679 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12680
12681 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12682     /**
12683      * @cfg {String} url The URL from which to request the data object.
12684      */
12685     /**
12686      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12687      */
12688     timeout : 30000,
12689     /**
12690      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12691      * the server the name of the callback function set up by the load call to process the returned data object.
12692      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12693      * javascript output which calls this named function passing the data object as its only parameter.
12694      */
12695     callbackParam : "callback",
12696     /**
12697      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12698      * name to the request.
12699      */
12700     nocache : true,
12701
12702     /**
12703      * Load data from the configured URL, read the data object into
12704      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12705      * process that block using the passed callback.
12706      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12707      * for the request to the remote server.
12708      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12709      * object into a block of Roo.data.Records.
12710      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12711      * The function must be passed <ul>
12712      * <li>The Record block object</li>
12713      * <li>The "arg" argument from the load function</li>
12714      * <li>A boolean success indicator</li>
12715      * </ul>
12716      * @param {Object} scope The scope in which to call the callback
12717      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12718      */
12719     load : function(params, reader, callback, scope, arg){
12720         if(this.fireEvent("beforeload", this, params) !== false){
12721
12722             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12723
12724             var url = this.url;
12725             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12726             if(this.nocache){
12727                 url += "&_dc=" + (new Date().getTime());
12728             }
12729             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12730             var trans = {
12731                 id : transId,
12732                 cb : "stcCallback"+transId,
12733                 scriptId : "stcScript"+transId,
12734                 params : params,
12735                 arg : arg,
12736                 url : url,
12737                 callback : callback,
12738                 scope : scope,
12739                 reader : reader
12740             };
12741             var conn = this;
12742
12743             window[trans.cb] = function(o){
12744                 conn.handleResponse(o, trans);
12745             };
12746
12747             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12748
12749             if(this.autoAbort !== false){
12750                 this.abort();
12751             }
12752
12753             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12754
12755             var script = document.createElement("script");
12756             script.setAttribute("src", url);
12757             script.setAttribute("type", "text/javascript");
12758             script.setAttribute("id", trans.scriptId);
12759             this.head.appendChild(script);
12760
12761             this.trans = trans;
12762         }else{
12763             callback.call(scope||this, null, arg, false);
12764         }
12765     },
12766
12767     // private
12768     isLoading : function(){
12769         return this.trans ? true : false;
12770     },
12771
12772     /**
12773      * Abort the current server request.
12774      */
12775     abort : function(){
12776         if(this.isLoading()){
12777             this.destroyTrans(this.trans);
12778         }
12779     },
12780
12781     // private
12782     destroyTrans : function(trans, isLoaded){
12783         this.head.removeChild(document.getElementById(trans.scriptId));
12784         clearTimeout(trans.timeoutId);
12785         if(isLoaded){
12786             window[trans.cb] = undefined;
12787             try{
12788                 delete window[trans.cb];
12789             }catch(e){}
12790         }else{
12791             // if hasn't been loaded, wait for load to remove it to prevent script error
12792             window[trans.cb] = function(){
12793                 window[trans.cb] = undefined;
12794                 try{
12795                     delete window[trans.cb];
12796                 }catch(e){}
12797             };
12798         }
12799     },
12800
12801     // private
12802     handleResponse : function(o, trans){
12803         this.trans = false;
12804         this.destroyTrans(trans, true);
12805         var result;
12806         try {
12807             result = trans.reader.readRecords(o);
12808         }catch(e){
12809             this.fireEvent("loadexception", this, o, trans.arg, e);
12810             trans.callback.call(trans.scope||window, null, trans.arg, false);
12811             return;
12812         }
12813         this.fireEvent("load", this, o, trans.arg);
12814         trans.callback.call(trans.scope||window, result, trans.arg, true);
12815     },
12816
12817     // private
12818     handleFailure : function(trans){
12819         this.trans = false;
12820         this.destroyTrans(trans, false);
12821         this.fireEvent("loadexception", this, null, trans.arg);
12822         trans.callback.call(trans.scope||window, null, trans.arg, false);
12823     }
12824 });/*
12825  * Based on:
12826  * Ext JS Library 1.1.1
12827  * Copyright(c) 2006-2007, Ext JS, LLC.
12828  *
12829  * Originally Released Under LGPL - original licence link has changed is not relivant.
12830  *
12831  * Fork - LGPL
12832  * <script type="text/javascript">
12833  */
12834
12835 /**
12836  * @class Roo.data.JsonReader
12837  * @extends Roo.data.DataReader
12838  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12839  * based on mappings in a provided Roo.data.Record constructor.
12840  * 
12841  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12842  * in the reply previously. 
12843  * 
12844  * <p>
12845  * Example code:
12846  * <pre><code>
12847 var RecordDef = Roo.data.Record.create([
12848     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12849     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12850 ]);
12851 var myReader = new Roo.data.JsonReader({
12852     totalProperty: "results",    // The property which contains the total dataset size (optional)
12853     root: "rows",                // The property which contains an Array of row objects
12854     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12855 }, RecordDef);
12856 </code></pre>
12857  * <p>
12858  * This would consume a JSON file like this:
12859  * <pre><code>
12860 { 'results': 2, 'rows': [
12861     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12862     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12863 }
12864 </code></pre>
12865  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12866  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12867  * paged from the remote server.
12868  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12869  * @cfg {String} root name of the property which contains the Array of row objects.
12870  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12871  * @cfg {Array} fields Array of field definition objects
12872  * @constructor
12873  * Create a new JsonReader
12874  * @param {Object} meta Metadata configuration options
12875  * @param {Object} recordType Either an Array of field definition objects,
12876  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12877  */
12878 Roo.data.JsonReader = function(meta, recordType){
12879     
12880     meta = meta || {};
12881     // set some defaults:
12882     Roo.applyIf(meta, {
12883         totalProperty: 'total',
12884         successProperty : 'success',
12885         root : 'data',
12886         id : 'id'
12887     });
12888     
12889     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12890 };
12891 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12892     
12893     readerType : 'Json',
12894     
12895     /**
12896      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12897      * Used by Store query builder to append _requestMeta to params.
12898      * 
12899      */
12900     metaFromRemote : false,
12901     /**
12902      * This method is only used by a DataProxy which has retrieved data from a remote server.
12903      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12904      * @return {Object} data A data block which is used by an Roo.data.Store object as
12905      * a cache of Roo.data.Records.
12906      */
12907     read : function(response){
12908         var json = response.responseText;
12909        
12910         var o = /* eval:var:o */ eval("("+json+")");
12911         if(!o) {
12912             throw {message: "JsonReader.read: Json object not found"};
12913         }
12914         
12915         if(o.metaData){
12916             
12917             delete this.ef;
12918             this.metaFromRemote = true;
12919             this.meta = o.metaData;
12920             this.recordType = Roo.data.Record.create(o.metaData.fields);
12921             this.onMetaChange(this.meta, this.recordType, o);
12922         }
12923         return this.readRecords(o);
12924     },
12925
12926     // private function a store will implement
12927     onMetaChange : function(meta, recordType, o){
12928
12929     },
12930
12931     /**
12932          * @ignore
12933          */
12934     simpleAccess: function(obj, subsc) {
12935         return obj[subsc];
12936     },
12937
12938         /**
12939          * @ignore
12940          */
12941     getJsonAccessor: function(){
12942         var re = /[\[\.]/;
12943         return function(expr) {
12944             try {
12945                 return(re.test(expr))
12946                     ? new Function("obj", "return obj." + expr)
12947                     : function(obj){
12948                         return obj[expr];
12949                     };
12950             } catch(e){}
12951             return Roo.emptyFn;
12952         };
12953     }(),
12954
12955     /**
12956      * Create a data block containing Roo.data.Records from an XML document.
12957      * @param {Object} o An object which contains an Array of row objects in the property specified
12958      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12959      * which contains the total size of the dataset.
12960      * @return {Object} data A data block which is used by an Roo.data.Store object as
12961      * a cache of Roo.data.Records.
12962      */
12963     readRecords : function(o){
12964         /**
12965          * After any data loads, the raw JSON data is available for further custom processing.
12966          * @type Object
12967          */
12968         this.o = o;
12969         var s = this.meta, Record = this.recordType,
12970             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12971
12972 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12973         if (!this.ef) {
12974             if(s.totalProperty) {
12975                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12976                 }
12977                 if(s.successProperty) {
12978                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12979                 }
12980                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12981                 if (s.id) {
12982                         var g = this.getJsonAccessor(s.id);
12983                         this.getId = function(rec) {
12984                                 var r = g(rec);  
12985                                 return (r === undefined || r === "") ? null : r;
12986                         };
12987                 } else {
12988                         this.getId = function(){return null;};
12989                 }
12990             this.ef = [];
12991             for(var jj = 0; jj < fl; jj++){
12992                 f = fi[jj];
12993                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12994                 this.ef[jj] = this.getJsonAccessor(map);
12995             }
12996         }
12997
12998         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12999         if(s.totalProperty){
13000             var vt = parseInt(this.getTotal(o), 10);
13001             if(!isNaN(vt)){
13002                 totalRecords = vt;
13003             }
13004         }
13005         if(s.successProperty){
13006             var vs = this.getSuccess(o);
13007             if(vs === false || vs === 'false'){
13008                 success = false;
13009             }
13010         }
13011         var records = [];
13012         for(var i = 0; i < c; i++){
13013                 var n = root[i];
13014             var values = {};
13015             var id = this.getId(n);
13016             for(var j = 0; j < fl; j++){
13017                 f = fi[j];
13018             var v = this.ef[j](n);
13019             if (!f.convert) {
13020                 Roo.log('missing convert for ' + f.name);
13021                 Roo.log(f);
13022                 continue;
13023             }
13024             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13025             }
13026             var record = new Record(values, id);
13027             record.json = n;
13028             records[i] = record;
13029         }
13030         return {
13031             raw : o,
13032             success : success,
13033             records : records,
13034             totalRecords : totalRecords
13035         };
13036     },
13037     // used when loading children.. @see loadDataFromChildren
13038     toLoadData: function(rec)
13039     {
13040         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13041         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13042         return { data : data, total : data.length };
13043         
13044     }
13045 });/*
13046  * Based on:
13047  * Ext JS Library 1.1.1
13048  * Copyright(c) 2006-2007, Ext JS, LLC.
13049  *
13050  * Originally Released Under LGPL - original licence link has changed is not relivant.
13051  *
13052  * Fork - LGPL
13053  * <script type="text/javascript">
13054  */
13055
13056 /**
13057  * @class Roo.data.ArrayReader
13058  * @extends Roo.data.DataReader
13059  * Data reader class to create an Array of Roo.data.Record objects from an Array.
13060  * Each element of that Array represents a row of data fields. The
13061  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13062  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13063  * <p>
13064  * Example code:.
13065  * <pre><code>
13066 var RecordDef = Roo.data.Record.create([
13067     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
13068     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
13069 ]);
13070 var myReader = new Roo.data.ArrayReader({
13071     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
13072 }, RecordDef);
13073 </code></pre>
13074  * <p>
13075  * This would consume an Array like this:
13076  * <pre><code>
13077 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13078   </code></pre>
13079  
13080  * @constructor
13081  * Create a new JsonReader
13082  * @param {Object} meta Metadata configuration options.
13083  * @param {Object|Array} recordType Either an Array of field definition objects
13084  * 
13085  * @cfg {Array} fields Array of field definition objects
13086  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13087  * as specified to {@link Roo.data.Record#create},
13088  * or an {@link Roo.data.Record} object
13089  *
13090  * 
13091  * created using {@link Roo.data.Record#create}.
13092  */
13093 Roo.data.ArrayReader = function(meta, recordType)
13094 {    
13095     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13096 };
13097
13098 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13099     
13100       /**
13101      * Create a data block containing Roo.data.Records from an XML document.
13102      * @param {Object} o An Array of row objects which represents the dataset.
13103      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13104      * a cache of Roo.data.Records.
13105      */
13106     readRecords : function(o)
13107     {
13108         var sid = this.meta ? this.meta.id : null;
13109         var recordType = this.recordType, fields = recordType.prototype.fields;
13110         var records = [];
13111         var root = o;
13112         for(var i = 0; i < root.length; i++){
13113                 var n = root[i];
13114             var values = {};
13115             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13116             for(var j = 0, jlen = fields.length; j < jlen; j++){
13117                 var f = fields.items[j];
13118                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13119                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13120                 v = f.convert(v);
13121                 values[f.name] = v;
13122             }
13123             var record = new recordType(values, id);
13124             record.json = n;
13125             records[records.length] = record;
13126         }
13127         return {
13128             records : records,
13129             totalRecords : records.length
13130         };
13131     },
13132     // used when loading children.. @see loadDataFromChildren
13133     toLoadData: function(rec)
13134     {
13135         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13136         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13137         
13138     }
13139     
13140     
13141 });/*
13142  * - LGPL
13143  * * 
13144  */
13145
13146 /**
13147  * @class Roo.bootstrap.ComboBox
13148  * @extends Roo.bootstrap.TriggerField
13149  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13150  * @cfg {Boolean} append (true|false) default false
13151  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13152  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13153  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13154  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13155  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13156  * @cfg {Boolean} animate default true
13157  * @cfg {Boolean} emptyResultText only for touch device
13158  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13159  * @cfg {String} emptyTitle default ''
13160  * @constructor
13161  * Create a new ComboBox.
13162  * @param {Object} config Configuration options
13163  */
13164 Roo.bootstrap.ComboBox = function(config){
13165     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13166     this.addEvents({
13167         /**
13168          * @event expand
13169          * Fires when the dropdown list is expanded
13170         * @param {Roo.bootstrap.ComboBox} combo This combo box
13171         */
13172         'expand' : true,
13173         /**
13174          * @event collapse
13175          * Fires when the dropdown list is collapsed
13176         * @param {Roo.bootstrap.ComboBox} combo This combo box
13177         */
13178         'collapse' : true,
13179         /**
13180          * @event beforeselect
13181          * Fires before a list item is selected. Return false to cancel the selection.
13182         * @param {Roo.bootstrap.ComboBox} combo This combo box
13183         * @param {Roo.data.Record} record The data record returned from the underlying store
13184         * @param {Number} index The index of the selected item in the dropdown list
13185         */
13186         'beforeselect' : true,
13187         /**
13188          * @event select
13189          * Fires when a list item is selected
13190         * @param {Roo.bootstrap.ComboBox} combo This combo box
13191         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13192         * @param {Number} index The index of the selected item in the dropdown list
13193         */
13194         'select' : true,
13195         /**
13196          * @event beforequery
13197          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13198          * The event object passed has these properties:
13199         * @param {Roo.bootstrap.ComboBox} combo This combo box
13200         * @param {String} query The query
13201         * @param {Boolean} forceAll true to force "all" query
13202         * @param {Boolean} cancel true to cancel the query
13203         * @param {Object} e The query event object
13204         */
13205         'beforequery': true,
13206          /**
13207          * @event add
13208          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13209         * @param {Roo.bootstrap.ComboBox} combo This combo box
13210         */
13211         'add' : true,
13212         /**
13213          * @event edit
13214          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13215         * @param {Roo.bootstrap.ComboBox} combo This combo box
13216         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13217         */
13218         'edit' : true,
13219         /**
13220          * @event remove
13221          * Fires when the remove value from the combobox array
13222         * @param {Roo.bootstrap.ComboBox} combo This combo box
13223         */
13224         'remove' : true,
13225         /**
13226          * @event afterremove
13227          * Fires when the remove value from the combobox array
13228         * @param {Roo.bootstrap.ComboBox} combo This combo box
13229         */
13230         'afterremove' : true,
13231         /**
13232          * @event specialfilter
13233          * Fires when specialfilter
13234             * @param {Roo.bootstrap.ComboBox} combo This combo box
13235             */
13236         'specialfilter' : true,
13237         /**
13238          * @event tick
13239          * Fires when tick the element
13240             * @param {Roo.bootstrap.ComboBox} combo This combo box
13241             */
13242         'tick' : true,
13243         /**
13244          * @event touchviewdisplay
13245          * Fires when touch view require special display (default is using displayField)
13246             * @param {Roo.bootstrap.ComboBox} combo This combo box
13247             * @param {Object} cfg set html .
13248             */
13249         'touchviewdisplay' : true
13250         
13251     });
13252     
13253     this.item = [];
13254     this.tickItems = [];
13255     
13256     this.selectedIndex = -1;
13257     if(this.mode == 'local'){
13258         if(config.queryDelay === undefined){
13259             this.queryDelay = 10;
13260         }
13261         if(config.minChars === undefined){
13262             this.minChars = 0;
13263         }
13264     }
13265 };
13266
13267 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13268      
13269     /**
13270      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13271      * rendering into an Roo.Editor, defaults to false)
13272      */
13273     /**
13274      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13275      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13276      */
13277     /**
13278      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13279      */
13280     /**
13281      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13282      * the dropdown list (defaults to undefined, with no header element)
13283      */
13284
13285      /**
13286      * @cfg {String/Roo.Template} tpl The template to use to render the output
13287      */
13288      
13289      /**
13290      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13291      */
13292     listWidth: undefined,
13293     /**
13294      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13295      * mode = 'remote' or 'text' if mode = 'local')
13296      */
13297     displayField: undefined,
13298     
13299     /**
13300      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13301      * mode = 'remote' or 'value' if mode = 'local'). 
13302      * Note: use of a valueField requires the user make a selection
13303      * in order for a value to be mapped.
13304      */
13305     valueField: undefined,
13306     /**
13307      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13308      */
13309     modalTitle : '',
13310     
13311     /**
13312      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13313      * field's data value (defaults to the underlying DOM element's name)
13314      */
13315     hiddenName: undefined,
13316     /**
13317      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13318      */
13319     listClass: '',
13320     /**
13321      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13322      */
13323     selectedClass: 'active',
13324     
13325     /**
13326      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13327      */
13328     shadow:'sides',
13329     /**
13330      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13331      * anchor positions (defaults to 'tl-bl')
13332      */
13333     listAlign: 'tl-bl?',
13334     /**
13335      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13336      */
13337     maxHeight: 300,
13338     /**
13339      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13340      * query specified by the allQuery config option (defaults to 'query')
13341      */
13342     triggerAction: 'query',
13343     /**
13344      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13345      * (defaults to 4, does not apply if editable = false)
13346      */
13347     minChars : 4,
13348     /**
13349      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13350      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13351      */
13352     typeAhead: false,
13353     /**
13354      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13355      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13356      */
13357     queryDelay: 500,
13358     /**
13359      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13360      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13361      */
13362     pageSize: 0,
13363     /**
13364      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13365      * when editable = true (defaults to false)
13366      */
13367     selectOnFocus:false,
13368     /**
13369      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13370      */
13371     queryParam: 'query',
13372     /**
13373      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13374      * when mode = 'remote' (defaults to 'Loading...')
13375      */
13376     loadingText: 'Loading...',
13377     /**
13378      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13379      */
13380     resizable: false,
13381     /**
13382      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13383      */
13384     handleHeight : 8,
13385     /**
13386      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13387      * traditional select (defaults to true)
13388      */
13389     editable: true,
13390     /**
13391      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13392      */
13393     allQuery: '',
13394     /**
13395      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13396      */
13397     mode: 'remote',
13398     /**
13399      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13400      * listWidth has a higher value)
13401      */
13402     minListWidth : 70,
13403     /**
13404      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13405      * allow the user to set arbitrary text into the field (defaults to false)
13406      */
13407     forceSelection:false,
13408     /**
13409      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13410      * if typeAhead = true (defaults to 250)
13411      */
13412     typeAheadDelay : 250,
13413     /**
13414      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13415      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13416      */
13417     valueNotFoundText : undefined,
13418     /**
13419      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13420      */
13421     blockFocus : false,
13422     
13423     /**
13424      * @cfg {Boolean} disableClear Disable showing of clear button.
13425      */
13426     disableClear : false,
13427     /**
13428      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13429      */
13430     alwaysQuery : false,
13431     
13432     /**
13433      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13434      */
13435     multiple : false,
13436     
13437     /**
13438      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13439      */
13440     invalidClass : "has-warning",
13441     
13442     /**
13443      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13444      */
13445     validClass : "has-success",
13446     
13447     /**
13448      * @cfg {Boolean} specialFilter (true|false) special filter default false
13449      */
13450     specialFilter : false,
13451     
13452     /**
13453      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13454      */
13455     mobileTouchView : true,
13456     
13457     /**
13458      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13459      */
13460     useNativeIOS : false,
13461     
13462     /**
13463      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13464      */
13465     mobile_restrict_height : false,
13466     
13467     ios_options : false,
13468     
13469     //private
13470     addicon : false,
13471     editicon: false,
13472     
13473     page: 0,
13474     hasQuery: false,
13475     append: false,
13476     loadNext: false,
13477     autoFocus : true,
13478     tickable : false,
13479     btnPosition : 'right',
13480     triggerList : true,
13481     showToggleBtn : true,
13482     animate : true,
13483     emptyResultText: 'Empty',
13484     triggerText : 'Select',
13485     emptyTitle : '',
13486     
13487     // element that contains real text value.. (when hidden is used..)
13488     
13489     getAutoCreate : function()
13490     {   
13491         var cfg = false;
13492         //render
13493         /*
13494          * Render classic select for iso
13495          */
13496         
13497         if(Roo.isIOS && this.useNativeIOS){
13498             cfg = this.getAutoCreateNativeIOS();
13499             return cfg;
13500         }
13501         
13502         /*
13503          * Touch Devices
13504          */
13505         
13506         if(Roo.isTouch && this.mobileTouchView){
13507             cfg = this.getAutoCreateTouchView();
13508             return cfg;;
13509         }
13510         
13511         /*
13512          *  Normal ComboBox
13513          */
13514         if(!this.tickable){
13515             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13516             return cfg;
13517         }
13518         
13519         /*
13520          *  ComboBox with tickable selections
13521          */
13522              
13523         var align = this.labelAlign || this.parentLabelAlign();
13524         
13525         cfg = {
13526             cls : 'form-group roo-combobox-tickable' //input-group
13527         };
13528         
13529         var btn_text_select = '';
13530         var btn_text_done = '';
13531         var btn_text_cancel = '';
13532         
13533         if (this.btn_text_show) {
13534             btn_text_select = 'Select';
13535             btn_text_done = 'Done';
13536             btn_text_cancel = 'Cancel'; 
13537         }
13538         
13539         var buttons = {
13540             tag : 'div',
13541             cls : 'tickable-buttons',
13542             cn : [
13543                 {
13544                     tag : 'button',
13545                     type : 'button',
13546                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13547                     //html : this.triggerText
13548                     html: btn_text_select
13549                 },
13550                 {
13551                     tag : 'button',
13552                     type : 'button',
13553                     name : 'ok',
13554                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13555                     //html : 'Done'
13556                     html: btn_text_done
13557                 },
13558                 {
13559                     tag : 'button',
13560                     type : 'button',
13561                     name : 'cancel',
13562                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13563                     //html : 'Cancel'
13564                     html: btn_text_cancel
13565                 }
13566             ]
13567         };
13568         
13569         if(this.editable){
13570             buttons.cn.unshift({
13571                 tag: 'input',
13572                 cls: 'roo-select2-search-field-input'
13573             });
13574         }
13575         
13576         var _this = this;
13577         
13578         Roo.each(buttons.cn, function(c){
13579             if (_this.size) {
13580                 c.cls += ' btn-' + _this.size;
13581             }
13582
13583             if (_this.disabled) {
13584                 c.disabled = true;
13585             }
13586         });
13587         
13588         var box = {
13589             tag: 'div',
13590             style : 'display: contents',
13591             cn: [
13592                 {
13593                     tag: 'input',
13594                     type : 'hidden',
13595                     cls: 'form-hidden-field'
13596                 },
13597                 {
13598                     tag: 'ul',
13599                     cls: 'roo-select2-choices',
13600                     cn:[
13601                         {
13602                             tag: 'li',
13603                             cls: 'roo-select2-search-field',
13604                             cn: [
13605                                 buttons
13606                             ]
13607                         }
13608                     ]
13609                 }
13610             ]
13611         };
13612         
13613         var combobox = {
13614             cls: 'roo-select2-container input-group roo-select2-container-multi',
13615             cn: [
13616                 
13617                 box
13618 //                {
13619 //                    tag: 'ul',
13620 //                    cls: 'typeahead typeahead-long dropdown-menu',
13621 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13622 //                }
13623             ]
13624         };
13625         
13626         if(this.hasFeedback && !this.allowBlank){
13627             
13628             var feedback = {
13629                 tag: 'span',
13630                 cls: 'glyphicon form-control-feedback'
13631             };
13632
13633             combobox.cn.push(feedback);
13634         }
13635         
13636         var indicator = {
13637             tag : 'i',
13638             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13639             tooltip : 'This field is required'
13640         };
13641         if (Roo.bootstrap.version == 4) {
13642             indicator = {
13643                 tag : 'i',
13644                 style : 'display:none'
13645             };
13646         }
13647         if (align ==='left' && this.fieldLabel.length) {
13648             
13649             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13650             
13651             cfg.cn = [
13652                 indicator,
13653                 {
13654                     tag: 'label',
13655                     'for' :  id,
13656                     cls : 'control-label col-form-label',
13657                     html : this.fieldLabel
13658
13659                 },
13660                 {
13661                     cls : "", 
13662                     cn: [
13663                         combobox
13664                     ]
13665                 }
13666
13667             ];
13668             
13669             var labelCfg = cfg.cn[1];
13670             var contentCfg = cfg.cn[2];
13671             
13672
13673             if(this.indicatorpos == 'right'){
13674                 
13675                 cfg.cn = [
13676                     {
13677                         tag: 'label',
13678                         'for' :  id,
13679                         cls : 'control-label col-form-label',
13680                         cn : [
13681                             {
13682                                 tag : 'span',
13683                                 html : this.fieldLabel
13684                             },
13685                             indicator
13686                         ]
13687                     },
13688                     {
13689                         cls : "",
13690                         cn: [
13691                             combobox
13692                         ]
13693                     }
13694
13695                 ];
13696                 
13697                 
13698                 
13699                 labelCfg = cfg.cn[0];
13700                 contentCfg = cfg.cn[1];
13701             
13702             }
13703             
13704             if(this.labelWidth > 12){
13705                 labelCfg.style = "width: " + this.labelWidth + 'px';
13706             }
13707             
13708             if(this.labelWidth < 13 && this.labelmd == 0){
13709                 this.labelmd = this.labelWidth;
13710             }
13711             
13712             if(this.labellg > 0){
13713                 labelCfg.cls += ' col-lg-' + this.labellg;
13714                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13715             }
13716             
13717             if(this.labelmd > 0){
13718                 labelCfg.cls += ' col-md-' + this.labelmd;
13719                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13720             }
13721             
13722             if(this.labelsm > 0){
13723                 labelCfg.cls += ' col-sm-' + this.labelsm;
13724                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13725             }
13726             
13727             if(this.labelxs > 0){
13728                 labelCfg.cls += ' col-xs-' + this.labelxs;
13729                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13730             }
13731                 
13732                 
13733         } else if ( this.fieldLabel.length) {
13734 //                Roo.log(" label");
13735                  cfg.cn = [
13736                    indicator,
13737                     {
13738                         tag: 'label',
13739                         //cls : 'input-group-addon',
13740                         html : this.fieldLabel
13741                     },
13742                     combobox
13743                 ];
13744                 
13745                 if(this.indicatorpos == 'right'){
13746                     cfg.cn = [
13747                         {
13748                             tag: 'label',
13749                             //cls : 'input-group-addon',
13750                             html : this.fieldLabel
13751                         },
13752                         indicator,
13753                         combobox
13754                     ];
13755                     
13756                 }
13757
13758         } else {
13759             
13760 //                Roo.log(" no label && no align");
13761                 cfg = combobox
13762                      
13763                 
13764         }
13765          
13766         var settings=this;
13767         ['xs','sm','md','lg'].map(function(size){
13768             if (settings[size]) {
13769                 cfg.cls += ' col-' + size + '-' + settings[size];
13770             }
13771         });
13772         
13773         return cfg;
13774         
13775     },
13776     
13777     _initEventsCalled : false,
13778     
13779     // private
13780     initEvents: function()
13781     {   
13782         if (this._initEventsCalled) { // as we call render... prevent looping...
13783             return;
13784         }
13785         this._initEventsCalled = true;
13786         
13787         if (!this.store) {
13788             throw "can not find store for combo";
13789         }
13790         
13791         this.indicator = this.indicatorEl();
13792         
13793         this.store = Roo.factory(this.store, Roo.data);
13794         this.store.parent = this;
13795         
13796         // if we are building from html. then this element is so complex, that we can not really
13797         // use the rendered HTML.
13798         // so we have to trash and replace the previous code.
13799         if (Roo.XComponent.build_from_html) {
13800             // remove this element....
13801             var e = this.el.dom, k=0;
13802             while (e ) { e = e.previousSibling;  ++k;}
13803
13804             this.el.remove();
13805             
13806             this.el=false;
13807             this.rendered = false;
13808             
13809             this.render(this.parent().getChildContainer(true), k);
13810         }
13811         
13812         if(Roo.isIOS && this.useNativeIOS){
13813             this.initIOSView();
13814             return;
13815         }
13816         
13817         /*
13818          * Touch Devices
13819          */
13820         
13821         if(Roo.isTouch && this.mobileTouchView){
13822             this.initTouchView();
13823             return;
13824         }
13825         
13826         if(this.tickable){
13827             this.initTickableEvents();
13828             return;
13829         }
13830         
13831         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13832         
13833         if(this.hiddenName){
13834             
13835             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13836             
13837             this.hiddenField.dom.value =
13838                 this.hiddenValue !== undefined ? this.hiddenValue :
13839                 this.value !== undefined ? this.value : '';
13840
13841             // prevent input submission
13842             this.el.dom.removeAttribute('name');
13843             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13844              
13845              
13846         }
13847         //if(Roo.isGecko){
13848         //    this.el.dom.setAttribute('autocomplete', 'off');
13849         //}
13850         
13851         var cls = 'x-combo-list';
13852         
13853         //this.list = new Roo.Layer({
13854         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13855         //});
13856         
13857         var _this = this;
13858         
13859         (function(){
13860             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13861             _this.list.setWidth(lw);
13862         }).defer(100);
13863         
13864         this.list.on('mouseover', this.onViewOver, this);
13865         this.list.on('mousemove', this.onViewMove, this);
13866         this.list.on('scroll', this.onViewScroll, this);
13867         
13868         /*
13869         this.list.swallowEvent('mousewheel');
13870         this.assetHeight = 0;
13871
13872         if(this.title){
13873             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13874             this.assetHeight += this.header.getHeight();
13875         }
13876
13877         this.innerList = this.list.createChild({cls:cls+'-inner'});
13878         this.innerList.on('mouseover', this.onViewOver, this);
13879         this.innerList.on('mousemove', this.onViewMove, this);
13880         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13881         
13882         if(this.allowBlank && !this.pageSize && !this.disableClear){
13883             this.footer = this.list.createChild({cls:cls+'-ft'});
13884             this.pageTb = new Roo.Toolbar(this.footer);
13885            
13886         }
13887         if(this.pageSize){
13888             this.footer = this.list.createChild({cls:cls+'-ft'});
13889             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13890                     {pageSize: this.pageSize});
13891             
13892         }
13893         
13894         if (this.pageTb && this.allowBlank && !this.disableClear) {
13895             var _this = this;
13896             this.pageTb.add(new Roo.Toolbar.Fill(), {
13897                 cls: 'x-btn-icon x-btn-clear',
13898                 text: '&#160;',
13899                 handler: function()
13900                 {
13901                     _this.collapse();
13902                     _this.clearValue();
13903                     _this.onSelect(false, -1);
13904                 }
13905             });
13906         }
13907         if (this.footer) {
13908             this.assetHeight += this.footer.getHeight();
13909         }
13910         */
13911             
13912         if(!this.tpl){
13913             this.tpl = Roo.bootstrap.version == 4 ?
13914                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13915                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13916         }
13917
13918         this.view = new Roo.View(this.list, this.tpl, {
13919             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13920         });
13921         //this.view.wrapEl.setDisplayed(false);
13922         this.view.on('click', this.onViewClick, this);
13923         
13924         
13925         this.store.on('beforeload', this.onBeforeLoad, this);
13926         this.store.on('load', this.onLoad, this);
13927         this.store.on('loadexception', this.onLoadException, this);
13928         /*
13929         if(this.resizable){
13930             this.resizer = new Roo.Resizable(this.list,  {
13931                pinned:true, handles:'se'
13932             });
13933             this.resizer.on('resize', function(r, w, h){
13934                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13935                 this.listWidth = w;
13936                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13937                 this.restrictHeight();
13938             }, this);
13939             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13940         }
13941         */
13942         if(!this.editable){
13943             this.editable = true;
13944             this.setEditable(false);
13945         }
13946         
13947         /*
13948         
13949         if (typeof(this.events.add.listeners) != 'undefined') {
13950             
13951             this.addicon = this.wrap.createChild(
13952                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13953        
13954             this.addicon.on('click', function(e) {
13955                 this.fireEvent('add', this);
13956             }, this);
13957         }
13958         if (typeof(this.events.edit.listeners) != 'undefined') {
13959             
13960             this.editicon = this.wrap.createChild(
13961                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13962             if (this.addicon) {
13963                 this.editicon.setStyle('margin-left', '40px');
13964             }
13965             this.editicon.on('click', function(e) {
13966                 
13967                 // we fire even  if inothing is selected..
13968                 this.fireEvent('edit', this, this.lastData );
13969                 
13970             }, this);
13971         }
13972         */
13973         
13974         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13975             "up" : function(e){
13976                 this.inKeyMode = true;
13977                 this.selectPrev();
13978             },
13979
13980             "down" : function(e){
13981                 if(!this.isExpanded()){
13982                     this.onTriggerClick();
13983                 }else{
13984                     this.inKeyMode = true;
13985                     this.selectNext();
13986                 }
13987             },
13988
13989             "enter" : function(e){
13990 //                this.onViewClick();
13991                 //return true;
13992                 this.collapse();
13993                 
13994                 if(this.fireEvent("specialkey", this, e)){
13995                     this.onViewClick(false);
13996                 }
13997                 
13998                 return true;
13999             },
14000
14001             "esc" : function(e){
14002                 this.collapse();
14003             },
14004
14005             "tab" : function(e){
14006                 this.collapse();
14007                 
14008                 if(this.fireEvent("specialkey", this, e)){
14009                     this.onViewClick(false);
14010                 }
14011                 
14012                 return true;
14013             },
14014
14015             scope : this,
14016
14017             doRelay : function(foo, bar, hname){
14018                 if(hname == 'down' || this.scope.isExpanded()){
14019                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14020                 }
14021                 return true;
14022             },
14023
14024             forceKeyDown: true
14025         });
14026         
14027         
14028         this.queryDelay = Math.max(this.queryDelay || 10,
14029                 this.mode == 'local' ? 10 : 250);
14030         
14031         
14032         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14033         
14034         if(this.typeAhead){
14035             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14036         }
14037         if(this.editable !== false){
14038             this.inputEl().on("keyup", this.onKeyUp, this);
14039         }
14040         if(this.forceSelection){
14041             this.inputEl().on('blur', this.doForce, this);
14042         }
14043         
14044         if(this.multiple){
14045             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14046             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14047         }
14048     },
14049     
14050     initTickableEvents: function()
14051     {   
14052         this.createList();
14053         
14054         if(this.hiddenName){
14055             
14056             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14057             
14058             this.hiddenField.dom.value =
14059                 this.hiddenValue !== undefined ? this.hiddenValue :
14060                 this.value !== undefined ? this.value : '';
14061
14062             // prevent input submission
14063             this.el.dom.removeAttribute('name');
14064             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14065              
14066              
14067         }
14068         
14069 //        this.list = this.el.select('ul.dropdown-menu',true).first();
14070         
14071         this.choices = this.el.select('ul.roo-select2-choices', true).first();
14072         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14073         if(this.triggerList){
14074             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14075         }
14076          
14077         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14078         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14079         
14080         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14081         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14082         
14083         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14084         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14085         
14086         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14087         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14088         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14089         
14090         this.okBtn.hide();
14091         this.cancelBtn.hide();
14092         
14093         var _this = this;
14094         
14095         (function(){
14096             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14097             _this.list.setWidth(lw);
14098         }).defer(100);
14099         
14100         this.list.on('mouseover', this.onViewOver, this);
14101         this.list.on('mousemove', this.onViewMove, this);
14102         
14103         this.list.on('scroll', this.onViewScroll, this);
14104         
14105         if(!this.tpl){
14106             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14107                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14108         }
14109
14110         this.view = new Roo.View(this.list, this.tpl, {
14111             singleSelect:true,
14112             tickable:true,
14113             parent:this,
14114             store: this.store,
14115             selectedClass: this.selectedClass
14116         });
14117         
14118         //this.view.wrapEl.setDisplayed(false);
14119         this.view.on('click', this.onViewClick, this);
14120         
14121         
14122         
14123         this.store.on('beforeload', this.onBeforeLoad, this);
14124         this.store.on('load', this.onLoad, this);
14125         this.store.on('loadexception', this.onLoadException, this);
14126         
14127         if(this.editable){
14128             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14129                 "up" : function(e){
14130                     this.inKeyMode = true;
14131                     this.selectPrev();
14132                 },
14133
14134                 "down" : function(e){
14135                     this.inKeyMode = true;
14136                     this.selectNext();
14137                 },
14138
14139                 "enter" : function(e){
14140                     if(this.fireEvent("specialkey", this, e)){
14141                         this.onViewClick(false);
14142                     }
14143                     
14144                     return true;
14145                 },
14146
14147                 "esc" : function(e){
14148                     this.onTickableFooterButtonClick(e, false, false);
14149                 },
14150
14151                 "tab" : function(e){
14152                     this.fireEvent("specialkey", this, e);
14153                     
14154                     this.onTickableFooterButtonClick(e, false, false);
14155                     
14156                     return true;
14157                 },
14158
14159                 scope : this,
14160
14161                 doRelay : function(e, fn, key){
14162                     if(this.scope.isExpanded()){
14163                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14164                     }
14165                     return true;
14166                 },
14167
14168                 forceKeyDown: true
14169             });
14170         }
14171         
14172         this.queryDelay = Math.max(this.queryDelay || 10,
14173                 this.mode == 'local' ? 10 : 250);
14174         
14175         
14176         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14177         
14178         if(this.typeAhead){
14179             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14180         }
14181         
14182         if(this.editable !== false){
14183             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14184         }
14185         
14186         this.indicator = this.indicatorEl();
14187         
14188         if(this.indicator){
14189             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14190             this.indicator.hide();
14191         }
14192         
14193     },
14194
14195     onDestroy : function(){
14196         if(this.view){
14197             this.view.setStore(null);
14198             this.view.el.removeAllListeners();
14199             this.view.el.remove();
14200             this.view.purgeListeners();
14201         }
14202         if(this.list){
14203             this.list.dom.innerHTML  = '';
14204         }
14205         
14206         if(this.store){
14207             this.store.un('beforeload', this.onBeforeLoad, this);
14208             this.store.un('load', this.onLoad, this);
14209             this.store.un('loadexception', this.onLoadException, this);
14210         }
14211         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14212     },
14213
14214     // private
14215     fireKey : function(e){
14216         if(e.isNavKeyPress() && !this.list.isVisible()){
14217             this.fireEvent("specialkey", this, e);
14218         }
14219     },
14220
14221     // private
14222     onResize: function(w, h){
14223 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14224 //        
14225 //        if(typeof w != 'number'){
14226 //            // we do not handle it!?!?
14227 //            return;
14228 //        }
14229 //        var tw = this.trigger.getWidth();
14230 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14231 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14232 //        var x = w - tw;
14233 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14234 //            
14235 //        //this.trigger.setStyle('left', x+'px');
14236 //        
14237 //        if(this.list && this.listWidth === undefined){
14238 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14239 //            this.list.setWidth(lw);
14240 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14241 //        }
14242         
14243     
14244         
14245     },
14246
14247     /**
14248      * Allow or prevent the user from directly editing the field text.  If false is passed,
14249      * the user will only be able to select from the items defined in the dropdown list.  This method
14250      * is the runtime equivalent of setting the 'editable' config option at config time.
14251      * @param {Boolean} value True to allow the user to directly edit the field text
14252      */
14253     setEditable : function(value){
14254         if(value == this.editable){
14255             return;
14256         }
14257         this.editable = value;
14258         if(!value){
14259             this.inputEl().dom.setAttribute('readOnly', true);
14260             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14261             this.inputEl().addClass('x-combo-noedit');
14262         }else{
14263             this.inputEl().dom.setAttribute('readOnly', false);
14264             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14265             this.inputEl().removeClass('x-combo-noedit');
14266         }
14267     },
14268
14269     // private
14270     
14271     onBeforeLoad : function(combo,opts){
14272         if(!this.hasFocus){
14273             return;
14274         }
14275          if (!opts.add) {
14276             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14277          }
14278         this.restrictHeight();
14279         this.selectedIndex = -1;
14280     },
14281
14282     // private
14283     onLoad : function(){
14284         
14285         this.hasQuery = false;
14286         
14287         if(!this.hasFocus){
14288             return;
14289         }
14290         
14291         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14292             this.loading.hide();
14293         }
14294         
14295         if(this.store.getCount() > 0){
14296             
14297             this.expand();
14298             this.restrictHeight();
14299             if(this.lastQuery == this.allQuery){
14300                 if(this.editable && !this.tickable){
14301                     this.inputEl().dom.select();
14302                 }
14303                 
14304                 if(
14305                     !this.selectByValue(this.value, true) &&
14306                     this.autoFocus && 
14307                     (
14308                         !this.store.lastOptions ||
14309                         typeof(this.store.lastOptions.add) == 'undefined' || 
14310                         this.store.lastOptions.add != true
14311                     )
14312                 ){
14313                     this.select(0, true);
14314                 }
14315             }else{
14316                 if(this.autoFocus){
14317                     this.selectNext();
14318                 }
14319                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14320                     this.taTask.delay(this.typeAheadDelay);
14321                 }
14322             }
14323         }else{
14324             this.onEmptyResults();
14325         }
14326         
14327         //this.el.focus();
14328     },
14329     // private
14330     onLoadException : function()
14331     {
14332         this.hasQuery = false;
14333         
14334         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14335             this.loading.hide();
14336         }
14337         
14338         if(this.tickable && this.editable){
14339             return;
14340         }
14341         
14342         this.collapse();
14343         // only causes errors at present
14344         //Roo.log(this.store.reader.jsonData);
14345         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14346             // fixme
14347             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14348         //}
14349         
14350         
14351     },
14352     // private
14353     onTypeAhead : function(){
14354         if(this.store.getCount() > 0){
14355             var r = this.store.getAt(0);
14356             var newValue = r.data[this.displayField];
14357             var len = newValue.length;
14358             var selStart = this.getRawValue().length;
14359             
14360             if(selStart != len){
14361                 this.setRawValue(newValue);
14362                 this.selectText(selStart, newValue.length);
14363             }
14364         }
14365     },
14366
14367     // private
14368     onSelect : function(record, index){
14369         
14370         if(this.fireEvent('beforeselect', this, record, index) !== false){
14371         
14372             this.setFromData(index > -1 ? record.data : false);
14373             
14374             this.collapse();
14375             this.fireEvent('select', this, record, index);
14376         }
14377     },
14378
14379     /**
14380      * Returns the currently selected field value or empty string if no value is set.
14381      * @return {String} value The selected value
14382      */
14383     getValue : function()
14384     {
14385         if(Roo.isIOS && this.useNativeIOS){
14386             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14387         }
14388         
14389         if(this.multiple){
14390             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14391         }
14392         
14393         if(this.valueField){
14394             return typeof this.value != 'undefined' ? this.value : '';
14395         }else{
14396             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14397         }
14398     },
14399     
14400     getRawValue : function()
14401     {
14402         if(Roo.isIOS && this.useNativeIOS){
14403             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14404         }
14405         
14406         var v = this.inputEl().getValue();
14407         
14408         return v;
14409     },
14410
14411     /**
14412      * Clears any text/value currently set in the field
14413      */
14414     clearValue : function(){
14415         
14416         if(this.hiddenField){
14417             this.hiddenField.dom.value = '';
14418         }
14419         this.value = '';
14420         this.setRawValue('');
14421         this.lastSelectionText = '';
14422         this.lastData = false;
14423         
14424         var close = this.closeTriggerEl();
14425         
14426         if(close){
14427             close.hide();
14428         }
14429         
14430         this.validate();
14431         
14432     },
14433
14434     /**
14435      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14436      * will be displayed in the field.  If the value does not match the data value of an existing item,
14437      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14438      * Otherwise the field will be blank (although the value will still be set).
14439      * @param {String} value The value to match
14440      */
14441     setValue : function(v)
14442     {
14443         if(Roo.isIOS && this.useNativeIOS){
14444             this.setIOSValue(v);
14445             return;
14446         }
14447         
14448         if(this.multiple){
14449             this.syncValue();
14450             return;
14451         }
14452         
14453         var text = v;
14454         if(this.valueField){
14455             var r = this.findRecord(this.valueField, v);
14456             if(r){
14457                 text = r.data[this.displayField];
14458             }else if(this.valueNotFoundText !== undefined){
14459                 text = this.valueNotFoundText;
14460             }
14461         }
14462         this.lastSelectionText = text;
14463         if(this.hiddenField){
14464             this.hiddenField.dom.value = v;
14465         }
14466         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14467         this.value = v;
14468         
14469         var close = this.closeTriggerEl();
14470         
14471         if(close){
14472             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14473         }
14474         
14475         this.validate();
14476     },
14477     /**
14478      * @property {Object} the last set data for the element
14479      */
14480     
14481     lastData : false,
14482     /**
14483      * Sets the value of the field based on a object which is related to the record format for the store.
14484      * @param {Object} value the value to set as. or false on reset?
14485      */
14486     setFromData : function(o){
14487         
14488         if(this.multiple){
14489             this.addItem(o);
14490             return;
14491         }
14492             
14493         var dv = ''; // display value
14494         var vv = ''; // value value..
14495         this.lastData = o;
14496         if (this.displayField) {
14497             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14498         } else {
14499             // this is an error condition!!!
14500             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14501         }
14502         
14503         if(this.valueField){
14504             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14505         }
14506         
14507         var close = this.closeTriggerEl();
14508         
14509         if(close){
14510             if(dv.length || vv * 1 > 0){
14511                 close.show() ;
14512                 this.blockFocus=true;
14513             } else {
14514                 close.hide();
14515             }             
14516         }
14517         
14518         if(this.hiddenField){
14519             this.hiddenField.dom.value = vv;
14520             
14521             this.lastSelectionText = dv;
14522             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14523             this.value = vv;
14524             return;
14525         }
14526         // no hidden field.. - we store the value in 'value', but still display
14527         // display field!!!!
14528         this.lastSelectionText = dv;
14529         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14530         this.value = vv;
14531         
14532         
14533         
14534     },
14535     // private
14536     reset : function(){
14537         // overridden so that last data is reset..
14538         
14539         if(this.multiple){
14540             this.clearItem();
14541             return;
14542         }
14543         
14544         this.setValue(this.originalValue);
14545         //this.clearInvalid();
14546         this.lastData = false;
14547         if (this.view) {
14548             this.view.clearSelections();
14549         }
14550         
14551         this.validate();
14552     },
14553     // private
14554     findRecord : function(prop, value){
14555         var record;
14556         if(this.store.getCount() > 0){
14557             this.store.each(function(r){
14558                 if(r.data[prop] == value){
14559                     record = r;
14560                     return false;
14561                 }
14562                 return true;
14563             });
14564         }
14565         return record;
14566     },
14567     
14568     getName: function()
14569     {
14570         // returns hidden if it's set..
14571         if (!this.rendered) {return ''};
14572         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14573         
14574     },
14575     // private
14576     onViewMove : function(e, t){
14577         this.inKeyMode = false;
14578     },
14579
14580     // private
14581     onViewOver : function(e, t){
14582         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14583             return;
14584         }
14585         var item = this.view.findItemFromChild(t);
14586         
14587         if(item){
14588             var index = this.view.indexOf(item);
14589             this.select(index, false);
14590         }
14591     },
14592
14593     // private
14594     onViewClick : function(view, doFocus, el, e)
14595     {
14596         var index = this.view.getSelectedIndexes()[0];
14597         
14598         var r = this.store.getAt(index);
14599         
14600         if(this.tickable){
14601             
14602             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14603                 return;
14604             }
14605             
14606             var rm = false;
14607             var _this = this;
14608             
14609             Roo.each(this.tickItems, function(v,k){
14610                 
14611                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14612                     Roo.log(v);
14613                     _this.tickItems.splice(k, 1);
14614                     
14615                     if(typeof(e) == 'undefined' && view == false){
14616                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14617                     }
14618                     
14619                     rm = true;
14620                     return;
14621                 }
14622             });
14623             
14624             if(rm){
14625                 return;
14626             }
14627             
14628             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14629                 this.tickItems.push(r.data);
14630             }
14631             
14632             if(typeof(e) == 'undefined' && view == false){
14633                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14634             }
14635                     
14636             return;
14637         }
14638         
14639         if(r){
14640             this.onSelect(r, index);
14641         }
14642         if(doFocus !== false && !this.blockFocus){
14643             this.inputEl().focus();
14644         }
14645     },
14646
14647     // private
14648     restrictHeight : function(){
14649         //this.innerList.dom.style.height = '';
14650         //var inner = this.innerList.dom;
14651         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14652         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14653         //this.list.beginUpdate();
14654         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14655         this.list.alignTo(this.inputEl(), this.listAlign);
14656         this.list.alignTo(this.inputEl(), this.listAlign);
14657         //this.list.endUpdate();
14658     },
14659
14660     // private
14661     onEmptyResults : function(){
14662         
14663         if(this.tickable && this.editable){
14664             this.hasFocus = false;
14665             this.restrictHeight();
14666             return;
14667         }
14668         
14669         this.collapse();
14670     },
14671
14672     /**
14673      * Returns true if the dropdown list is expanded, else false.
14674      */
14675     isExpanded : function(){
14676         return this.list.isVisible();
14677     },
14678
14679     /**
14680      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14681      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14682      * @param {String} value The data value of the item to select
14683      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14684      * selected item if it is not currently in view (defaults to true)
14685      * @return {Boolean} True if the value matched an item in the list, else false
14686      */
14687     selectByValue : function(v, scrollIntoView){
14688         if(v !== undefined && v !== null){
14689             var r = this.findRecord(this.valueField || this.displayField, v);
14690             if(r){
14691                 this.select(this.store.indexOf(r), scrollIntoView);
14692                 return true;
14693             }
14694         }
14695         return false;
14696     },
14697
14698     /**
14699      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14700      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14701      * @param {Number} index The zero-based index of the list item to select
14702      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14703      * selected item if it is not currently in view (defaults to true)
14704      */
14705     select : function(index, scrollIntoView){
14706         this.selectedIndex = index;
14707         this.view.select(index);
14708         if(scrollIntoView !== false){
14709             var el = this.view.getNode(index);
14710             /*
14711              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14712              */
14713             if(el){
14714                 this.list.scrollChildIntoView(el, false);
14715             }
14716         }
14717     },
14718
14719     // private
14720     selectNext : function(){
14721         var ct = this.store.getCount();
14722         if(ct > 0){
14723             if(this.selectedIndex == -1){
14724                 this.select(0);
14725             }else if(this.selectedIndex < ct-1){
14726                 this.select(this.selectedIndex+1);
14727             }
14728         }
14729     },
14730
14731     // private
14732     selectPrev : function(){
14733         var ct = this.store.getCount();
14734         if(ct > 0){
14735             if(this.selectedIndex == -1){
14736                 this.select(0);
14737             }else if(this.selectedIndex != 0){
14738                 this.select(this.selectedIndex-1);
14739             }
14740         }
14741     },
14742
14743     // private
14744     onKeyUp : function(e){
14745         if(this.editable !== false && !e.isSpecialKey()){
14746             this.lastKey = e.getKey();
14747             this.dqTask.delay(this.queryDelay);
14748         }
14749     },
14750
14751     // private
14752     validateBlur : function(){
14753         return !this.list || !this.list.isVisible();   
14754     },
14755
14756     // private
14757     initQuery : function(){
14758         
14759         var v = this.getRawValue();
14760         
14761         if(this.tickable && this.editable){
14762             v = this.tickableInputEl().getValue();
14763         }
14764         
14765         this.doQuery(v);
14766     },
14767
14768     // private
14769     doForce : function(){
14770         if(this.inputEl().dom.value.length > 0){
14771             this.inputEl().dom.value =
14772                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14773              
14774         }
14775     },
14776
14777     /**
14778      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14779      * query allowing the query action to be canceled if needed.
14780      * @param {String} query The SQL query to execute
14781      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14782      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14783      * saved in the current store (defaults to false)
14784      */
14785     doQuery : function(q, forceAll){
14786         
14787         if(q === undefined || q === null){
14788             q = '';
14789         }
14790         var qe = {
14791             query: q,
14792             forceAll: forceAll,
14793             combo: this,
14794             cancel:false
14795         };
14796         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14797             return false;
14798         }
14799         q = qe.query;
14800         
14801         forceAll = qe.forceAll;
14802         if(forceAll === true || (q.length >= this.minChars)){
14803             
14804             this.hasQuery = true;
14805             
14806             if(this.lastQuery != q || this.alwaysQuery){
14807                 this.lastQuery = q;
14808                 if(this.mode == 'local'){
14809                     this.selectedIndex = -1;
14810                     if(forceAll){
14811                         this.store.clearFilter();
14812                     }else{
14813                         
14814                         if(this.specialFilter){
14815                             this.fireEvent('specialfilter', this);
14816                             this.onLoad();
14817                             return;
14818                         }
14819                         
14820                         this.store.filter(this.displayField, q);
14821                     }
14822                     
14823                     this.store.fireEvent("datachanged", this.store);
14824                     
14825                     this.onLoad();
14826                     
14827                     
14828                 }else{
14829                     
14830                     this.store.baseParams[this.queryParam] = q;
14831                     
14832                     var options = {params : this.getParams(q)};
14833                     
14834                     if(this.loadNext){
14835                         options.add = true;
14836                         options.params.start = this.page * this.pageSize;
14837                     }
14838                     
14839                     this.store.load(options);
14840                     
14841                     /*
14842                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14843                      *  we should expand the list on onLoad
14844                      *  so command out it
14845                      */
14846 //                    this.expand();
14847                 }
14848             }else{
14849                 this.selectedIndex = -1;
14850                 this.onLoad();   
14851             }
14852         }
14853         
14854         this.loadNext = false;
14855     },
14856     
14857     // private
14858     getParams : function(q){
14859         var p = {};
14860         //p[this.queryParam] = q;
14861         
14862         if(this.pageSize){
14863             p.start = 0;
14864             p.limit = this.pageSize;
14865         }
14866         return p;
14867     },
14868
14869     /**
14870      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14871      */
14872     collapse : function(){
14873         if(!this.isExpanded()){
14874             return;
14875         }
14876         
14877         this.list.hide();
14878         
14879         this.hasFocus = false;
14880         
14881         if(this.tickable){
14882             this.okBtn.hide();
14883             this.cancelBtn.hide();
14884             this.trigger.show();
14885             
14886             if(this.editable){
14887                 this.tickableInputEl().dom.value = '';
14888                 this.tickableInputEl().blur();
14889             }
14890             
14891         }
14892         
14893         Roo.get(document).un('mousedown', this.collapseIf, this);
14894         Roo.get(document).un('mousewheel', this.collapseIf, this);
14895         if (!this.editable) {
14896             Roo.get(document).un('keydown', this.listKeyPress, this);
14897         }
14898         this.fireEvent('collapse', this);
14899         
14900         this.validate();
14901     },
14902
14903     // private
14904     collapseIf : function(e){
14905         var in_combo  = e.within(this.el);
14906         var in_list =  e.within(this.list);
14907         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14908         
14909         if (in_combo || in_list || is_list) {
14910             //e.stopPropagation();
14911             return;
14912         }
14913         
14914         if(this.tickable){
14915             this.onTickableFooterButtonClick(e, false, false);
14916         }
14917
14918         this.collapse();
14919         
14920     },
14921
14922     /**
14923      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14924      */
14925     expand : function(){
14926        
14927         if(this.isExpanded() || !this.hasFocus){
14928             return;
14929         }
14930         
14931         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14932         this.list.setWidth(lw);
14933         
14934         Roo.log('expand');
14935         
14936         this.list.show();
14937         
14938         this.restrictHeight();
14939         
14940         if(this.tickable){
14941             
14942             this.tickItems = Roo.apply([], this.item);
14943             
14944             this.okBtn.show();
14945             this.cancelBtn.show();
14946             this.trigger.hide();
14947             
14948             if(this.editable){
14949                 this.tickableInputEl().focus();
14950             }
14951             
14952         }
14953         
14954         Roo.get(document).on('mousedown', this.collapseIf, this);
14955         Roo.get(document).on('mousewheel', this.collapseIf, this);
14956         if (!this.editable) {
14957             Roo.get(document).on('keydown', this.listKeyPress, this);
14958         }
14959         
14960         this.fireEvent('expand', this);
14961     },
14962
14963     // private
14964     // Implements the default empty TriggerField.onTriggerClick function
14965     onTriggerClick : function(e)
14966     {
14967         Roo.log('trigger click');
14968         
14969         if(this.disabled || !this.triggerList){
14970             return;
14971         }
14972         
14973         this.page = 0;
14974         this.loadNext = false;
14975         
14976         if(this.isExpanded()){
14977             this.collapse();
14978             if (!this.blockFocus) {
14979                 this.inputEl().focus();
14980             }
14981             
14982         }else {
14983             this.hasFocus = true;
14984             if(this.triggerAction == 'all') {
14985                 this.doQuery(this.allQuery, true);
14986             } else {
14987                 this.doQuery(this.getRawValue());
14988             }
14989             if (!this.blockFocus) {
14990                 this.inputEl().focus();
14991             }
14992         }
14993     },
14994     
14995     onTickableTriggerClick : function(e)
14996     {
14997         if(this.disabled){
14998             return;
14999         }
15000         
15001         this.page = 0;
15002         this.loadNext = false;
15003         this.hasFocus = true;
15004         
15005         if(this.triggerAction == 'all') {
15006             this.doQuery(this.allQuery, true);
15007         } else {
15008             this.doQuery(this.getRawValue());
15009         }
15010     },
15011     
15012     onSearchFieldClick : function(e)
15013     {
15014         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15015             this.onTickableFooterButtonClick(e, false, false);
15016             return;
15017         }
15018         
15019         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15020             return;
15021         }
15022         
15023         this.page = 0;
15024         this.loadNext = false;
15025         this.hasFocus = true;
15026         
15027         if(this.triggerAction == 'all') {
15028             this.doQuery(this.allQuery, true);
15029         } else {
15030             this.doQuery(this.getRawValue());
15031         }
15032     },
15033     
15034     listKeyPress : function(e)
15035     {
15036         //Roo.log('listkeypress');
15037         // scroll to first matching element based on key pres..
15038         if (e.isSpecialKey()) {
15039             return false;
15040         }
15041         var k = String.fromCharCode(e.getKey()).toUpperCase();
15042         //Roo.log(k);
15043         var match  = false;
15044         var csel = this.view.getSelectedNodes();
15045         var cselitem = false;
15046         if (csel.length) {
15047             var ix = this.view.indexOf(csel[0]);
15048             cselitem  = this.store.getAt(ix);
15049             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15050                 cselitem = false;
15051             }
15052             
15053         }
15054         
15055         this.store.each(function(v) { 
15056             if (cselitem) {
15057                 // start at existing selection.
15058                 if (cselitem.id == v.id) {
15059                     cselitem = false;
15060                 }
15061                 return true;
15062             }
15063                 
15064             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15065                 match = this.store.indexOf(v);
15066                 return false;
15067             }
15068             return true;
15069         }, this);
15070         
15071         if (match === false) {
15072             return true; // no more action?
15073         }
15074         // scroll to?
15075         this.view.select(match);
15076         var sn = Roo.get(this.view.getSelectedNodes()[0]);
15077         sn.scrollIntoView(sn.dom.parentNode, false);
15078     },
15079     
15080     onViewScroll : function(e, t){
15081         
15082         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){
15083             return;
15084         }
15085         
15086         this.hasQuery = true;
15087         
15088         this.loading = this.list.select('.loading', true).first();
15089         
15090         if(this.loading === null){
15091             this.list.createChild({
15092                 tag: 'div',
15093                 cls: 'loading roo-select2-more-results roo-select2-active',
15094                 html: 'Loading more results...'
15095             });
15096             
15097             this.loading = this.list.select('.loading', true).first();
15098             
15099             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15100             
15101             this.loading.hide();
15102         }
15103         
15104         this.loading.show();
15105         
15106         var _combo = this;
15107         
15108         this.page++;
15109         this.loadNext = true;
15110         
15111         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15112         
15113         return;
15114     },
15115     
15116     addItem : function(o)
15117     {   
15118         var dv = ''; // display value
15119         
15120         if (this.displayField) {
15121             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15122         } else {
15123             // this is an error condition!!!
15124             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15125         }
15126         
15127         if(!dv.length){
15128             return;
15129         }
15130         
15131         var choice = this.choices.createChild({
15132             tag: 'li',
15133             cls: 'roo-select2-search-choice',
15134             cn: [
15135                 {
15136                     tag: 'div',
15137                     html: dv
15138                 },
15139                 {
15140                     tag: 'a',
15141                     href: '#',
15142                     cls: 'roo-select2-search-choice-close fa fa-times',
15143                     tabindex: '-1'
15144                 }
15145             ]
15146             
15147         }, this.searchField);
15148         
15149         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15150         
15151         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15152         
15153         this.item.push(o);
15154         
15155         this.lastData = o;
15156         
15157         this.syncValue();
15158         
15159         this.inputEl().dom.value = '';
15160         
15161         this.validate();
15162     },
15163     
15164     onRemoveItem : function(e, _self, o)
15165     {
15166         e.preventDefault();
15167         
15168         this.lastItem = Roo.apply([], this.item);
15169         
15170         var index = this.item.indexOf(o.data) * 1;
15171         
15172         if( index < 0){
15173             Roo.log('not this item?!');
15174             return;
15175         }
15176         
15177         this.item.splice(index, 1);
15178         o.item.remove();
15179         
15180         this.syncValue();
15181         
15182         this.fireEvent('remove', this, e);
15183         
15184         this.validate();
15185         
15186     },
15187     
15188     syncValue : function()
15189     {
15190         if(!this.item.length){
15191             this.clearValue();
15192             return;
15193         }
15194             
15195         var value = [];
15196         var _this = this;
15197         Roo.each(this.item, function(i){
15198             if(_this.valueField){
15199                 value.push(i[_this.valueField]);
15200                 return;
15201             }
15202
15203             value.push(i);
15204         });
15205
15206         this.value = value.join(',');
15207
15208         if(this.hiddenField){
15209             this.hiddenField.dom.value = this.value;
15210         }
15211         
15212         this.store.fireEvent("datachanged", this.store);
15213         
15214         this.validate();
15215     },
15216     
15217     clearItem : function()
15218     {
15219         if(!this.multiple){
15220             return;
15221         }
15222         
15223         this.item = [];
15224         
15225         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15226            c.remove();
15227         });
15228         
15229         this.syncValue();
15230         
15231         this.validate();
15232         
15233         if(this.tickable && !Roo.isTouch){
15234             this.view.refresh();
15235         }
15236     },
15237     
15238     inputEl: function ()
15239     {
15240         if(Roo.isIOS && this.useNativeIOS){
15241             return this.el.select('select.roo-ios-select', true).first();
15242         }
15243         
15244         if(Roo.isTouch && this.mobileTouchView){
15245             return this.el.select('input.form-control',true).first();
15246         }
15247         
15248         if(this.tickable){
15249             return this.searchField;
15250         }
15251         
15252         return this.el.select('input.form-control',true).first();
15253     },
15254     
15255     onTickableFooterButtonClick : function(e, btn, el)
15256     {
15257         e.preventDefault();
15258         
15259         this.lastItem = Roo.apply([], this.item);
15260         
15261         if(btn && btn.name == 'cancel'){
15262             this.tickItems = Roo.apply([], this.item);
15263             this.collapse();
15264             return;
15265         }
15266         
15267         this.clearItem();
15268         
15269         var _this = this;
15270         
15271         Roo.each(this.tickItems, function(o){
15272             _this.addItem(o);
15273         });
15274         
15275         this.collapse();
15276         
15277     },
15278     
15279     validate : function()
15280     {
15281         if(this.getVisibilityEl().hasClass('hidden')){
15282             return true;
15283         }
15284         
15285         var v = this.getRawValue();
15286         
15287         if(this.multiple){
15288             v = this.getValue();
15289         }
15290         
15291         if(this.disabled || this.allowBlank || v.length){
15292             this.markValid();
15293             return true;
15294         }
15295         
15296         this.markInvalid();
15297         return false;
15298     },
15299     
15300     tickableInputEl : function()
15301     {
15302         if(!this.tickable || !this.editable){
15303             return this.inputEl();
15304         }
15305         
15306         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15307     },
15308     
15309     
15310     getAutoCreateTouchView : function()
15311     {
15312         var id = Roo.id();
15313         
15314         var cfg = {
15315             cls: 'form-group' //input-group
15316         };
15317         
15318         var input =  {
15319             tag: 'input',
15320             id : id,
15321             type : this.inputType,
15322             cls : 'form-control x-combo-noedit',
15323             autocomplete: 'new-password',
15324             placeholder : this.placeholder || '',
15325             readonly : true
15326         };
15327         
15328         if (this.name) {
15329             input.name = this.name;
15330         }
15331         
15332         if (this.size) {
15333             input.cls += ' input-' + this.size;
15334         }
15335         
15336         if (this.disabled) {
15337             input.disabled = true;
15338         }
15339         
15340         var inputblock = {
15341             cls : '',
15342             cn : [
15343                 input
15344             ]
15345         };
15346         
15347         if(this.before){
15348             inputblock.cls += ' input-group';
15349             
15350             inputblock.cn.unshift({
15351                 tag :'span',
15352                 cls : 'input-group-addon input-group-prepend input-group-text',
15353                 html : this.before
15354             });
15355         }
15356         
15357         if(this.removable && !this.multiple){
15358             inputblock.cls += ' roo-removable';
15359             
15360             inputblock.cn.push({
15361                 tag: 'button',
15362                 html : 'x',
15363                 cls : 'roo-combo-removable-btn close'
15364             });
15365         }
15366
15367         if(this.hasFeedback && !this.allowBlank){
15368             
15369             inputblock.cls += ' has-feedback';
15370             
15371             inputblock.cn.push({
15372                 tag: 'span',
15373                 cls: 'glyphicon form-control-feedback'
15374             });
15375             
15376         }
15377         
15378         if (this.after) {
15379             
15380             inputblock.cls += (this.before) ? '' : ' input-group';
15381             
15382             inputblock.cn.push({
15383                 tag :'span',
15384                 cls : 'input-group-addon input-group-append input-group-text',
15385                 html : this.after
15386             });
15387         }
15388
15389         
15390         var ibwrap = inputblock;
15391         
15392         if(this.multiple){
15393             ibwrap = {
15394                 tag: 'ul',
15395                 cls: 'roo-select2-choices',
15396                 cn:[
15397                     {
15398                         tag: 'li',
15399                         cls: 'roo-select2-search-field',
15400                         cn: [
15401
15402                             inputblock
15403                         ]
15404                     }
15405                 ]
15406             };
15407         
15408             
15409         }
15410         
15411         var combobox = {
15412             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15413             cn: [
15414                 {
15415                     tag: 'input',
15416                     type : 'hidden',
15417                     cls: 'form-hidden-field'
15418                 },
15419                 ibwrap
15420             ]
15421         };
15422         
15423         if(!this.multiple && this.showToggleBtn){
15424             
15425             var caret = {
15426                 cls: 'caret'
15427             };
15428             
15429             if (this.caret != false) {
15430                 caret = {
15431                      tag: 'i',
15432                      cls: 'fa fa-' + this.caret
15433                 };
15434                 
15435             }
15436             
15437             combobox.cn.push({
15438                 tag :'span',
15439                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15440                 cn : [
15441                     Roo.bootstrap.version == 3 ? caret : '',
15442                     {
15443                         tag: 'span',
15444                         cls: 'combobox-clear',
15445                         cn  : [
15446                             {
15447                                 tag : 'i',
15448                                 cls: 'icon-remove'
15449                             }
15450                         ]
15451                     }
15452                 ]
15453
15454             })
15455         }
15456         
15457         if(this.multiple){
15458             combobox.cls += ' roo-select2-container-multi';
15459         }
15460         
15461         var align = this.labelAlign || this.parentLabelAlign();
15462         
15463         if (align ==='left' && this.fieldLabel.length) {
15464
15465             cfg.cn = [
15466                 {
15467                    tag : 'i',
15468                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15469                    tooltip : 'This field is required'
15470                 },
15471                 {
15472                     tag: 'label',
15473                     cls : 'control-label col-form-label',
15474                     html : this.fieldLabel
15475
15476                 },
15477                 {
15478                     cls : '', 
15479                     cn: [
15480                         combobox
15481                     ]
15482                 }
15483             ];
15484             
15485             var labelCfg = cfg.cn[1];
15486             var contentCfg = cfg.cn[2];
15487             
15488
15489             if(this.indicatorpos == 'right'){
15490                 cfg.cn = [
15491                     {
15492                         tag: 'label',
15493                         'for' :  id,
15494                         cls : 'control-label col-form-label',
15495                         cn : [
15496                             {
15497                                 tag : 'span',
15498                                 html : this.fieldLabel
15499                             },
15500                             {
15501                                 tag : 'i',
15502                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15503                                 tooltip : 'This field is required'
15504                             }
15505                         ]
15506                     },
15507                     {
15508                         cls : "",
15509                         cn: [
15510                             combobox
15511                         ]
15512                     }
15513
15514                 ];
15515                 
15516                 labelCfg = cfg.cn[0];
15517                 contentCfg = cfg.cn[1];
15518             }
15519             
15520            
15521             
15522             if(this.labelWidth > 12){
15523                 labelCfg.style = "width: " + this.labelWidth + 'px';
15524             }
15525             
15526             if(this.labelWidth < 13 && this.labelmd == 0){
15527                 this.labelmd = this.labelWidth;
15528             }
15529             
15530             if(this.labellg > 0){
15531                 labelCfg.cls += ' col-lg-' + this.labellg;
15532                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15533             }
15534             
15535             if(this.labelmd > 0){
15536                 labelCfg.cls += ' col-md-' + this.labelmd;
15537                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15538             }
15539             
15540             if(this.labelsm > 0){
15541                 labelCfg.cls += ' col-sm-' + this.labelsm;
15542                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15543             }
15544             
15545             if(this.labelxs > 0){
15546                 labelCfg.cls += ' col-xs-' + this.labelxs;
15547                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15548             }
15549                 
15550                 
15551         } else if ( this.fieldLabel.length) {
15552             cfg.cn = [
15553                 {
15554                    tag : 'i',
15555                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15556                    tooltip : 'This field is required'
15557                 },
15558                 {
15559                     tag: 'label',
15560                     cls : 'control-label',
15561                     html : this.fieldLabel
15562
15563                 },
15564                 {
15565                     cls : '', 
15566                     cn: [
15567                         combobox
15568                     ]
15569                 }
15570             ];
15571             
15572             if(this.indicatorpos == 'right'){
15573                 cfg.cn = [
15574                     {
15575                         tag: 'label',
15576                         cls : 'control-label',
15577                         html : this.fieldLabel,
15578                         cn : [
15579                             {
15580                                tag : 'i',
15581                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15582                                tooltip : 'This field is required'
15583                             }
15584                         ]
15585                     },
15586                     {
15587                         cls : '', 
15588                         cn: [
15589                             combobox
15590                         ]
15591                     }
15592                 ];
15593             }
15594         } else {
15595             cfg.cn = combobox;    
15596         }
15597         
15598         
15599         var settings = this;
15600         
15601         ['xs','sm','md','lg'].map(function(size){
15602             if (settings[size]) {
15603                 cfg.cls += ' col-' + size + '-' + settings[size];
15604             }
15605         });
15606         
15607         return cfg;
15608     },
15609     
15610     initTouchView : function()
15611     {
15612         this.renderTouchView();
15613         
15614         this.touchViewEl.on('scroll', function(){
15615             this.el.dom.scrollTop = 0;
15616         }, this);
15617         
15618         this.originalValue = this.getValue();
15619         
15620         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15621         
15622         this.inputEl().on("click", this.showTouchView, this);
15623         if (this.triggerEl) {
15624             this.triggerEl.on("click", this.showTouchView, this);
15625         }
15626         
15627         
15628         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15629         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15630         
15631         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15632         
15633         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15634         this.store.on('load', this.onTouchViewLoad, this);
15635         this.store.on('loadexception', this.onTouchViewLoadException, this);
15636         
15637         if(this.hiddenName){
15638             
15639             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15640             
15641             this.hiddenField.dom.value =
15642                 this.hiddenValue !== undefined ? this.hiddenValue :
15643                 this.value !== undefined ? this.value : '';
15644         
15645             this.el.dom.removeAttribute('name');
15646             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15647         }
15648         
15649         if(this.multiple){
15650             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15651             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15652         }
15653         
15654         if(this.removable && !this.multiple){
15655             var close = this.closeTriggerEl();
15656             if(close){
15657                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15658                 close.on('click', this.removeBtnClick, this, close);
15659             }
15660         }
15661         /*
15662          * fix the bug in Safari iOS8
15663          */
15664         this.inputEl().on("focus", function(e){
15665             document.activeElement.blur();
15666         }, this);
15667         
15668         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15669         
15670         return;
15671         
15672         
15673     },
15674     
15675     renderTouchView : function()
15676     {
15677         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15678         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15679         
15680         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15681         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15682         
15683         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15684         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15685         this.touchViewBodyEl.setStyle('overflow', 'auto');
15686         
15687         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15688         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15689         
15690         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15691         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15692         
15693     },
15694     
15695     showTouchView : function()
15696     {
15697         if(this.disabled){
15698             return;
15699         }
15700         
15701         this.touchViewHeaderEl.hide();
15702
15703         if(this.modalTitle.length){
15704             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15705             this.touchViewHeaderEl.show();
15706         }
15707
15708         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15709         this.touchViewEl.show();
15710
15711         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15712         
15713         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15714         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15715
15716         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15717
15718         if(this.modalTitle.length){
15719             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15720         }
15721         
15722         this.touchViewBodyEl.setHeight(bodyHeight);
15723
15724         if(this.animate){
15725             var _this = this;
15726             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15727         }else{
15728             this.touchViewEl.addClass('in');
15729         }
15730         
15731         if(this._touchViewMask){
15732             Roo.get(document.body).addClass("x-body-masked");
15733             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15734             this._touchViewMask.setStyle('z-index', 10000);
15735             this._touchViewMask.addClass('show');
15736         }
15737         
15738         this.doTouchViewQuery();
15739         
15740     },
15741     
15742     hideTouchView : function()
15743     {
15744         this.touchViewEl.removeClass('in');
15745
15746         if(this.animate){
15747             var _this = this;
15748             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15749         }else{
15750             this.touchViewEl.setStyle('display', 'none');
15751         }
15752         
15753         if(this._touchViewMask){
15754             this._touchViewMask.removeClass('show');
15755             Roo.get(document.body).removeClass("x-body-masked");
15756         }
15757     },
15758     
15759     setTouchViewValue : function()
15760     {
15761         if(this.multiple){
15762             this.clearItem();
15763         
15764             var _this = this;
15765
15766             Roo.each(this.tickItems, function(o){
15767                 this.addItem(o);
15768             }, this);
15769         }
15770         
15771         this.hideTouchView();
15772     },
15773     
15774     doTouchViewQuery : function()
15775     {
15776         var qe = {
15777             query: '',
15778             forceAll: true,
15779             combo: this,
15780             cancel:false
15781         };
15782         
15783         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15784             return false;
15785         }
15786         
15787         if(!this.alwaysQuery || this.mode == 'local'){
15788             this.onTouchViewLoad();
15789             return;
15790         }
15791         
15792         this.store.load();
15793     },
15794     
15795     onTouchViewBeforeLoad : function(combo,opts)
15796     {
15797         return;
15798     },
15799
15800     // private
15801     onTouchViewLoad : function()
15802     {
15803         if(this.store.getCount() < 1){
15804             this.onTouchViewEmptyResults();
15805             return;
15806         }
15807         
15808         this.clearTouchView();
15809         
15810         var rawValue = this.getRawValue();
15811         
15812         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15813         
15814         this.tickItems = [];
15815         
15816         this.store.data.each(function(d, rowIndex){
15817             var row = this.touchViewListGroup.createChild(template);
15818             
15819             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15820                 row.addClass(d.data.cls);
15821             }
15822             
15823             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15824                 var cfg = {
15825                     data : d.data,
15826                     html : d.data[this.displayField]
15827                 };
15828                 
15829                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15830                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15831                 }
15832             }
15833             row.removeClass('selected');
15834             if(!this.multiple && this.valueField &&
15835                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15836             {
15837                 // radio buttons..
15838                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15839                 row.addClass('selected');
15840             }
15841             
15842             if(this.multiple && this.valueField &&
15843                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15844             {
15845                 
15846                 // checkboxes...
15847                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15848                 this.tickItems.push(d.data);
15849             }
15850             
15851             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15852             
15853         }, this);
15854         
15855         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15856         
15857         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15858
15859         if(this.modalTitle.length){
15860             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15861         }
15862
15863         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15864         
15865         if(this.mobile_restrict_height && listHeight < bodyHeight){
15866             this.touchViewBodyEl.setHeight(listHeight);
15867         }
15868         
15869         var _this = this;
15870         
15871         if(firstChecked && listHeight > bodyHeight){
15872             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15873         }
15874         
15875     },
15876     
15877     onTouchViewLoadException : function()
15878     {
15879         this.hideTouchView();
15880     },
15881     
15882     onTouchViewEmptyResults : function()
15883     {
15884         this.clearTouchView();
15885         
15886         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15887         
15888         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15889         
15890     },
15891     
15892     clearTouchView : function()
15893     {
15894         this.touchViewListGroup.dom.innerHTML = '';
15895     },
15896     
15897     onTouchViewClick : function(e, el, o)
15898     {
15899         e.preventDefault();
15900         
15901         var row = o.row;
15902         var rowIndex = o.rowIndex;
15903         
15904         var r = this.store.getAt(rowIndex);
15905         
15906         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15907             
15908             if(!this.multiple){
15909                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15910                     c.dom.removeAttribute('checked');
15911                 }, this);
15912
15913                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15914
15915                 this.setFromData(r.data);
15916
15917                 var close = this.closeTriggerEl();
15918
15919                 if(close){
15920                     close.show();
15921                 }
15922
15923                 this.hideTouchView();
15924
15925                 this.fireEvent('select', this, r, rowIndex);
15926
15927                 return;
15928             }
15929
15930             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15931                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15932                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15933                 return;
15934             }
15935
15936             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15937             this.addItem(r.data);
15938             this.tickItems.push(r.data);
15939         }
15940     },
15941     
15942     getAutoCreateNativeIOS : function()
15943     {
15944         var cfg = {
15945             cls: 'form-group' //input-group,
15946         };
15947         
15948         var combobox =  {
15949             tag: 'select',
15950             cls : 'roo-ios-select'
15951         };
15952         
15953         if (this.name) {
15954             combobox.name = this.name;
15955         }
15956         
15957         if (this.disabled) {
15958             combobox.disabled = true;
15959         }
15960         
15961         var settings = this;
15962         
15963         ['xs','sm','md','lg'].map(function(size){
15964             if (settings[size]) {
15965                 cfg.cls += ' col-' + size + '-' + settings[size];
15966             }
15967         });
15968         
15969         cfg.cn = combobox;
15970         
15971         return cfg;
15972         
15973     },
15974     
15975     initIOSView : function()
15976     {
15977         this.store.on('load', this.onIOSViewLoad, this);
15978         
15979         return;
15980     },
15981     
15982     onIOSViewLoad : function()
15983     {
15984         if(this.store.getCount() < 1){
15985             return;
15986         }
15987         
15988         this.clearIOSView();
15989         
15990         if(this.allowBlank) {
15991             
15992             var default_text = '-- SELECT --';
15993             
15994             if(this.placeholder.length){
15995                 default_text = this.placeholder;
15996             }
15997             
15998             if(this.emptyTitle.length){
15999                 default_text += ' - ' + this.emptyTitle + ' -';
16000             }
16001             
16002             var opt = this.inputEl().createChild({
16003                 tag: 'option',
16004                 value : 0,
16005                 html : default_text
16006             });
16007             
16008             var o = {};
16009             o[this.valueField] = 0;
16010             o[this.displayField] = default_text;
16011             
16012             this.ios_options.push({
16013                 data : o,
16014                 el : opt
16015             });
16016             
16017         }
16018         
16019         this.store.data.each(function(d, rowIndex){
16020             
16021             var html = '';
16022             
16023             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16024                 html = d.data[this.displayField];
16025             }
16026             
16027             var value = '';
16028             
16029             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16030                 value = d.data[this.valueField];
16031             }
16032             
16033             var option = {
16034                 tag: 'option',
16035                 value : value,
16036                 html : html
16037             };
16038             
16039             if(this.value == d.data[this.valueField]){
16040                 option['selected'] = true;
16041             }
16042             
16043             var opt = this.inputEl().createChild(option);
16044             
16045             this.ios_options.push({
16046                 data : d.data,
16047                 el : opt
16048             });
16049             
16050         }, this);
16051         
16052         this.inputEl().on('change', function(){
16053            this.fireEvent('select', this);
16054         }, this);
16055         
16056     },
16057     
16058     clearIOSView: function()
16059     {
16060         this.inputEl().dom.innerHTML = '';
16061         
16062         this.ios_options = [];
16063     },
16064     
16065     setIOSValue: function(v)
16066     {
16067         this.value = v;
16068         
16069         if(!this.ios_options){
16070             return;
16071         }
16072         
16073         Roo.each(this.ios_options, function(opts){
16074            
16075            opts.el.dom.removeAttribute('selected');
16076            
16077            if(opts.data[this.valueField] != v){
16078                return;
16079            }
16080            
16081            opts.el.dom.setAttribute('selected', true);
16082            
16083         }, this);
16084     }
16085
16086     /** 
16087     * @cfg {Boolean} grow 
16088     * @hide 
16089     */
16090     /** 
16091     * @cfg {Number} growMin 
16092     * @hide 
16093     */
16094     /** 
16095     * @cfg {Number} growMax 
16096     * @hide 
16097     */
16098     /**
16099      * @hide
16100      * @method autoSize
16101      */
16102 });
16103
16104 Roo.apply(Roo.bootstrap.ComboBox,  {
16105     
16106     header : {
16107         tag: 'div',
16108         cls: 'modal-header',
16109         cn: [
16110             {
16111                 tag: 'h4',
16112                 cls: 'modal-title'
16113             }
16114         ]
16115     },
16116     
16117     body : {
16118         tag: 'div',
16119         cls: 'modal-body',
16120         cn: [
16121             {
16122                 tag: 'ul',
16123                 cls: 'list-group'
16124             }
16125         ]
16126     },
16127     
16128     listItemRadio : {
16129         tag: 'li',
16130         cls: 'list-group-item',
16131         cn: [
16132             {
16133                 tag: 'span',
16134                 cls: 'roo-combobox-list-group-item-value'
16135             },
16136             {
16137                 tag: 'div',
16138                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16139                 cn: [
16140                     {
16141                         tag: 'input',
16142                         type: 'radio'
16143                     },
16144                     {
16145                         tag: 'label'
16146                     }
16147                 ]
16148             }
16149         ]
16150     },
16151     
16152     listItemCheckbox : {
16153         tag: 'li',
16154         cls: 'list-group-item',
16155         cn: [
16156             {
16157                 tag: 'span',
16158                 cls: 'roo-combobox-list-group-item-value'
16159             },
16160             {
16161                 tag: 'div',
16162                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16163                 cn: [
16164                     {
16165                         tag: 'input',
16166                         type: 'checkbox'
16167                     },
16168                     {
16169                         tag: 'label'
16170                     }
16171                 ]
16172             }
16173         ]
16174     },
16175     
16176     emptyResult : {
16177         tag: 'div',
16178         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16179     },
16180     
16181     footer : {
16182         tag: 'div',
16183         cls: 'modal-footer',
16184         cn: [
16185             {
16186                 tag: 'div',
16187                 cls: 'row',
16188                 cn: [
16189                     {
16190                         tag: 'div',
16191                         cls: 'col-xs-6 text-left',
16192                         cn: {
16193                             tag: 'button',
16194                             cls: 'btn btn-danger roo-touch-view-cancel',
16195                             html: 'Cancel'
16196                         }
16197                     },
16198                     {
16199                         tag: 'div',
16200                         cls: 'col-xs-6 text-right',
16201                         cn: {
16202                             tag: 'button',
16203                             cls: 'btn btn-success roo-touch-view-ok',
16204                             html: 'OK'
16205                         }
16206                     }
16207                 ]
16208             }
16209         ]
16210         
16211     }
16212 });
16213
16214 Roo.apply(Roo.bootstrap.ComboBox,  {
16215     
16216     touchViewTemplate : {
16217         tag: 'div',
16218         cls: 'modal fade roo-combobox-touch-view',
16219         cn: [
16220             {
16221                 tag: 'div',
16222                 cls: 'modal-dialog',
16223                 style : 'position:fixed', // we have to fix position....
16224                 cn: [
16225                     {
16226                         tag: 'div',
16227                         cls: 'modal-content',
16228                         cn: [
16229                             Roo.bootstrap.ComboBox.header,
16230                             Roo.bootstrap.ComboBox.body,
16231                             Roo.bootstrap.ComboBox.footer
16232                         ]
16233                     }
16234                 ]
16235             }
16236         ]
16237     }
16238 });/*
16239  * Based on:
16240  * Ext JS Library 1.1.1
16241  * Copyright(c) 2006-2007, Ext JS, LLC.
16242  *
16243  * Originally Released Under LGPL - original licence link has changed is not relivant.
16244  *
16245  * Fork - LGPL
16246  * <script type="text/javascript">
16247  */
16248
16249 /**
16250  * @class Roo.View
16251  * @extends Roo.util.Observable
16252  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16253  * This class also supports single and multi selection modes. <br>
16254  * Create a data model bound view:
16255  <pre><code>
16256  var store = new Roo.data.Store(...);
16257
16258  var view = new Roo.View({
16259     el : "my-element",
16260     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16261  
16262     singleSelect: true,
16263     selectedClass: "ydataview-selected",
16264     store: store
16265  });
16266
16267  // listen for node click?
16268  view.on("click", function(vw, index, node, e){
16269  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16270  });
16271
16272  // load XML data
16273  dataModel.load("foobar.xml");
16274  </code></pre>
16275  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16276  * <br><br>
16277  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16278  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16279  * 
16280  * Note: old style constructor is still suported (container, template, config)
16281  * 
16282  * @constructor
16283  * Create a new View
16284  * @param {Object} config The config object
16285  * 
16286  */
16287 Roo.View = function(config, depreciated_tpl, depreciated_config){
16288     
16289     this.parent = false;
16290     
16291     if (typeof(depreciated_tpl) == 'undefined') {
16292         // new way.. - universal constructor.
16293         Roo.apply(this, config);
16294         this.el  = Roo.get(this.el);
16295     } else {
16296         // old format..
16297         this.el  = Roo.get(config);
16298         this.tpl = depreciated_tpl;
16299         Roo.apply(this, depreciated_config);
16300     }
16301     this.wrapEl  = this.el.wrap().wrap();
16302     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16303     
16304     
16305     if(typeof(this.tpl) == "string"){
16306         this.tpl = new Roo.Template(this.tpl);
16307     } else {
16308         // support xtype ctors..
16309         this.tpl = new Roo.factory(this.tpl, Roo);
16310     }
16311     
16312     
16313     this.tpl.compile();
16314     
16315     /** @private */
16316     this.addEvents({
16317         /**
16318          * @event beforeclick
16319          * Fires before a click is processed. Returns false to cancel the default action.
16320          * @param {Roo.View} this
16321          * @param {Number} index The index of the target node
16322          * @param {HTMLElement} node The target node
16323          * @param {Roo.EventObject} e The raw event object
16324          */
16325             "beforeclick" : true,
16326         /**
16327          * @event click
16328          * Fires when a template node is clicked.
16329          * @param {Roo.View} this
16330          * @param {Number} index The index of the target node
16331          * @param {HTMLElement} node The target node
16332          * @param {Roo.EventObject} e The raw event object
16333          */
16334             "click" : true,
16335         /**
16336          * @event dblclick
16337          * Fires when a template node is double clicked.
16338          * @param {Roo.View} this
16339          * @param {Number} index The index of the target node
16340          * @param {HTMLElement} node The target node
16341          * @param {Roo.EventObject} e The raw event object
16342          */
16343             "dblclick" : true,
16344         /**
16345          * @event contextmenu
16346          * Fires when a template node is right clicked.
16347          * @param {Roo.View} this
16348          * @param {Number} index The index of the target node
16349          * @param {HTMLElement} node The target node
16350          * @param {Roo.EventObject} e The raw event object
16351          */
16352             "contextmenu" : true,
16353         /**
16354          * @event selectionchange
16355          * Fires when the selected nodes change.
16356          * @param {Roo.View} this
16357          * @param {Array} selections Array of the selected nodes
16358          */
16359             "selectionchange" : true,
16360     
16361         /**
16362          * @event beforeselect
16363          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16364          * @param {Roo.View} this
16365          * @param {HTMLElement} node The node to be selected
16366          * @param {Array} selections Array of currently selected nodes
16367          */
16368             "beforeselect" : true,
16369         /**
16370          * @event preparedata
16371          * Fires on every row to render, to allow you to change the data.
16372          * @param {Roo.View} this
16373          * @param {Object} data to be rendered (change this)
16374          */
16375           "preparedata" : true
16376           
16377           
16378         });
16379
16380
16381
16382     this.el.on({
16383         "click": this.onClick,
16384         "dblclick": this.onDblClick,
16385         "contextmenu": this.onContextMenu,
16386         scope:this
16387     });
16388
16389     this.selections = [];
16390     this.nodes = [];
16391     this.cmp = new Roo.CompositeElementLite([]);
16392     if(this.store){
16393         this.store = Roo.factory(this.store, Roo.data);
16394         this.setStore(this.store, true);
16395     }
16396     
16397     if ( this.footer && this.footer.xtype) {
16398            
16399          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16400         
16401         this.footer.dataSource = this.store;
16402         this.footer.container = fctr;
16403         this.footer = Roo.factory(this.footer, Roo);
16404         fctr.insertFirst(this.el);
16405         
16406         // this is a bit insane - as the paging toolbar seems to detach the el..
16407 //        dom.parentNode.parentNode.parentNode
16408          // they get detached?
16409     }
16410     
16411     
16412     Roo.View.superclass.constructor.call(this);
16413     
16414     
16415 };
16416
16417 Roo.extend(Roo.View, Roo.util.Observable, {
16418     
16419      /**
16420      * @cfg {Roo.data.Store} store Data store to load data from.
16421      */
16422     store : false,
16423     
16424     /**
16425      * @cfg {String|Roo.Element} el The container element.
16426      */
16427     el : '',
16428     
16429     /**
16430      * @cfg {String|Roo.Template} tpl The template used by this View 
16431      */
16432     tpl : false,
16433     /**
16434      * @cfg {String} dataName the named area of the template to use as the data area
16435      *                          Works with domtemplates roo-name="name"
16436      */
16437     dataName: false,
16438     /**
16439      * @cfg {String} selectedClass The css class to add to selected nodes
16440      */
16441     selectedClass : "x-view-selected",
16442      /**
16443      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16444      */
16445     emptyText : "",
16446     
16447     /**
16448      * @cfg {String} text to display on mask (default Loading)
16449      */
16450     mask : false,
16451     /**
16452      * @cfg {Boolean} multiSelect Allow multiple selection
16453      */
16454     multiSelect : false,
16455     /**
16456      * @cfg {Boolean} singleSelect Allow single selection
16457      */
16458     singleSelect:  false,
16459     
16460     /**
16461      * @cfg {Boolean} toggleSelect - selecting 
16462      */
16463     toggleSelect : false,
16464     
16465     /**
16466      * @cfg {Boolean} tickable - selecting 
16467      */
16468     tickable : false,
16469     
16470     /**
16471      * Returns the element this view is bound to.
16472      * @return {Roo.Element}
16473      */
16474     getEl : function(){
16475         return this.wrapEl;
16476     },
16477     
16478     
16479
16480     /**
16481      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16482      */
16483     refresh : function(){
16484         //Roo.log('refresh');
16485         var t = this.tpl;
16486         
16487         // if we are using something like 'domtemplate', then
16488         // the what gets used is:
16489         // t.applySubtemplate(NAME, data, wrapping data..)
16490         // the outer template then get' applied with
16491         //     the store 'extra data'
16492         // and the body get's added to the
16493         //      roo-name="data" node?
16494         //      <span class='roo-tpl-{name}'></span> ?????
16495         
16496         
16497         
16498         this.clearSelections();
16499         this.el.update("");
16500         var html = [];
16501         var records = this.store.getRange();
16502         if(records.length < 1) {
16503             
16504             // is this valid??  = should it render a template??
16505             
16506             this.el.update(this.emptyText);
16507             return;
16508         }
16509         var el = this.el;
16510         if (this.dataName) {
16511             this.el.update(t.apply(this.store.meta)); //????
16512             el = this.el.child('.roo-tpl-' + this.dataName);
16513         }
16514         
16515         for(var i = 0, len = records.length; i < len; i++){
16516             var data = this.prepareData(records[i].data, i, records[i]);
16517             this.fireEvent("preparedata", this, data, i, records[i]);
16518             
16519             var d = Roo.apply({}, data);
16520             
16521             if(this.tickable){
16522                 Roo.apply(d, {'roo-id' : Roo.id()});
16523                 
16524                 var _this = this;
16525             
16526                 Roo.each(this.parent.item, function(item){
16527                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16528                         return;
16529                     }
16530                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16531                 });
16532             }
16533             
16534             html[html.length] = Roo.util.Format.trim(
16535                 this.dataName ?
16536                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16537                     t.apply(d)
16538             );
16539         }
16540         
16541         
16542         
16543         el.update(html.join(""));
16544         this.nodes = el.dom.childNodes;
16545         this.updateIndexes(0);
16546     },
16547     
16548
16549     /**
16550      * Function to override to reformat the data that is sent to
16551      * the template for each node.
16552      * DEPRICATED - use the preparedata event handler.
16553      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16554      * a JSON object for an UpdateManager bound view).
16555      */
16556     prepareData : function(data, index, record)
16557     {
16558         this.fireEvent("preparedata", this, data, index, record);
16559         return data;
16560     },
16561
16562     onUpdate : function(ds, record){
16563         // Roo.log('on update');   
16564         this.clearSelections();
16565         var index = this.store.indexOf(record);
16566         var n = this.nodes[index];
16567         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16568         n.parentNode.removeChild(n);
16569         this.updateIndexes(index, index);
16570     },
16571
16572     
16573     
16574 // --------- FIXME     
16575     onAdd : function(ds, records, index)
16576     {
16577         //Roo.log(['on Add', ds, records, index] );        
16578         this.clearSelections();
16579         if(this.nodes.length == 0){
16580             this.refresh();
16581             return;
16582         }
16583         var n = this.nodes[index];
16584         for(var i = 0, len = records.length; i < len; i++){
16585             var d = this.prepareData(records[i].data, i, records[i]);
16586             if(n){
16587                 this.tpl.insertBefore(n, d);
16588             }else{
16589                 
16590                 this.tpl.append(this.el, d);
16591             }
16592         }
16593         this.updateIndexes(index);
16594     },
16595
16596     onRemove : function(ds, record, index){
16597        // Roo.log('onRemove');
16598         this.clearSelections();
16599         var el = this.dataName  ?
16600             this.el.child('.roo-tpl-' + this.dataName) :
16601             this.el; 
16602         
16603         el.dom.removeChild(this.nodes[index]);
16604         this.updateIndexes(index);
16605     },
16606
16607     /**
16608      * Refresh an individual node.
16609      * @param {Number} index
16610      */
16611     refreshNode : function(index){
16612         this.onUpdate(this.store, this.store.getAt(index));
16613     },
16614
16615     updateIndexes : function(startIndex, endIndex){
16616         var ns = this.nodes;
16617         startIndex = startIndex || 0;
16618         endIndex = endIndex || ns.length - 1;
16619         for(var i = startIndex; i <= endIndex; i++){
16620             ns[i].nodeIndex = i;
16621         }
16622     },
16623
16624     /**
16625      * Changes the data store this view uses and refresh the view.
16626      * @param {Store} store
16627      */
16628     setStore : function(store, initial){
16629         if(!initial && this.store){
16630             this.store.un("datachanged", this.refresh);
16631             this.store.un("add", this.onAdd);
16632             this.store.un("remove", this.onRemove);
16633             this.store.un("update", this.onUpdate);
16634             this.store.un("clear", this.refresh);
16635             this.store.un("beforeload", this.onBeforeLoad);
16636             this.store.un("load", this.onLoad);
16637             this.store.un("loadexception", this.onLoad);
16638         }
16639         if(store){
16640           
16641             store.on("datachanged", this.refresh, this);
16642             store.on("add", this.onAdd, this);
16643             store.on("remove", this.onRemove, this);
16644             store.on("update", this.onUpdate, this);
16645             store.on("clear", this.refresh, this);
16646             store.on("beforeload", this.onBeforeLoad, this);
16647             store.on("load", this.onLoad, this);
16648             store.on("loadexception", this.onLoad, this);
16649         }
16650         
16651         if(store){
16652             this.refresh();
16653         }
16654     },
16655     /**
16656      * onbeforeLoad - masks the loading area.
16657      *
16658      */
16659     onBeforeLoad : function(store,opts)
16660     {
16661          //Roo.log('onBeforeLoad');   
16662         if (!opts.add) {
16663             this.el.update("");
16664         }
16665         this.el.mask(this.mask ? this.mask : "Loading" ); 
16666     },
16667     onLoad : function ()
16668     {
16669         this.el.unmask();
16670     },
16671     
16672
16673     /**
16674      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16675      * @param {HTMLElement} node
16676      * @return {HTMLElement} The template node
16677      */
16678     findItemFromChild : function(node){
16679         var el = this.dataName  ?
16680             this.el.child('.roo-tpl-' + this.dataName,true) :
16681             this.el.dom; 
16682         
16683         if(!node || node.parentNode == el){
16684                     return node;
16685             }
16686             var p = node.parentNode;
16687             while(p && p != el){
16688             if(p.parentNode == el){
16689                 return p;
16690             }
16691             p = p.parentNode;
16692         }
16693             return null;
16694     },
16695
16696     /** @ignore */
16697     onClick : function(e){
16698         var item = this.findItemFromChild(e.getTarget());
16699         if(item){
16700             var index = this.indexOf(item);
16701             if(this.onItemClick(item, index, e) !== false){
16702                 this.fireEvent("click", this, index, item, e);
16703             }
16704         }else{
16705             this.clearSelections();
16706         }
16707     },
16708
16709     /** @ignore */
16710     onContextMenu : function(e){
16711         var item = this.findItemFromChild(e.getTarget());
16712         if(item){
16713             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16714         }
16715     },
16716
16717     /** @ignore */
16718     onDblClick : function(e){
16719         var item = this.findItemFromChild(e.getTarget());
16720         if(item){
16721             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16722         }
16723     },
16724
16725     onItemClick : function(item, index, e)
16726     {
16727         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16728             return false;
16729         }
16730         if (this.toggleSelect) {
16731             var m = this.isSelected(item) ? 'unselect' : 'select';
16732             //Roo.log(m);
16733             var _t = this;
16734             _t[m](item, true, false);
16735             return true;
16736         }
16737         if(this.multiSelect || this.singleSelect){
16738             if(this.multiSelect && e.shiftKey && this.lastSelection){
16739                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16740             }else{
16741                 this.select(item, this.multiSelect && e.ctrlKey);
16742                 this.lastSelection = item;
16743             }
16744             
16745             if(!this.tickable){
16746                 e.preventDefault();
16747             }
16748             
16749         }
16750         return true;
16751     },
16752
16753     /**
16754      * Get the number of selected nodes.
16755      * @return {Number}
16756      */
16757     getSelectionCount : function(){
16758         return this.selections.length;
16759     },
16760
16761     /**
16762      * Get the currently selected nodes.
16763      * @return {Array} An array of HTMLElements
16764      */
16765     getSelectedNodes : function(){
16766         return this.selections;
16767     },
16768
16769     /**
16770      * Get the indexes of the selected nodes.
16771      * @return {Array}
16772      */
16773     getSelectedIndexes : function(){
16774         var indexes = [], s = this.selections;
16775         for(var i = 0, len = s.length; i < len; i++){
16776             indexes.push(s[i].nodeIndex);
16777         }
16778         return indexes;
16779     },
16780
16781     /**
16782      * Clear all selections
16783      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16784      */
16785     clearSelections : function(suppressEvent){
16786         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16787             this.cmp.elements = this.selections;
16788             this.cmp.removeClass(this.selectedClass);
16789             this.selections = [];
16790             if(!suppressEvent){
16791                 this.fireEvent("selectionchange", this, this.selections);
16792             }
16793         }
16794     },
16795
16796     /**
16797      * Returns true if the passed node is selected
16798      * @param {HTMLElement/Number} node The node or node index
16799      * @return {Boolean}
16800      */
16801     isSelected : function(node){
16802         var s = this.selections;
16803         if(s.length < 1){
16804             return false;
16805         }
16806         node = this.getNode(node);
16807         return s.indexOf(node) !== -1;
16808     },
16809
16810     /**
16811      * Selects nodes.
16812      * @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
16813      * @param {Boolean} keepExisting (optional) true to keep existing selections
16814      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16815      */
16816     select : function(nodeInfo, keepExisting, suppressEvent){
16817         if(nodeInfo instanceof Array){
16818             if(!keepExisting){
16819                 this.clearSelections(true);
16820             }
16821             for(var i = 0, len = nodeInfo.length; i < len; i++){
16822                 this.select(nodeInfo[i], true, true);
16823             }
16824             return;
16825         } 
16826         var node = this.getNode(nodeInfo);
16827         if(!node || this.isSelected(node)){
16828             return; // already selected.
16829         }
16830         if(!keepExisting){
16831             this.clearSelections(true);
16832         }
16833         
16834         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16835             Roo.fly(node).addClass(this.selectedClass);
16836             this.selections.push(node);
16837             if(!suppressEvent){
16838                 this.fireEvent("selectionchange", this, this.selections);
16839             }
16840         }
16841         
16842         
16843     },
16844       /**
16845      * Unselects nodes.
16846      * @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
16847      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16848      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16849      */
16850     unselect : function(nodeInfo, keepExisting, suppressEvent)
16851     {
16852         if(nodeInfo instanceof Array){
16853             Roo.each(this.selections, function(s) {
16854                 this.unselect(s, nodeInfo);
16855             }, this);
16856             return;
16857         }
16858         var node = this.getNode(nodeInfo);
16859         if(!node || !this.isSelected(node)){
16860             //Roo.log("not selected");
16861             return; // not selected.
16862         }
16863         // fireevent???
16864         var ns = [];
16865         Roo.each(this.selections, function(s) {
16866             if (s == node ) {
16867                 Roo.fly(node).removeClass(this.selectedClass);
16868
16869                 return;
16870             }
16871             ns.push(s);
16872         },this);
16873         
16874         this.selections= ns;
16875         this.fireEvent("selectionchange", this, this.selections);
16876     },
16877
16878     /**
16879      * Gets a template node.
16880      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16881      * @return {HTMLElement} The node or null if it wasn't found
16882      */
16883     getNode : function(nodeInfo){
16884         if(typeof nodeInfo == "string"){
16885             return document.getElementById(nodeInfo);
16886         }else if(typeof nodeInfo == "number"){
16887             return this.nodes[nodeInfo];
16888         }
16889         return nodeInfo;
16890     },
16891
16892     /**
16893      * Gets a range template nodes.
16894      * @param {Number} startIndex
16895      * @param {Number} endIndex
16896      * @return {Array} An array of nodes
16897      */
16898     getNodes : function(start, end){
16899         var ns = this.nodes;
16900         start = start || 0;
16901         end = typeof end == "undefined" ? ns.length - 1 : end;
16902         var nodes = [];
16903         if(start <= end){
16904             for(var i = start; i <= end; i++){
16905                 nodes.push(ns[i]);
16906             }
16907         } else{
16908             for(var i = start; i >= end; i--){
16909                 nodes.push(ns[i]);
16910             }
16911         }
16912         return nodes;
16913     },
16914
16915     /**
16916      * Finds the index of the passed node
16917      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16918      * @return {Number} The index of the node or -1
16919      */
16920     indexOf : function(node){
16921         node = this.getNode(node);
16922         if(typeof node.nodeIndex == "number"){
16923             return node.nodeIndex;
16924         }
16925         var ns = this.nodes;
16926         for(var i = 0, len = ns.length; i < len; i++){
16927             if(ns[i] == node){
16928                 return i;
16929             }
16930         }
16931         return -1;
16932     }
16933 });
16934 /*
16935  * - LGPL
16936  *
16937  * based on jquery fullcalendar
16938  * 
16939  */
16940
16941 Roo.bootstrap = Roo.bootstrap || {};
16942 /**
16943  * @class Roo.bootstrap.Calendar
16944  * @extends Roo.bootstrap.Component
16945  * Bootstrap Calendar class
16946  * @cfg {Boolean} loadMask (true|false) default false
16947  * @cfg {Object} header generate the user specific header of the calendar, default false
16948
16949  * @constructor
16950  * Create a new Container
16951  * @param {Object} config The config object
16952  */
16953
16954
16955
16956 Roo.bootstrap.Calendar = function(config){
16957     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16958      this.addEvents({
16959         /**
16960              * @event select
16961              * Fires when a date is selected
16962              * @param {DatePicker} this
16963              * @param {Date} date The selected date
16964              */
16965         'select': true,
16966         /**
16967              * @event monthchange
16968              * Fires when the displayed month changes 
16969              * @param {DatePicker} this
16970              * @param {Date} date The selected month
16971              */
16972         'monthchange': true,
16973         /**
16974              * @event evententer
16975              * Fires when mouse over an event
16976              * @param {Calendar} this
16977              * @param {event} Event
16978              */
16979         'evententer': true,
16980         /**
16981              * @event eventleave
16982              * Fires when the mouse leaves an
16983              * @param {Calendar} this
16984              * @param {event}
16985              */
16986         'eventleave': true,
16987         /**
16988              * @event eventclick
16989              * Fires when the mouse click an
16990              * @param {Calendar} this
16991              * @param {event}
16992              */
16993         'eventclick': true
16994         
16995     });
16996
16997 };
16998
16999 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
17000     
17001      /**
17002      * @cfg {Number} startDay
17003      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17004      */
17005     startDay : 0,
17006     
17007     loadMask : false,
17008     
17009     header : false,
17010       
17011     getAutoCreate : function(){
17012         
17013         
17014         var fc_button = function(name, corner, style, content ) {
17015             return Roo.apply({},{
17016                 tag : 'span',
17017                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
17018                          (corner.length ?
17019                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17020                             ''
17021                         ),
17022                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17023                 unselectable: 'on'
17024             });
17025         };
17026         
17027         var header = {};
17028         
17029         if(!this.header){
17030             header = {
17031                 tag : 'table',
17032                 cls : 'fc-header',
17033                 style : 'width:100%',
17034                 cn : [
17035                     {
17036                         tag: 'tr',
17037                         cn : [
17038                             {
17039                                 tag : 'td',
17040                                 cls : 'fc-header-left',
17041                                 cn : [
17042                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
17043                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
17044                                     { tag: 'span', cls: 'fc-header-space' },
17045                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
17046
17047
17048                                 ]
17049                             },
17050
17051                             {
17052                                 tag : 'td',
17053                                 cls : 'fc-header-center',
17054                                 cn : [
17055                                     {
17056                                         tag: 'span',
17057                                         cls: 'fc-header-title',
17058                                         cn : {
17059                                             tag: 'H2',
17060                                             html : 'month / year'
17061                                         }
17062                                     }
17063
17064                                 ]
17065                             },
17066                             {
17067                                 tag : 'td',
17068                                 cls : 'fc-header-right',
17069                                 cn : [
17070                               /*      fc_button('month', 'left', '', 'month' ),
17071                                     fc_button('week', '', '', 'week' ),
17072                                     fc_button('day', 'right', '', 'day' )
17073                                 */    
17074
17075                                 ]
17076                             }
17077
17078                         ]
17079                     }
17080                 ]
17081             };
17082         }
17083         
17084         header = this.header;
17085         
17086        
17087         var cal_heads = function() {
17088             var ret = [];
17089             // fixme - handle this.
17090             
17091             for (var i =0; i < Date.dayNames.length; i++) {
17092                 var d = Date.dayNames[i];
17093                 ret.push({
17094                     tag: 'th',
17095                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17096                     html : d.substring(0,3)
17097                 });
17098                 
17099             }
17100             ret[0].cls += ' fc-first';
17101             ret[6].cls += ' fc-last';
17102             return ret;
17103         };
17104         var cal_cell = function(n) {
17105             return  {
17106                 tag: 'td',
17107                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17108                 cn : [
17109                     {
17110                         cn : [
17111                             {
17112                                 cls: 'fc-day-number',
17113                                 html: 'D'
17114                             },
17115                             {
17116                                 cls: 'fc-day-content',
17117                              
17118                                 cn : [
17119                                      {
17120                                         style: 'position: relative;' // height: 17px;
17121                                     }
17122                                 ]
17123                             }
17124                             
17125                             
17126                         ]
17127                     }
17128                 ]
17129                 
17130             }
17131         };
17132         var cal_rows = function() {
17133             
17134             var ret = [];
17135             for (var r = 0; r < 6; r++) {
17136                 var row= {
17137                     tag : 'tr',
17138                     cls : 'fc-week',
17139                     cn : []
17140                 };
17141                 
17142                 for (var i =0; i < Date.dayNames.length; i++) {
17143                     var d = Date.dayNames[i];
17144                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17145
17146                 }
17147                 row.cn[0].cls+=' fc-first';
17148                 row.cn[0].cn[0].style = 'min-height:90px';
17149                 row.cn[6].cls+=' fc-last';
17150                 ret.push(row);
17151                 
17152             }
17153             ret[0].cls += ' fc-first';
17154             ret[4].cls += ' fc-prev-last';
17155             ret[5].cls += ' fc-last';
17156             return ret;
17157             
17158         };
17159         
17160         var cal_table = {
17161             tag: 'table',
17162             cls: 'fc-border-separate',
17163             style : 'width:100%',
17164             cellspacing  : 0,
17165             cn : [
17166                 { 
17167                     tag: 'thead',
17168                     cn : [
17169                         { 
17170                             tag: 'tr',
17171                             cls : 'fc-first fc-last',
17172                             cn : cal_heads()
17173                         }
17174                     ]
17175                 },
17176                 { 
17177                     tag: 'tbody',
17178                     cn : cal_rows()
17179                 }
17180                   
17181             ]
17182         };
17183          
17184          var cfg = {
17185             cls : 'fc fc-ltr',
17186             cn : [
17187                 header,
17188                 {
17189                     cls : 'fc-content',
17190                     style : "position: relative;",
17191                     cn : [
17192                         {
17193                             cls : 'fc-view fc-view-month fc-grid',
17194                             style : 'position: relative',
17195                             unselectable : 'on',
17196                             cn : [
17197                                 {
17198                                     cls : 'fc-event-container',
17199                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17200                                 },
17201                                 cal_table
17202                             ]
17203                         }
17204                     ]
17205     
17206                 }
17207            ] 
17208             
17209         };
17210         
17211          
17212         
17213         return cfg;
17214     },
17215     
17216     
17217     initEvents : function()
17218     {
17219         if(!this.store){
17220             throw "can not find store for calendar";
17221         }
17222         
17223         var mark = {
17224             tag: "div",
17225             cls:"x-dlg-mask",
17226             style: "text-align:center",
17227             cn: [
17228                 {
17229                     tag: "div",
17230                     style: "background-color:white;width:50%;margin:250 auto",
17231                     cn: [
17232                         {
17233                             tag: "img",
17234                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17235                         },
17236                         {
17237                             tag: "span",
17238                             html: "Loading"
17239                         }
17240                         
17241                     ]
17242                 }
17243             ]
17244         };
17245         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17246         
17247         var size = this.el.select('.fc-content', true).first().getSize();
17248         this.maskEl.setSize(size.width, size.height);
17249         this.maskEl.enableDisplayMode("block");
17250         if(!this.loadMask){
17251             this.maskEl.hide();
17252         }
17253         
17254         this.store = Roo.factory(this.store, Roo.data);
17255         this.store.on('load', this.onLoad, this);
17256         this.store.on('beforeload', this.onBeforeLoad, this);
17257         
17258         this.resize();
17259         
17260         this.cells = this.el.select('.fc-day',true);
17261         //Roo.log(this.cells);
17262         this.textNodes = this.el.query('.fc-day-number');
17263         this.cells.addClassOnOver('fc-state-hover');
17264         
17265         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17266         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17267         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17268         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17269         
17270         this.on('monthchange', this.onMonthChange, this);
17271         
17272         this.update(new Date().clearTime());
17273     },
17274     
17275     resize : function() {
17276         var sz  = this.el.getSize();
17277         
17278         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17279         this.el.select('.fc-day-content div',true).setHeight(34);
17280     },
17281     
17282     
17283     // private
17284     showPrevMonth : function(e){
17285         this.update(this.activeDate.add("mo", -1));
17286     },
17287     showToday : function(e){
17288         this.update(new Date().clearTime());
17289     },
17290     // private
17291     showNextMonth : function(e){
17292         this.update(this.activeDate.add("mo", 1));
17293     },
17294
17295     // private
17296     showPrevYear : function(){
17297         this.update(this.activeDate.add("y", -1));
17298     },
17299
17300     // private
17301     showNextYear : function(){
17302         this.update(this.activeDate.add("y", 1));
17303     },
17304
17305     
17306    // private
17307     update : function(date)
17308     {
17309         var vd = this.activeDate;
17310         this.activeDate = date;
17311 //        if(vd && this.el){
17312 //            var t = date.getTime();
17313 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17314 //                Roo.log('using add remove');
17315 //                
17316 //                this.fireEvent('monthchange', this, date);
17317 //                
17318 //                this.cells.removeClass("fc-state-highlight");
17319 //                this.cells.each(function(c){
17320 //                   if(c.dateValue == t){
17321 //                       c.addClass("fc-state-highlight");
17322 //                       setTimeout(function(){
17323 //                            try{c.dom.firstChild.focus();}catch(e){}
17324 //                       }, 50);
17325 //                       return false;
17326 //                   }
17327 //                   return true;
17328 //                });
17329 //                return;
17330 //            }
17331 //        }
17332         
17333         var days = date.getDaysInMonth();
17334         
17335         var firstOfMonth = date.getFirstDateOfMonth();
17336         var startingPos = firstOfMonth.getDay()-this.startDay;
17337         
17338         if(startingPos < this.startDay){
17339             startingPos += 7;
17340         }
17341         
17342         var pm = date.add(Date.MONTH, -1);
17343         var prevStart = pm.getDaysInMonth()-startingPos;
17344 //        
17345         this.cells = this.el.select('.fc-day',true);
17346         this.textNodes = this.el.query('.fc-day-number');
17347         this.cells.addClassOnOver('fc-state-hover');
17348         
17349         var cells = this.cells.elements;
17350         var textEls = this.textNodes;
17351         
17352         Roo.each(cells, function(cell){
17353             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17354         });
17355         
17356         days += startingPos;
17357
17358         // convert everything to numbers so it's fast
17359         var day = 86400000;
17360         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17361         //Roo.log(d);
17362         //Roo.log(pm);
17363         //Roo.log(prevStart);
17364         
17365         var today = new Date().clearTime().getTime();
17366         var sel = date.clearTime().getTime();
17367         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17368         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17369         var ddMatch = this.disabledDatesRE;
17370         var ddText = this.disabledDatesText;
17371         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17372         var ddaysText = this.disabledDaysText;
17373         var format = this.format;
17374         
17375         var setCellClass = function(cal, cell){
17376             cell.row = 0;
17377             cell.events = [];
17378             cell.more = [];
17379             //Roo.log('set Cell Class');
17380             cell.title = "";
17381             var t = d.getTime();
17382             
17383             //Roo.log(d);
17384             
17385             cell.dateValue = t;
17386             if(t == today){
17387                 cell.className += " fc-today";
17388                 cell.className += " fc-state-highlight";
17389                 cell.title = cal.todayText;
17390             }
17391             if(t == sel){
17392                 // disable highlight in other month..
17393                 //cell.className += " fc-state-highlight";
17394                 
17395             }
17396             // disabling
17397             if(t < min) {
17398                 cell.className = " fc-state-disabled";
17399                 cell.title = cal.minText;
17400                 return;
17401             }
17402             if(t > max) {
17403                 cell.className = " fc-state-disabled";
17404                 cell.title = cal.maxText;
17405                 return;
17406             }
17407             if(ddays){
17408                 if(ddays.indexOf(d.getDay()) != -1){
17409                     cell.title = ddaysText;
17410                     cell.className = " fc-state-disabled";
17411                 }
17412             }
17413             if(ddMatch && format){
17414                 var fvalue = d.dateFormat(format);
17415                 if(ddMatch.test(fvalue)){
17416                     cell.title = ddText.replace("%0", fvalue);
17417                     cell.className = " fc-state-disabled";
17418                 }
17419             }
17420             
17421             if (!cell.initialClassName) {
17422                 cell.initialClassName = cell.dom.className;
17423             }
17424             
17425             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17426         };
17427
17428         var i = 0;
17429         
17430         for(; i < startingPos; i++) {
17431             textEls[i].innerHTML = (++prevStart);
17432             d.setDate(d.getDate()+1);
17433             
17434             cells[i].className = "fc-past fc-other-month";
17435             setCellClass(this, cells[i]);
17436         }
17437         
17438         var intDay = 0;
17439         
17440         for(; i < days; i++){
17441             intDay = i - startingPos + 1;
17442             textEls[i].innerHTML = (intDay);
17443             d.setDate(d.getDate()+1);
17444             
17445             cells[i].className = ''; // "x-date-active";
17446             setCellClass(this, cells[i]);
17447         }
17448         var extraDays = 0;
17449         
17450         for(; i < 42; i++) {
17451             textEls[i].innerHTML = (++extraDays);
17452             d.setDate(d.getDate()+1);
17453             
17454             cells[i].className = "fc-future fc-other-month";
17455             setCellClass(this, cells[i]);
17456         }
17457         
17458         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17459         
17460         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17461         
17462         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17463         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17464         
17465         if(totalRows != 6){
17466             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17467             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17468         }
17469         
17470         this.fireEvent('monthchange', this, date);
17471         
17472         
17473         /*
17474         if(!this.internalRender){
17475             var main = this.el.dom.firstChild;
17476             var w = main.offsetWidth;
17477             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17478             Roo.fly(main).setWidth(w);
17479             this.internalRender = true;
17480             // opera does not respect the auto grow header center column
17481             // then, after it gets a width opera refuses to recalculate
17482             // without a second pass
17483             if(Roo.isOpera && !this.secondPass){
17484                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17485                 this.secondPass = true;
17486                 this.update.defer(10, this, [date]);
17487             }
17488         }
17489         */
17490         
17491     },
17492     
17493     findCell : function(dt) {
17494         dt = dt.clearTime().getTime();
17495         var ret = false;
17496         this.cells.each(function(c){
17497             //Roo.log("check " +c.dateValue + '?=' + dt);
17498             if(c.dateValue == dt){
17499                 ret = c;
17500                 return false;
17501             }
17502             return true;
17503         });
17504         
17505         return ret;
17506     },
17507     
17508     findCells : function(ev) {
17509         var s = ev.start.clone().clearTime().getTime();
17510        // Roo.log(s);
17511         var e= ev.end.clone().clearTime().getTime();
17512        // Roo.log(e);
17513         var ret = [];
17514         this.cells.each(function(c){
17515              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17516             
17517             if(c.dateValue > e){
17518                 return ;
17519             }
17520             if(c.dateValue < s){
17521                 return ;
17522             }
17523             ret.push(c);
17524         });
17525         
17526         return ret;    
17527     },
17528     
17529 //    findBestRow: function(cells)
17530 //    {
17531 //        var ret = 0;
17532 //        
17533 //        for (var i =0 ; i < cells.length;i++) {
17534 //            ret  = Math.max(cells[i].rows || 0,ret);
17535 //        }
17536 //        return ret;
17537 //        
17538 //    },
17539     
17540     
17541     addItem : function(ev)
17542     {
17543         // look for vertical location slot in
17544         var cells = this.findCells(ev);
17545         
17546 //        ev.row = this.findBestRow(cells);
17547         
17548         // work out the location.
17549         
17550         var crow = false;
17551         var rows = [];
17552         for(var i =0; i < cells.length; i++) {
17553             
17554             cells[i].row = cells[0].row;
17555             
17556             if(i == 0){
17557                 cells[i].row = cells[i].row + 1;
17558             }
17559             
17560             if (!crow) {
17561                 crow = {
17562                     start : cells[i],
17563                     end :  cells[i]
17564                 };
17565                 continue;
17566             }
17567             if (crow.start.getY() == cells[i].getY()) {
17568                 // on same row.
17569                 crow.end = cells[i];
17570                 continue;
17571             }
17572             // different row.
17573             rows.push(crow);
17574             crow = {
17575                 start: cells[i],
17576                 end : cells[i]
17577             };
17578             
17579         }
17580         
17581         rows.push(crow);
17582         ev.els = [];
17583         ev.rows = rows;
17584         ev.cells = cells;
17585         
17586         cells[0].events.push(ev);
17587         
17588         this.calevents.push(ev);
17589     },
17590     
17591     clearEvents: function() {
17592         
17593         if(!this.calevents){
17594             return;
17595         }
17596         
17597         Roo.each(this.cells.elements, function(c){
17598             c.row = 0;
17599             c.events = [];
17600             c.more = [];
17601         });
17602         
17603         Roo.each(this.calevents, function(e) {
17604             Roo.each(e.els, function(el) {
17605                 el.un('mouseenter' ,this.onEventEnter, this);
17606                 el.un('mouseleave' ,this.onEventLeave, this);
17607                 el.remove();
17608             },this);
17609         },this);
17610         
17611         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17612             e.remove();
17613         });
17614         
17615     },
17616     
17617     renderEvents: function()
17618     {   
17619         var _this = this;
17620         
17621         this.cells.each(function(c) {
17622             
17623             if(c.row < 5){
17624                 return;
17625             }
17626             
17627             var ev = c.events;
17628             
17629             var r = 4;
17630             if(c.row != c.events.length){
17631                 r = 4 - (4 - (c.row - c.events.length));
17632             }
17633             
17634             c.events = ev.slice(0, r);
17635             c.more = ev.slice(r);
17636             
17637             if(c.more.length && c.more.length == 1){
17638                 c.events.push(c.more.pop());
17639             }
17640             
17641             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17642             
17643         });
17644             
17645         this.cells.each(function(c) {
17646             
17647             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17648             
17649             
17650             for (var e = 0; e < c.events.length; e++){
17651                 var ev = c.events[e];
17652                 var rows = ev.rows;
17653                 
17654                 for(var i = 0; i < rows.length; i++) {
17655                 
17656                     // how many rows should it span..
17657
17658                     var  cfg = {
17659                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17660                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17661
17662                         unselectable : "on",
17663                         cn : [
17664                             {
17665                                 cls: 'fc-event-inner',
17666                                 cn : [
17667     //                                {
17668     //                                  tag:'span',
17669     //                                  cls: 'fc-event-time',
17670     //                                  html : cells.length > 1 ? '' : ev.time
17671     //                                },
17672                                     {
17673                                       tag:'span',
17674                                       cls: 'fc-event-title',
17675                                       html : String.format('{0}', ev.title)
17676                                     }
17677
17678
17679                                 ]
17680                             },
17681                             {
17682                                 cls: 'ui-resizable-handle ui-resizable-e',
17683                                 html : '&nbsp;&nbsp;&nbsp'
17684                             }
17685
17686                         ]
17687                     };
17688
17689                     if (i == 0) {
17690                         cfg.cls += ' fc-event-start';
17691                     }
17692                     if ((i+1) == rows.length) {
17693                         cfg.cls += ' fc-event-end';
17694                     }
17695
17696                     var ctr = _this.el.select('.fc-event-container',true).first();
17697                     var cg = ctr.createChild(cfg);
17698
17699                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17700                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17701
17702                     var r = (c.more.length) ? 1 : 0;
17703                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17704                     cg.setWidth(ebox.right - sbox.x -2);
17705
17706                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17707                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17708                     cg.on('click', _this.onEventClick, _this, ev);
17709
17710                     ev.els.push(cg);
17711                     
17712                 }
17713                 
17714             }
17715             
17716             
17717             if(c.more.length){
17718                 var  cfg = {
17719                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17720                     style : 'position: absolute',
17721                     unselectable : "on",
17722                     cn : [
17723                         {
17724                             cls: 'fc-event-inner',
17725                             cn : [
17726                                 {
17727                                   tag:'span',
17728                                   cls: 'fc-event-title',
17729                                   html : 'More'
17730                                 }
17731
17732
17733                             ]
17734                         },
17735                         {
17736                             cls: 'ui-resizable-handle ui-resizable-e',
17737                             html : '&nbsp;&nbsp;&nbsp'
17738                         }
17739
17740                     ]
17741                 };
17742
17743                 var ctr = _this.el.select('.fc-event-container',true).first();
17744                 var cg = ctr.createChild(cfg);
17745
17746                 var sbox = c.select('.fc-day-content',true).first().getBox();
17747                 var ebox = c.select('.fc-day-content',true).first().getBox();
17748                 //Roo.log(cg);
17749                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17750                 cg.setWidth(ebox.right - sbox.x -2);
17751
17752                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17753                 
17754             }
17755             
17756         });
17757         
17758         
17759         
17760     },
17761     
17762     onEventEnter: function (e, el,event,d) {
17763         this.fireEvent('evententer', this, el, event);
17764     },
17765     
17766     onEventLeave: function (e, el,event,d) {
17767         this.fireEvent('eventleave', this, el, event);
17768     },
17769     
17770     onEventClick: function (e, el,event,d) {
17771         this.fireEvent('eventclick', this, el, event);
17772     },
17773     
17774     onMonthChange: function () {
17775         this.store.load();
17776     },
17777     
17778     onMoreEventClick: function(e, el, more)
17779     {
17780         var _this = this;
17781         
17782         this.calpopover.placement = 'right';
17783         this.calpopover.setTitle('More');
17784         
17785         this.calpopover.setContent('');
17786         
17787         var ctr = this.calpopover.el.select('.popover-content', true).first();
17788         
17789         Roo.each(more, function(m){
17790             var cfg = {
17791                 cls : 'fc-event-hori fc-event-draggable',
17792                 html : m.title
17793             };
17794             var cg = ctr.createChild(cfg);
17795             
17796             cg.on('click', _this.onEventClick, _this, m);
17797         });
17798         
17799         this.calpopover.show(el);
17800         
17801         
17802     },
17803     
17804     onLoad: function () 
17805     {   
17806         this.calevents = [];
17807         var cal = this;
17808         
17809         if(this.store.getCount() > 0){
17810             this.store.data.each(function(d){
17811                cal.addItem({
17812                     id : d.data.id,
17813                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17814                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17815                     time : d.data.start_time,
17816                     title : d.data.title,
17817                     description : d.data.description,
17818                     venue : d.data.venue
17819                 });
17820             });
17821         }
17822         
17823         this.renderEvents();
17824         
17825         if(this.calevents.length && this.loadMask){
17826             this.maskEl.hide();
17827         }
17828     },
17829     
17830     onBeforeLoad: function()
17831     {
17832         this.clearEvents();
17833         if(this.loadMask){
17834             this.maskEl.show();
17835         }
17836     }
17837 });
17838
17839  
17840  /*
17841  * - LGPL
17842  *
17843  * element
17844  * 
17845  */
17846
17847 /**
17848  * @class Roo.bootstrap.Popover
17849  * @extends Roo.bootstrap.Component
17850  * Bootstrap Popover class
17851  * @cfg {String} html contents of the popover   (or false to use children..)
17852  * @cfg {String} title of popover (or false to hide)
17853  * @cfg {String} placement how it is placed
17854  * @cfg {String} trigger click || hover (or false to trigger manually)
17855  * @cfg {String} over what (parent or false to trigger manually.)
17856  * @cfg {Number} delay - delay before showing
17857  
17858  * @constructor
17859  * Create a new Popover
17860  * @param {Object} config The config object
17861  */
17862
17863 Roo.bootstrap.Popover = function(config){
17864     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17865     
17866     this.addEvents({
17867         // raw events
17868          /**
17869          * @event show
17870          * After the popover show
17871          * 
17872          * @param {Roo.bootstrap.Popover} this
17873          */
17874         "show" : true,
17875         /**
17876          * @event hide
17877          * After the popover hide
17878          * 
17879          * @param {Roo.bootstrap.Popover} this
17880          */
17881         "hide" : true
17882     });
17883 };
17884
17885 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17886     
17887     title: 'Fill in a title',
17888     html: false,
17889     
17890     placement : 'right',
17891     trigger : 'hover', // hover
17892     
17893     delay : 0,
17894     
17895     over: 'parent',
17896     
17897     can_build_overlaid : false,
17898     
17899     getChildContainer : function()
17900     {
17901         return this.el.select('.popover-content',true).first();
17902     },
17903     
17904     getAutoCreate : function(){
17905          
17906         var cfg = {
17907            cls : 'popover roo-dynamic',
17908            style: 'display:block',
17909            cn : [
17910                 {
17911                     cls : 'arrow'
17912                 },
17913                 {
17914                     cls : 'popover-inner',
17915                     cn : [
17916                         {
17917                             tag: 'h3',
17918                             cls: 'popover-title popover-header',
17919                             html : this.title
17920                         },
17921                         {
17922                             cls : 'popover-content popover-body',
17923                             html : this.html
17924                         }
17925                     ]
17926                     
17927                 }
17928            ]
17929         };
17930         
17931         return cfg;
17932     },
17933     setTitle: function(str)
17934     {
17935         this.title = str;
17936         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17937     },
17938     setContent: function(str)
17939     {
17940         this.html = str;
17941         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17942     },
17943     // as it get's added to the bottom of the page.
17944     onRender : function(ct, position)
17945     {
17946         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17947         if(!this.el){
17948             var cfg = Roo.apply({},  this.getAutoCreate());
17949             cfg.id = Roo.id();
17950             
17951             if (this.cls) {
17952                 cfg.cls += ' ' + this.cls;
17953             }
17954             if (this.style) {
17955                 cfg.style = this.style;
17956             }
17957             //Roo.log("adding to ");
17958             this.el = Roo.get(document.body).createChild(cfg, position);
17959 //            Roo.log(this.el);
17960         }
17961         this.initEvents();
17962     },
17963     
17964     initEvents : function()
17965     {
17966         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17967         this.el.enableDisplayMode('block');
17968         this.el.hide();
17969         if (this.over === false) {
17970             return; 
17971         }
17972         if (this.triggers === false) {
17973             return;
17974         }
17975         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17976         var triggers = this.trigger ? this.trigger.split(' ') : [];
17977         Roo.each(triggers, function(trigger) {
17978         
17979             if (trigger == 'click') {
17980                 on_el.on('click', this.toggle, this);
17981             } else if (trigger != 'manual') {
17982                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17983                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17984       
17985                 on_el.on(eventIn  ,this.enter, this);
17986                 on_el.on(eventOut, this.leave, this);
17987             }
17988         }, this);
17989         
17990     },
17991     
17992     
17993     // private
17994     timeout : null,
17995     hoverState : null,
17996     
17997     toggle : function () {
17998         this.hoverState == 'in' ? this.leave() : this.enter();
17999     },
18000     
18001     enter : function () {
18002         
18003         clearTimeout(this.timeout);
18004     
18005         this.hoverState = 'in';
18006     
18007         if (!this.delay || !this.delay.show) {
18008             this.show();
18009             return;
18010         }
18011         var _t = this;
18012         this.timeout = setTimeout(function () {
18013             if (_t.hoverState == 'in') {
18014                 _t.show();
18015             }
18016         }, this.delay.show)
18017     },
18018     
18019     leave : function() {
18020         clearTimeout(this.timeout);
18021     
18022         this.hoverState = 'out';
18023     
18024         if (!this.delay || !this.delay.hide) {
18025             this.hide();
18026             return;
18027         }
18028         var _t = this;
18029         this.timeout = setTimeout(function () {
18030             if (_t.hoverState == 'out') {
18031                 _t.hide();
18032             }
18033         }, this.delay.hide)
18034     },
18035     
18036     show : function (on_el)
18037     {
18038         if (!on_el) {
18039             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18040         }
18041         
18042         // set content.
18043         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18044         if (this.html !== false) {
18045             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18046         }
18047         this.el.removeClass([
18048             'fade','top','bottom', 'left', 'right','in',
18049             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18050         ]);
18051         if (!this.title.length) {
18052             this.el.select('.popover-title',true).hide();
18053         }
18054         
18055         var placement = typeof this.placement == 'function' ?
18056             this.placement.call(this, this.el, on_el) :
18057             this.placement;
18058             
18059         var autoToken = /\s?auto?\s?/i;
18060         var autoPlace = autoToken.test(placement);
18061         if (autoPlace) {
18062             placement = placement.replace(autoToken, '') || 'top';
18063         }
18064         
18065         //this.el.detach()
18066         //this.el.setXY([0,0]);
18067         this.el.show();
18068         this.el.dom.style.display='block';
18069         this.el.addClass(placement);
18070         
18071         //this.el.appendTo(on_el);
18072         
18073         var p = this.getPosition();
18074         var box = this.el.getBox();
18075         
18076         if (autoPlace) {
18077             // fixme..
18078         }
18079         var align = Roo.bootstrap.Popover.alignment[placement];
18080         
18081 //        Roo.log(align);
18082         this.el.alignTo(on_el, align[0],align[1]);
18083         //var arrow = this.el.select('.arrow',true).first();
18084         //arrow.set(align[2], 
18085         
18086         this.el.addClass('in');
18087         
18088         
18089         if (this.el.hasClass('fade')) {
18090             // fade it?
18091         }
18092         
18093         this.hoverState = 'in';
18094         
18095         this.fireEvent('show', this);
18096         
18097     },
18098     hide : function()
18099     {
18100         this.el.setXY([0,0]);
18101         this.el.removeClass('in');
18102         this.el.hide();
18103         this.hoverState = null;
18104         
18105         this.fireEvent('hide', this);
18106     }
18107     
18108 });
18109
18110 Roo.bootstrap.Popover.alignment = {
18111     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18112     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18113     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18114     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18115 };
18116
18117  /*
18118  * - LGPL
18119  *
18120  * Progress
18121  * 
18122  */
18123
18124 /**
18125  * @class Roo.bootstrap.Progress
18126  * @extends Roo.bootstrap.Component
18127  * Bootstrap Progress class
18128  * @cfg {Boolean} striped striped of the progress bar
18129  * @cfg {Boolean} active animated of the progress bar
18130  * 
18131  * 
18132  * @constructor
18133  * Create a new Progress
18134  * @param {Object} config The config object
18135  */
18136
18137 Roo.bootstrap.Progress = function(config){
18138     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18139 };
18140
18141 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18142     
18143     striped : false,
18144     active: false,
18145     
18146     getAutoCreate : function(){
18147         var cfg = {
18148             tag: 'div',
18149             cls: 'progress'
18150         };
18151         
18152         
18153         if(this.striped){
18154             cfg.cls += ' progress-striped';
18155         }
18156       
18157         if(this.active){
18158             cfg.cls += ' active';
18159         }
18160         
18161         
18162         return cfg;
18163     }
18164    
18165 });
18166
18167  
18168
18169  /*
18170  * - LGPL
18171  *
18172  * ProgressBar
18173  * 
18174  */
18175
18176 /**
18177  * @class Roo.bootstrap.ProgressBar
18178  * @extends Roo.bootstrap.Component
18179  * Bootstrap ProgressBar class
18180  * @cfg {Number} aria_valuenow aria-value now
18181  * @cfg {Number} aria_valuemin aria-value min
18182  * @cfg {Number} aria_valuemax aria-value max
18183  * @cfg {String} label label for the progress bar
18184  * @cfg {String} panel (success | info | warning | danger )
18185  * @cfg {String} role role of the progress bar
18186  * @cfg {String} sr_only text
18187  * 
18188  * 
18189  * @constructor
18190  * Create a new ProgressBar
18191  * @param {Object} config The config object
18192  */
18193
18194 Roo.bootstrap.ProgressBar = function(config){
18195     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18196 };
18197
18198 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18199     
18200     aria_valuenow : 0,
18201     aria_valuemin : 0,
18202     aria_valuemax : 100,
18203     label : false,
18204     panel : false,
18205     role : false,
18206     sr_only: false,
18207     
18208     getAutoCreate : function()
18209     {
18210         
18211         var cfg = {
18212             tag: 'div',
18213             cls: 'progress-bar',
18214             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18215         };
18216         
18217         if(this.sr_only){
18218             cfg.cn = {
18219                 tag: 'span',
18220                 cls: 'sr-only',
18221                 html: this.sr_only
18222             }
18223         }
18224         
18225         if(this.role){
18226             cfg.role = this.role;
18227         }
18228         
18229         if(this.aria_valuenow){
18230             cfg['aria-valuenow'] = this.aria_valuenow;
18231         }
18232         
18233         if(this.aria_valuemin){
18234             cfg['aria-valuemin'] = this.aria_valuemin;
18235         }
18236         
18237         if(this.aria_valuemax){
18238             cfg['aria-valuemax'] = this.aria_valuemax;
18239         }
18240         
18241         if(this.label && !this.sr_only){
18242             cfg.html = this.label;
18243         }
18244         
18245         if(this.panel){
18246             cfg.cls += ' progress-bar-' + this.panel;
18247         }
18248         
18249         return cfg;
18250     },
18251     
18252     update : function(aria_valuenow)
18253     {
18254         this.aria_valuenow = aria_valuenow;
18255         
18256         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18257     }
18258    
18259 });
18260
18261  
18262
18263  /*
18264  * - LGPL
18265  *
18266  * column
18267  * 
18268  */
18269
18270 /**
18271  * @class Roo.bootstrap.TabGroup
18272  * @extends Roo.bootstrap.Column
18273  * Bootstrap Column class
18274  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18275  * @cfg {Boolean} carousel true to make the group behave like a carousel
18276  * @cfg {Boolean} bullets show bullets for the panels
18277  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18278  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18279  * @cfg {Boolean} showarrow (true|false) show arrow default true
18280  * 
18281  * @constructor
18282  * Create a new TabGroup
18283  * @param {Object} config The config object
18284  */
18285
18286 Roo.bootstrap.TabGroup = function(config){
18287     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18288     if (!this.navId) {
18289         this.navId = Roo.id();
18290     }
18291     this.tabs = [];
18292     Roo.bootstrap.TabGroup.register(this);
18293     
18294 };
18295
18296 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18297     
18298     carousel : false,
18299     transition : false,
18300     bullets : 0,
18301     timer : 0,
18302     autoslide : false,
18303     slideFn : false,
18304     slideOnTouch : false,
18305     showarrow : true,
18306     
18307     getAutoCreate : function()
18308     {
18309         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18310         
18311         cfg.cls += ' tab-content';
18312         
18313         if (this.carousel) {
18314             cfg.cls += ' carousel slide';
18315             
18316             cfg.cn = [{
18317                cls : 'carousel-inner',
18318                cn : []
18319             }];
18320         
18321             if(this.bullets  && !Roo.isTouch){
18322                 
18323                 var bullets = {
18324                     cls : 'carousel-bullets',
18325                     cn : []
18326                 };
18327                
18328                 if(this.bullets_cls){
18329                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18330                 }
18331                 
18332                 bullets.cn.push({
18333                     cls : 'clear'
18334                 });
18335                 
18336                 cfg.cn[0].cn.push(bullets);
18337             }
18338             
18339             if(this.showarrow){
18340                 cfg.cn[0].cn.push({
18341                     tag : 'div',
18342                     class : 'carousel-arrow',
18343                     cn : [
18344                         {
18345                             tag : 'div',
18346                             class : 'carousel-prev',
18347                             cn : [
18348                                 {
18349                                     tag : 'i',
18350                                     class : 'fa fa-chevron-left'
18351                                 }
18352                             ]
18353                         },
18354                         {
18355                             tag : 'div',
18356                             class : 'carousel-next',
18357                             cn : [
18358                                 {
18359                                     tag : 'i',
18360                                     class : 'fa fa-chevron-right'
18361                                 }
18362                             ]
18363                         }
18364                     ]
18365                 });
18366             }
18367             
18368         }
18369         
18370         return cfg;
18371     },
18372     
18373     initEvents:  function()
18374     {
18375 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18376 //            this.el.on("touchstart", this.onTouchStart, this);
18377 //        }
18378         
18379         if(this.autoslide){
18380             var _this = this;
18381             
18382             this.slideFn = window.setInterval(function() {
18383                 _this.showPanelNext();
18384             }, this.timer);
18385         }
18386         
18387         if(this.showarrow){
18388             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18389             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18390         }
18391         
18392         
18393     },
18394     
18395 //    onTouchStart : function(e, el, o)
18396 //    {
18397 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18398 //            return;
18399 //        }
18400 //        
18401 //        this.showPanelNext();
18402 //    },
18403     
18404     
18405     getChildContainer : function()
18406     {
18407         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18408     },
18409     
18410     /**
18411     * register a Navigation item
18412     * @param {Roo.bootstrap.NavItem} the navitem to add
18413     */
18414     register : function(item)
18415     {
18416         this.tabs.push( item);
18417         item.navId = this.navId; // not really needed..
18418         this.addBullet();
18419     
18420     },
18421     
18422     getActivePanel : function()
18423     {
18424         var r = false;
18425         Roo.each(this.tabs, function(t) {
18426             if (t.active) {
18427                 r = t;
18428                 return false;
18429             }
18430             return null;
18431         });
18432         return r;
18433         
18434     },
18435     getPanelByName : function(n)
18436     {
18437         var r = false;
18438         Roo.each(this.tabs, function(t) {
18439             if (t.tabId == n) {
18440                 r = t;
18441                 return false;
18442             }
18443             return null;
18444         });
18445         return r;
18446     },
18447     indexOfPanel : function(p)
18448     {
18449         var r = false;
18450         Roo.each(this.tabs, function(t,i) {
18451             if (t.tabId == p.tabId) {
18452                 r = i;
18453                 return false;
18454             }
18455             return null;
18456         });
18457         return r;
18458     },
18459     /**
18460      * show a specific panel
18461      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18462      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18463      */
18464     showPanel : function (pan)
18465     {
18466         if(this.transition || typeof(pan) == 'undefined'){
18467             Roo.log("waiting for the transitionend");
18468             return false;
18469         }
18470         
18471         if (typeof(pan) == 'number') {
18472             pan = this.tabs[pan];
18473         }
18474         
18475         if (typeof(pan) == 'string') {
18476             pan = this.getPanelByName(pan);
18477         }
18478         
18479         var cur = this.getActivePanel();
18480         
18481         if(!pan || !cur){
18482             Roo.log('pan or acitve pan is undefined');
18483             return false;
18484         }
18485         
18486         if (pan.tabId == this.getActivePanel().tabId) {
18487             return true;
18488         }
18489         
18490         if (false === cur.fireEvent('beforedeactivate')) {
18491             return false;
18492         }
18493         
18494         if(this.bullets > 0 && !Roo.isTouch){
18495             this.setActiveBullet(this.indexOfPanel(pan));
18496         }
18497         
18498         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18499             
18500             //class="carousel-item carousel-item-next carousel-item-left"
18501             
18502             this.transition = true;
18503             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18504             var lr = dir == 'next' ? 'left' : 'right';
18505             pan.el.addClass(dir); // or prev
18506             pan.el.addClass('carousel-item-' + dir); // or prev
18507             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18508             cur.el.addClass(lr); // or right
18509             pan.el.addClass(lr);
18510             cur.el.addClass('carousel-item-' +lr); // or right
18511             pan.el.addClass('carousel-item-' +lr);
18512             
18513             
18514             var _this = this;
18515             cur.el.on('transitionend', function() {
18516                 Roo.log("trans end?");
18517                 
18518                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18519                 pan.setActive(true);
18520                 
18521                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18522                 cur.setActive(false);
18523                 
18524                 _this.transition = false;
18525                 
18526             }, this, { single:  true } );
18527             
18528             return true;
18529         }
18530         
18531         cur.setActive(false);
18532         pan.setActive(true);
18533         
18534         return true;
18535         
18536     },
18537     showPanelNext : function()
18538     {
18539         var i = this.indexOfPanel(this.getActivePanel());
18540         
18541         if (i >= this.tabs.length - 1 && !this.autoslide) {
18542             return;
18543         }
18544         
18545         if (i >= this.tabs.length - 1 && this.autoslide) {
18546             i = -1;
18547         }
18548         
18549         this.showPanel(this.tabs[i+1]);
18550     },
18551     
18552     showPanelPrev : function()
18553     {
18554         var i = this.indexOfPanel(this.getActivePanel());
18555         
18556         if (i  < 1 && !this.autoslide) {
18557             return;
18558         }
18559         
18560         if (i < 1 && this.autoslide) {
18561             i = this.tabs.length;
18562         }
18563         
18564         this.showPanel(this.tabs[i-1]);
18565     },
18566     
18567     
18568     addBullet: function()
18569     {
18570         if(!this.bullets || Roo.isTouch){
18571             return;
18572         }
18573         var ctr = this.el.select('.carousel-bullets',true).first();
18574         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18575         var bullet = ctr.createChild({
18576             cls : 'bullet bullet-' + i
18577         },ctr.dom.lastChild);
18578         
18579         
18580         var _this = this;
18581         
18582         bullet.on('click', (function(e, el, o, ii, t){
18583
18584             e.preventDefault();
18585
18586             this.showPanel(ii);
18587
18588             if(this.autoslide && this.slideFn){
18589                 clearInterval(this.slideFn);
18590                 this.slideFn = window.setInterval(function() {
18591                     _this.showPanelNext();
18592                 }, this.timer);
18593             }
18594
18595         }).createDelegate(this, [i, bullet], true));
18596                 
18597         
18598     },
18599      
18600     setActiveBullet : function(i)
18601     {
18602         if(Roo.isTouch){
18603             return;
18604         }
18605         
18606         Roo.each(this.el.select('.bullet', true).elements, function(el){
18607             el.removeClass('selected');
18608         });
18609
18610         var bullet = this.el.select('.bullet-' + i, true).first();
18611         
18612         if(!bullet){
18613             return;
18614         }
18615         
18616         bullet.addClass('selected');
18617     }
18618     
18619     
18620   
18621 });
18622
18623  
18624
18625  
18626  
18627 Roo.apply(Roo.bootstrap.TabGroup, {
18628     
18629     groups: {},
18630      /**
18631     * register a Navigation Group
18632     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18633     */
18634     register : function(navgrp)
18635     {
18636         this.groups[navgrp.navId] = navgrp;
18637         
18638     },
18639     /**
18640     * fetch a Navigation Group based on the navigation ID
18641     * if one does not exist , it will get created.
18642     * @param {string} the navgroup to add
18643     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18644     */
18645     get: function(navId) {
18646         if (typeof(this.groups[navId]) == 'undefined') {
18647             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18648         }
18649         return this.groups[navId] ;
18650     }
18651     
18652     
18653     
18654 });
18655
18656  /*
18657  * - LGPL
18658  *
18659  * TabPanel
18660  * 
18661  */
18662
18663 /**
18664  * @class Roo.bootstrap.TabPanel
18665  * @extends Roo.bootstrap.Component
18666  * Bootstrap TabPanel class
18667  * @cfg {Boolean} active panel active
18668  * @cfg {String} html panel content
18669  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18670  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18671  * @cfg {String} href click to link..
18672  * 
18673  * 
18674  * @constructor
18675  * Create a new TabPanel
18676  * @param {Object} config The config object
18677  */
18678
18679 Roo.bootstrap.TabPanel = function(config){
18680     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18681     this.addEvents({
18682         /**
18683              * @event changed
18684              * Fires when the active status changes
18685              * @param {Roo.bootstrap.TabPanel} this
18686              * @param {Boolean} state the new state
18687             
18688          */
18689         'changed': true,
18690         /**
18691              * @event beforedeactivate
18692              * Fires before a tab is de-activated - can be used to do validation on a form.
18693              * @param {Roo.bootstrap.TabPanel} this
18694              * @return {Boolean} false if there is an error
18695             
18696          */
18697         'beforedeactivate': true
18698      });
18699     
18700     this.tabId = this.tabId || Roo.id();
18701   
18702 };
18703
18704 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18705     
18706     active: false,
18707     html: false,
18708     tabId: false,
18709     navId : false,
18710     href : '',
18711     
18712     getAutoCreate : function(){
18713         
18714         
18715         var cfg = {
18716             tag: 'div',
18717             // item is needed for carousel - not sure if it has any effect otherwise
18718             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18719             html: this.html || ''
18720         };
18721         
18722         if(this.active){
18723             cfg.cls += ' active';
18724         }
18725         
18726         if(this.tabId){
18727             cfg.tabId = this.tabId;
18728         }
18729         
18730         
18731         
18732         return cfg;
18733     },
18734     
18735     initEvents:  function()
18736     {
18737         var p = this.parent();
18738         
18739         this.navId = this.navId || p.navId;
18740         
18741         if (typeof(this.navId) != 'undefined') {
18742             // not really needed.. but just in case.. parent should be a NavGroup.
18743             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18744             
18745             tg.register(this);
18746             
18747             var i = tg.tabs.length - 1;
18748             
18749             if(this.active && tg.bullets > 0 && i < tg.bullets){
18750                 tg.setActiveBullet(i);
18751             }
18752         }
18753         
18754         this.el.on('click', this.onClick, this);
18755         
18756         if(Roo.isTouch){
18757             this.el.on("touchstart", this.onTouchStart, this);
18758             this.el.on("touchmove", this.onTouchMove, this);
18759             this.el.on("touchend", this.onTouchEnd, this);
18760         }
18761         
18762     },
18763     
18764     onRender : function(ct, position)
18765     {
18766         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18767     },
18768     
18769     setActive : function(state)
18770     {
18771         Roo.log("panel - set active " + this.tabId + "=" + state);
18772         
18773         this.active = state;
18774         if (!state) {
18775             this.el.removeClass('active');
18776             
18777         } else  if (!this.el.hasClass('active')) {
18778             this.el.addClass('active');
18779         }
18780         
18781         this.fireEvent('changed', this, state);
18782     },
18783     
18784     onClick : function(e)
18785     {
18786         e.preventDefault();
18787         
18788         if(!this.href.length){
18789             return;
18790         }
18791         
18792         window.location.href = this.href;
18793     },
18794     
18795     startX : 0,
18796     startY : 0,
18797     endX : 0,
18798     endY : 0,
18799     swiping : false,
18800     
18801     onTouchStart : function(e)
18802     {
18803         this.swiping = false;
18804         
18805         this.startX = e.browserEvent.touches[0].clientX;
18806         this.startY = e.browserEvent.touches[0].clientY;
18807     },
18808     
18809     onTouchMove : function(e)
18810     {
18811         this.swiping = true;
18812         
18813         this.endX = e.browserEvent.touches[0].clientX;
18814         this.endY = e.browserEvent.touches[0].clientY;
18815     },
18816     
18817     onTouchEnd : function(e)
18818     {
18819         if(!this.swiping){
18820             this.onClick(e);
18821             return;
18822         }
18823         
18824         var tabGroup = this.parent();
18825         
18826         if(this.endX > this.startX){ // swiping right
18827             tabGroup.showPanelPrev();
18828             return;
18829         }
18830         
18831         if(this.startX > this.endX){ // swiping left
18832             tabGroup.showPanelNext();
18833             return;
18834         }
18835     }
18836     
18837     
18838 });
18839  
18840
18841  
18842
18843  /*
18844  * - LGPL
18845  *
18846  * DateField
18847  * 
18848  */
18849
18850 /**
18851  * @class Roo.bootstrap.DateField
18852  * @extends Roo.bootstrap.Input
18853  * Bootstrap DateField class
18854  * @cfg {Number} weekStart default 0
18855  * @cfg {String} viewMode default empty, (months|years)
18856  * @cfg {String} minViewMode default empty, (months|years)
18857  * @cfg {Number} startDate default -Infinity
18858  * @cfg {Number} endDate default Infinity
18859  * @cfg {Boolean} todayHighlight default false
18860  * @cfg {Boolean} todayBtn default false
18861  * @cfg {Boolean} calendarWeeks default false
18862  * @cfg {Object} daysOfWeekDisabled default empty
18863  * @cfg {Boolean} singleMode default false (true | false)
18864  * 
18865  * @cfg {Boolean} keyboardNavigation default true
18866  * @cfg {String} language default en
18867  * 
18868  * @constructor
18869  * Create a new DateField
18870  * @param {Object} config The config object
18871  */
18872
18873 Roo.bootstrap.DateField = function(config){
18874     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18875      this.addEvents({
18876             /**
18877              * @event show
18878              * Fires when this field show.
18879              * @param {Roo.bootstrap.DateField} this
18880              * @param {Mixed} date The date value
18881              */
18882             show : true,
18883             /**
18884              * @event show
18885              * Fires when this field hide.
18886              * @param {Roo.bootstrap.DateField} this
18887              * @param {Mixed} date The date value
18888              */
18889             hide : true,
18890             /**
18891              * @event select
18892              * Fires when select a date.
18893              * @param {Roo.bootstrap.DateField} this
18894              * @param {Mixed} date The date value
18895              */
18896             select : true,
18897             /**
18898              * @event beforeselect
18899              * Fires when before select a date.
18900              * @param {Roo.bootstrap.DateField} this
18901              * @param {Mixed} date The date value
18902              */
18903             beforeselect : true
18904         });
18905 };
18906
18907 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18908     
18909     /**
18910      * @cfg {String} format
18911      * The default date format string which can be overriden for localization support.  The format must be
18912      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18913      */
18914     format : "m/d/y",
18915     /**
18916      * @cfg {String} altFormats
18917      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18918      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18919      */
18920     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18921     
18922     weekStart : 0,
18923     
18924     viewMode : '',
18925     
18926     minViewMode : '',
18927     
18928     todayHighlight : false,
18929     
18930     todayBtn: false,
18931     
18932     language: 'en',
18933     
18934     keyboardNavigation: true,
18935     
18936     calendarWeeks: false,
18937     
18938     startDate: -Infinity,
18939     
18940     endDate: Infinity,
18941     
18942     daysOfWeekDisabled: [],
18943     
18944     _events: [],
18945     
18946     singleMode : false,
18947     
18948     UTCDate: function()
18949     {
18950         return new Date(Date.UTC.apply(Date, arguments));
18951     },
18952     
18953     UTCToday: function()
18954     {
18955         var today = new Date();
18956         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18957     },
18958     
18959     getDate: function() {
18960             var d = this.getUTCDate();
18961             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18962     },
18963     
18964     getUTCDate: function() {
18965             return this.date;
18966     },
18967     
18968     setDate: function(d) {
18969             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18970     },
18971     
18972     setUTCDate: function(d) {
18973             this.date = d;
18974             this.setValue(this.formatDate(this.date));
18975     },
18976         
18977     onRender: function(ct, position)
18978     {
18979         
18980         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18981         
18982         this.language = this.language || 'en';
18983         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18984         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18985         
18986         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18987         this.format = this.format || 'm/d/y';
18988         this.isInline = false;
18989         this.isInput = true;
18990         this.component = this.el.select('.add-on', true).first() || false;
18991         this.component = (this.component && this.component.length === 0) ? false : this.component;
18992         this.hasInput = this.component && this.inputEl().length;
18993         
18994         if (typeof(this.minViewMode === 'string')) {
18995             switch (this.minViewMode) {
18996                 case 'months':
18997                     this.minViewMode = 1;
18998                     break;
18999                 case 'years':
19000                     this.minViewMode = 2;
19001                     break;
19002                 default:
19003                     this.minViewMode = 0;
19004                     break;
19005             }
19006         }
19007         
19008         if (typeof(this.viewMode === 'string')) {
19009             switch (this.viewMode) {
19010                 case 'months':
19011                     this.viewMode = 1;
19012                     break;
19013                 case 'years':
19014                     this.viewMode = 2;
19015                     break;
19016                 default:
19017                     this.viewMode = 0;
19018                     break;
19019             }
19020         }
19021                 
19022         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19023         
19024 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19025         
19026         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19027         
19028         this.picker().on('mousedown', this.onMousedown, this);
19029         this.picker().on('click', this.onClick, this);
19030         
19031         this.picker().addClass('datepicker-dropdown');
19032         
19033         this.startViewMode = this.viewMode;
19034         
19035         if(this.singleMode){
19036             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19037                 v.setVisibilityMode(Roo.Element.DISPLAY);
19038                 v.hide();
19039             });
19040             
19041             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19042                 v.setStyle('width', '189px');
19043             });
19044         }
19045         
19046         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19047             if(!this.calendarWeeks){
19048                 v.remove();
19049                 return;
19050             }
19051             
19052             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19053             v.attr('colspan', function(i, val){
19054                 return parseInt(val) + 1;
19055             });
19056         });
19057                         
19058         
19059         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19060         
19061         this.setStartDate(this.startDate);
19062         this.setEndDate(this.endDate);
19063         
19064         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19065         
19066         this.fillDow();
19067         this.fillMonths();
19068         this.update();
19069         this.showMode();
19070         
19071         if(this.isInline) {
19072             this.showPopup();
19073         }
19074     },
19075     
19076     picker : function()
19077     {
19078         return this.pickerEl;
19079 //        return this.el.select('.datepicker', true).first();
19080     },
19081     
19082     fillDow: function()
19083     {
19084         var dowCnt = this.weekStart;
19085         
19086         var dow = {
19087             tag: 'tr',
19088             cn: [
19089                 
19090             ]
19091         };
19092         
19093         if(this.calendarWeeks){
19094             dow.cn.push({
19095                 tag: 'th',
19096                 cls: 'cw',
19097                 html: '&nbsp;'
19098             })
19099         }
19100         
19101         while (dowCnt < this.weekStart + 7) {
19102             dow.cn.push({
19103                 tag: 'th',
19104                 cls: 'dow',
19105                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19106             });
19107         }
19108         
19109         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19110     },
19111     
19112     fillMonths: function()
19113     {    
19114         var i = 0;
19115         var months = this.picker().select('>.datepicker-months td', true).first();
19116         
19117         months.dom.innerHTML = '';
19118         
19119         while (i < 12) {
19120             var month = {
19121                 tag: 'span',
19122                 cls: 'month',
19123                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19124             };
19125             
19126             months.createChild(month);
19127         }
19128         
19129     },
19130     
19131     update: function()
19132     {
19133         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;
19134         
19135         if (this.date < this.startDate) {
19136             this.viewDate = new Date(this.startDate);
19137         } else if (this.date > this.endDate) {
19138             this.viewDate = new Date(this.endDate);
19139         } else {
19140             this.viewDate = new Date(this.date);
19141         }
19142         
19143         this.fill();
19144     },
19145     
19146     fill: function() 
19147     {
19148         var d = new Date(this.viewDate),
19149                 year = d.getUTCFullYear(),
19150                 month = d.getUTCMonth(),
19151                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19152                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19153                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19154                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19155                 currentDate = this.date && this.date.valueOf(),
19156                 today = this.UTCToday();
19157         
19158         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19159         
19160 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19161         
19162 //        this.picker.select('>tfoot th.today').
19163 //                                              .text(dates[this.language].today)
19164 //                                              .toggle(this.todayBtn !== false);
19165     
19166         this.updateNavArrows();
19167         this.fillMonths();
19168                                                 
19169         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19170         
19171         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19172          
19173         prevMonth.setUTCDate(day);
19174         
19175         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19176         
19177         var nextMonth = new Date(prevMonth);
19178         
19179         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19180         
19181         nextMonth = nextMonth.valueOf();
19182         
19183         var fillMonths = false;
19184         
19185         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19186         
19187         while(prevMonth.valueOf() <= nextMonth) {
19188             var clsName = '';
19189             
19190             if (prevMonth.getUTCDay() === this.weekStart) {
19191                 if(fillMonths){
19192                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19193                 }
19194                     
19195                 fillMonths = {
19196                     tag: 'tr',
19197                     cn: []
19198                 };
19199                 
19200                 if(this.calendarWeeks){
19201                     // ISO 8601: First week contains first thursday.
19202                     // ISO also states week starts on Monday, but we can be more abstract here.
19203                     var
19204                     // Start of current week: based on weekstart/current date
19205                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19206                     // Thursday of this week
19207                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19208                     // First Thursday of year, year from thursday
19209                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19210                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19211                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19212                     
19213                     fillMonths.cn.push({
19214                         tag: 'td',
19215                         cls: 'cw',
19216                         html: calWeek
19217                     });
19218                 }
19219             }
19220             
19221             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19222                 clsName += ' old';
19223             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19224                 clsName += ' new';
19225             }
19226             if (this.todayHighlight &&
19227                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19228                 prevMonth.getUTCMonth() == today.getMonth() &&
19229                 prevMonth.getUTCDate() == today.getDate()) {
19230                 clsName += ' today';
19231             }
19232             
19233             if (currentDate && prevMonth.valueOf() === currentDate) {
19234                 clsName += ' active';
19235             }
19236             
19237             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19238                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19239                     clsName += ' disabled';
19240             }
19241             
19242             fillMonths.cn.push({
19243                 tag: 'td',
19244                 cls: 'day ' + clsName,
19245                 html: prevMonth.getDate()
19246             });
19247             
19248             prevMonth.setDate(prevMonth.getDate()+1);
19249         }
19250           
19251         var currentYear = this.date && this.date.getUTCFullYear();
19252         var currentMonth = this.date && this.date.getUTCMonth();
19253         
19254         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19255         
19256         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19257             v.removeClass('active');
19258             
19259             if(currentYear === year && k === currentMonth){
19260                 v.addClass('active');
19261             }
19262             
19263             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19264                 v.addClass('disabled');
19265             }
19266             
19267         });
19268         
19269         
19270         year = parseInt(year/10, 10) * 10;
19271         
19272         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19273         
19274         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19275         
19276         year -= 1;
19277         for (var i = -1; i < 11; i++) {
19278             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19279                 tag: 'span',
19280                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19281                 html: year
19282             });
19283             
19284             year += 1;
19285         }
19286     },
19287     
19288     showMode: function(dir) 
19289     {
19290         if (dir) {
19291             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19292         }
19293         
19294         Roo.each(this.picker().select('>div',true).elements, function(v){
19295             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19296             v.hide();
19297         });
19298         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19299     },
19300     
19301     place: function()
19302     {
19303         if(this.isInline) {
19304             return;
19305         }
19306         
19307         this.picker().removeClass(['bottom', 'top']);
19308         
19309         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19310             /*
19311              * place to the top of element!
19312              *
19313              */
19314             
19315             this.picker().addClass('top');
19316             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19317             
19318             return;
19319         }
19320         
19321         this.picker().addClass('bottom');
19322         
19323         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19324     },
19325     
19326     parseDate : function(value)
19327     {
19328         if(!value || value instanceof Date){
19329             return value;
19330         }
19331         var v = Date.parseDate(value, this.format);
19332         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19333             v = Date.parseDate(value, 'Y-m-d');
19334         }
19335         if(!v && this.altFormats){
19336             if(!this.altFormatsArray){
19337                 this.altFormatsArray = this.altFormats.split("|");
19338             }
19339             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19340                 v = Date.parseDate(value, this.altFormatsArray[i]);
19341             }
19342         }
19343         return v;
19344     },
19345     
19346     formatDate : function(date, fmt)
19347     {   
19348         return (!date || !(date instanceof Date)) ?
19349         date : date.dateFormat(fmt || this.format);
19350     },
19351     
19352     onFocus : function()
19353     {
19354         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19355         this.showPopup();
19356     },
19357     
19358     onBlur : function()
19359     {
19360         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19361         
19362         var d = this.inputEl().getValue();
19363         
19364         this.setValue(d);
19365                 
19366         this.hidePopup();
19367     },
19368     
19369     showPopup : function()
19370     {
19371         this.picker().show();
19372         this.update();
19373         this.place();
19374         
19375         this.fireEvent('showpopup', this, this.date);
19376     },
19377     
19378     hidePopup : function()
19379     {
19380         if(this.isInline) {
19381             return;
19382         }
19383         this.picker().hide();
19384         this.viewMode = this.startViewMode;
19385         this.showMode();
19386         
19387         this.fireEvent('hidepopup', this, this.date);
19388         
19389     },
19390     
19391     onMousedown: function(e)
19392     {
19393         e.stopPropagation();
19394         e.preventDefault();
19395     },
19396     
19397     keyup: function(e)
19398     {
19399         Roo.bootstrap.DateField.superclass.keyup.call(this);
19400         this.update();
19401     },
19402
19403     setValue: function(v)
19404     {
19405         if(this.fireEvent('beforeselect', this, v) !== false){
19406             var d = new Date(this.parseDate(v) ).clearTime();
19407         
19408             if(isNaN(d.getTime())){
19409                 this.date = this.viewDate = '';
19410                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19411                 return;
19412             }
19413
19414             v = this.formatDate(d);
19415
19416             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19417
19418             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19419
19420             this.update();
19421
19422             this.fireEvent('select', this, this.date);
19423         }
19424     },
19425     
19426     getValue: function()
19427     {
19428         return this.formatDate(this.date);
19429     },
19430     
19431     fireKey: function(e)
19432     {
19433         if (!this.picker().isVisible()){
19434             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19435                 this.showPopup();
19436             }
19437             return;
19438         }
19439         
19440         var dateChanged = false,
19441         dir, day, month,
19442         newDate, newViewDate;
19443         
19444         switch(e.keyCode){
19445             case 27: // escape
19446                 this.hidePopup();
19447                 e.preventDefault();
19448                 break;
19449             case 37: // left
19450             case 39: // right
19451                 if (!this.keyboardNavigation) {
19452                     break;
19453                 }
19454                 dir = e.keyCode == 37 ? -1 : 1;
19455                 
19456                 if (e.ctrlKey){
19457                     newDate = this.moveYear(this.date, dir);
19458                     newViewDate = this.moveYear(this.viewDate, dir);
19459                 } else if (e.shiftKey){
19460                     newDate = this.moveMonth(this.date, dir);
19461                     newViewDate = this.moveMonth(this.viewDate, dir);
19462                 } else {
19463                     newDate = new Date(this.date);
19464                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19465                     newViewDate = new Date(this.viewDate);
19466                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19467                 }
19468                 if (this.dateWithinRange(newDate)){
19469                     this.date = newDate;
19470                     this.viewDate = newViewDate;
19471                     this.setValue(this.formatDate(this.date));
19472 //                    this.update();
19473                     e.preventDefault();
19474                     dateChanged = true;
19475                 }
19476                 break;
19477             case 38: // up
19478             case 40: // down
19479                 if (!this.keyboardNavigation) {
19480                     break;
19481                 }
19482                 dir = e.keyCode == 38 ? -1 : 1;
19483                 if (e.ctrlKey){
19484                     newDate = this.moveYear(this.date, dir);
19485                     newViewDate = this.moveYear(this.viewDate, dir);
19486                 } else if (e.shiftKey){
19487                     newDate = this.moveMonth(this.date, dir);
19488                     newViewDate = this.moveMonth(this.viewDate, dir);
19489                 } else {
19490                     newDate = new Date(this.date);
19491                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19492                     newViewDate = new Date(this.viewDate);
19493                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19494                 }
19495                 if (this.dateWithinRange(newDate)){
19496                     this.date = newDate;
19497                     this.viewDate = newViewDate;
19498                     this.setValue(this.formatDate(this.date));
19499 //                    this.update();
19500                     e.preventDefault();
19501                     dateChanged = true;
19502                 }
19503                 break;
19504             case 13: // enter
19505                 this.setValue(this.formatDate(this.date));
19506                 this.hidePopup();
19507                 e.preventDefault();
19508                 break;
19509             case 9: // tab
19510                 this.setValue(this.formatDate(this.date));
19511                 this.hidePopup();
19512                 break;
19513             case 16: // shift
19514             case 17: // ctrl
19515             case 18: // alt
19516                 break;
19517             default :
19518                 this.hidePopup();
19519                 
19520         }
19521     },
19522     
19523     
19524     onClick: function(e) 
19525     {
19526         e.stopPropagation();
19527         e.preventDefault();
19528         
19529         var target = e.getTarget();
19530         
19531         if(target.nodeName.toLowerCase() === 'i'){
19532             target = Roo.get(target).dom.parentNode;
19533         }
19534         
19535         var nodeName = target.nodeName;
19536         var className = target.className;
19537         var html = target.innerHTML;
19538         //Roo.log(nodeName);
19539         
19540         switch(nodeName.toLowerCase()) {
19541             case 'th':
19542                 switch(className) {
19543                     case 'switch':
19544                         this.showMode(1);
19545                         break;
19546                     case 'prev':
19547                     case 'next':
19548                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19549                         switch(this.viewMode){
19550                                 case 0:
19551                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19552                                         break;
19553                                 case 1:
19554                                 case 2:
19555                                         this.viewDate = this.moveYear(this.viewDate, dir);
19556                                         break;
19557                         }
19558                         this.fill();
19559                         break;
19560                     case 'today':
19561                         var date = new Date();
19562                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19563 //                        this.fill()
19564                         this.setValue(this.formatDate(this.date));
19565                         
19566                         this.hidePopup();
19567                         break;
19568                 }
19569                 break;
19570             case 'span':
19571                 if (className.indexOf('disabled') < 0) {
19572                     this.viewDate.setUTCDate(1);
19573                     if (className.indexOf('month') > -1) {
19574                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19575                     } else {
19576                         var year = parseInt(html, 10) || 0;
19577                         this.viewDate.setUTCFullYear(year);
19578                         
19579                     }
19580                     
19581                     if(this.singleMode){
19582                         this.setValue(this.formatDate(this.viewDate));
19583                         this.hidePopup();
19584                         return;
19585                     }
19586                     
19587                     this.showMode(-1);
19588                     this.fill();
19589                 }
19590                 break;
19591                 
19592             case 'td':
19593                 //Roo.log(className);
19594                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19595                     var day = parseInt(html, 10) || 1;
19596                     var year = this.viewDate.getUTCFullYear(),
19597                         month = this.viewDate.getUTCMonth();
19598
19599                     if (className.indexOf('old') > -1) {
19600                         if(month === 0 ){
19601                             month = 11;
19602                             year -= 1;
19603                         }else{
19604                             month -= 1;
19605                         }
19606                     } else if (className.indexOf('new') > -1) {
19607                         if (month == 11) {
19608                             month = 0;
19609                             year += 1;
19610                         } else {
19611                             month += 1;
19612                         }
19613                     }
19614                     //Roo.log([year,month,day]);
19615                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19616                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19617 //                    this.fill();
19618                     //Roo.log(this.formatDate(this.date));
19619                     this.setValue(this.formatDate(this.date));
19620                     this.hidePopup();
19621                 }
19622                 break;
19623         }
19624     },
19625     
19626     setStartDate: function(startDate)
19627     {
19628         this.startDate = startDate || -Infinity;
19629         if (this.startDate !== -Infinity) {
19630             this.startDate = this.parseDate(this.startDate);
19631         }
19632         this.update();
19633         this.updateNavArrows();
19634     },
19635
19636     setEndDate: function(endDate)
19637     {
19638         this.endDate = endDate || Infinity;
19639         if (this.endDate !== Infinity) {
19640             this.endDate = this.parseDate(this.endDate);
19641         }
19642         this.update();
19643         this.updateNavArrows();
19644     },
19645     
19646     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19647     {
19648         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19649         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19650             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19651         }
19652         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19653             return parseInt(d, 10);
19654         });
19655         this.update();
19656         this.updateNavArrows();
19657     },
19658     
19659     updateNavArrows: function() 
19660     {
19661         if(this.singleMode){
19662             return;
19663         }
19664         
19665         var d = new Date(this.viewDate),
19666         year = d.getUTCFullYear(),
19667         month = d.getUTCMonth();
19668         
19669         Roo.each(this.picker().select('.prev', true).elements, function(v){
19670             v.show();
19671             switch (this.viewMode) {
19672                 case 0:
19673
19674                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19675                         v.hide();
19676                     }
19677                     break;
19678                 case 1:
19679                 case 2:
19680                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19681                         v.hide();
19682                     }
19683                     break;
19684             }
19685         });
19686         
19687         Roo.each(this.picker().select('.next', true).elements, function(v){
19688             v.show();
19689             switch (this.viewMode) {
19690                 case 0:
19691
19692                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19693                         v.hide();
19694                     }
19695                     break;
19696                 case 1:
19697                 case 2:
19698                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19699                         v.hide();
19700                     }
19701                     break;
19702             }
19703         })
19704     },
19705     
19706     moveMonth: function(date, dir)
19707     {
19708         if (!dir) {
19709             return date;
19710         }
19711         var new_date = new Date(date.valueOf()),
19712         day = new_date.getUTCDate(),
19713         month = new_date.getUTCMonth(),
19714         mag = Math.abs(dir),
19715         new_month, test;
19716         dir = dir > 0 ? 1 : -1;
19717         if (mag == 1){
19718             test = dir == -1
19719             // If going back one month, make sure month is not current month
19720             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19721             ? function(){
19722                 return new_date.getUTCMonth() == month;
19723             }
19724             // If going forward one month, make sure month is as expected
19725             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19726             : function(){
19727                 return new_date.getUTCMonth() != new_month;
19728             };
19729             new_month = month + dir;
19730             new_date.setUTCMonth(new_month);
19731             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19732             if (new_month < 0 || new_month > 11) {
19733                 new_month = (new_month + 12) % 12;
19734             }
19735         } else {
19736             // For magnitudes >1, move one month at a time...
19737             for (var i=0; i<mag; i++) {
19738                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19739                 new_date = this.moveMonth(new_date, dir);
19740             }
19741             // ...then reset the day, keeping it in the new month
19742             new_month = new_date.getUTCMonth();
19743             new_date.setUTCDate(day);
19744             test = function(){
19745                 return new_month != new_date.getUTCMonth();
19746             };
19747         }
19748         // Common date-resetting loop -- if date is beyond end of month, make it
19749         // end of month
19750         while (test()){
19751             new_date.setUTCDate(--day);
19752             new_date.setUTCMonth(new_month);
19753         }
19754         return new_date;
19755     },
19756
19757     moveYear: function(date, dir)
19758     {
19759         return this.moveMonth(date, dir*12);
19760     },
19761
19762     dateWithinRange: function(date)
19763     {
19764         return date >= this.startDate && date <= this.endDate;
19765     },
19766
19767     
19768     remove: function() 
19769     {
19770         this.picker().remove();
19771     },
19772     
19773     validateValue : function(value)
19774     {
19775         if(this.getVisibilityEl().hasClass('hidden')){
19776             return true;
19777         }
19778         
19779         if(value.length < 1)  {
19780             if(this.allowBlank){
19781                 return true;
19782             }
19783             return false;
19784         }
19785         
19786         if(value.length < this.minLength){
19787             return false;
19788         }
19789         if(value.length > this.maxLength){
19790             return false;
19791         }
19792         if(this.vtype){
19793             var vt = Roo.form.VTypes;
19794             if(!vt[this.vtype](value, this)){
19795                 return false;
19796             }
19797         }
19798         if(typeof this.validator == "function"){
19799             var msg = this.validator(value);
19800             if(msg !== true){
19801                 return false;
19802             }
19803         }
19804         
19805         if(this.regex && !this.regex.test(value)){
19806             return false;
19807         }
19808         
19809         if(typeof(this.parseDate(value)) == 'undefined'){
19810             return false;
19811         }
19812         
19813         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19814             return false;
19815         }      
19816         
19817         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19818             return false;
19819         } 
19820         
19821         
19822         return true;
19823     },
19824     
19825     reset : function()
19826     {
19827         this.date = this.viewDate = '';
19828         
19829         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19830     }
19831    
19832 });
19833
19834 Roo.apply(Roo.bootstrap.DateField,  {
19835     
19836     head : {
19837         tag: 'thead',
19838         cn: [
19839         {
19840             tag: 'tr',
19841             cn: [
19842             {
19843                 tag: 'th',
19844                 cls: 'prev',
19845                 html: '<i class="fa fa-arrow-left"/>'
19846             },
19847             {
19848                 tag: 'th',
19849                 cls: 'switch',
19850                 colspan: '5'
19851             },
19852             {
19853                 tag: 'th',
19854                 cls: 'next',
19855                 html: '<i class="fa fa-arrow-right"/>'
19856             }
19857
19858             ]
19859         }
19860         ]
19861     },
19862     
19863     content : {
19864         tag: 'tbody',
19865         cn: [
19866         {
19867             tag: 'tr',
19868             cn: [
19869             {
19870                 tag: 'td',
19871                 colspan: '7'
19872             }
19873             ]
19874         }
19875         ]
19876     },
19877     
19878     footer : {
19879         tag: 'tfoot',
19880         cn: [
19881         {
19882             tag: 'tr',
19883             cn: [
19884             {
19885                 tag: 'th',
19886                 colspan: '7',
19887                 cls: 'today'
19888             }
19889                     
19890             ]
19891         }
19892         ]
19893     },
19894     
19895     dates:{
19896         en: {
19897             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19898             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19899             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19900             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19901             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19902             today: "Today"
19903         }
19904     },
19905     
19906     modes: [
19907     {
19908         clsName: 'days',
19909         navFnc: 'Month',
19910         navStep: 1
19911     },
19912     {
19913         clsName: 'months',
19914         navFnc: 'FullYear',
19915         navStep: 1
19916     },
19917     {
19918         clsName: 'years',
19919         navFnc: 'FullYear',
19920         navStep: 10
19921     }]
19922 });
19923
19924 Roo.apply(Roo.bootstrap.DateField,  {
19925   
19926     template : {
19927         tag: 'div',
19928         cls: 'datepicker dropdown-menu roo-dynamic',
19929         cn: [
19930         {
19931             tag: 'div',
19932             cls: 'datepicker-days',
19933             cn: [
19934             {
19935                 tag: 'table',
19936                 cls: 'table-condensed',
19937                 cn:[
19938                 Roo.bootstrap.DateField.head,
19939                 {
19940                     tag: 'tbody'
19941                 },
19942                 Roo.bootstrap.DateField.footer
19943                 ]
19944             }
19945             ]
19946         },
19947         {
19948             tag: 'div',
19949             cls: 'datepicker-months',
19950             cn: [
19951             {
19952                 tag: 'table',
19953                 cls: 'table-condensed',
19954                 cn:[
19955                 Roo.bootstrap.DateField.head,
19956                 Roo.bootstrap.DateField.content,
19957                 Roo.bootstrap.DateField.footer
19958                 ]
19959             }
19960             ]
19961         },
19962         {
19963             tag: 'div',
19964             cls: 'datepicker-years',
19965             cn: [
19966             {
19967                 tag: 'table',
19968                 cls: 'table-condensed',
19969                 cn:[
19970                 Roo.bootstrap.DateField.head,
19971                 Roo.bootstrap.DateField.content,
19972                 Roo.bootstrap.DateField.footer
19973                 ]
19974             }
19975             ]
19976         }
19977         ]
19978     }
19979 });
19980
19981  
19982
19983  /*
19984  * - LGPL
19985  *
19986  * TimeField
19987  * 
19988  */
19989
19990 /**
19991  * @class Roo.bootstrap.TimeField
19992  * @extends Roo.bootstrap.Input
19993  * Bootstrap DateField class
19994  * 
19995  * 
19996  * @constructor
19997  * Create a new TimeField
19998  * @param {Object} config The config object
19999  */
20000
20001 Roo.bootstrap.TimeField = function(config){
20002     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20003     this.addEvents({
20004             /**
20005              * @event show
20006              * Fires when this field show.
20007              * @param {Roo.bootstrap.DateField} thisthis
20008              * @param {Mixed} date The date value
20009              */
20010             show : true,
20011             /**
20012              * @event show
20013              * Fires when this field hide.
20014              * @param {Roo.bootstrap.DateField} this
20015              * @param {Mixed} date The date value
20016              */
20017             hide : true,
20018             /**
20019              * @event select
20020              * Fires when select a date.
20021              * @param {Roo.bootstrap.DateField} this
20022              * @param {Mixed} date The date value
20023              */
20024             select : true
20025         });
20026 };
20027
20028 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
20029     
20030     /**
20031      * @cfg {String} format
20032      * The default time format string which can be overriden for localization support.  The format must be
20033      * valid according to {@link Date#parseDate} (defaults to 'H:i').
20034      */
20035     format : "H:i",
20036        
20037     onRender: function(ct, position)
20038     {
20039         
20040         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20041                 
20042         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20043         
20044         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20045         
20046         this.pop = this.picker().select('>.datepicker-time',true).first();
20047         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20048         
20049         this.picker().on('mousedown', this.onMousedown, this);
20050         this.picker().on('click', this.onClick, this);
20051         
20052         this.picker().addClass('datepicker-dropdown');
20053     
20054         this.fillTime();
20055         this.update();
20056             
20057         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20058         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20059         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20060         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20061         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20062         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20063
20064     },
20065     
20066     fireKey: function(e){
20067         if (!this.picker().isVisible()){
20068             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20069                 this.show();
20070             }
20071             return;
20072         }
20073
20074         e.preventDefault();
20075         
20076         switch(e.keyCode){
20077             case 27: // escape
20078                 this.hide();
20079                 break;
20080             case 37: // left
20081             case 39: // right
20082                 this.onTogglePeriod();
20083                 break;
20084             case 38: // up
20085                 this.onIncrementMinutes();
20086                 break;
20087             case 40: // down
20088                 this.onDecrementMinutes();
20089                 break;
20090             case 13: // enter
20091             case 9: // tab
20092                 this.setTime();
20093                 break;
20094         }
20095     },
20096     
20097     onClick: function(e) {
20098         e.stopPropagation();
20099         e.preventDefault();
20100     },
20101     
20102     picker : function()
20103     {
20104         return this.el.select('.datepicker', true).first();
20105     },
20106     
20107     fillTime: function()
20108     {    
20109         var time = this.pop.select('tbody', true).first();
20110         
20111         time.dom.innerHTML = '';
20112         
20113         time.createChild({
20114             tag: 'tr',
20115             cn: [
20116                 {
20117                     tag: 'td',
20118                     cn: [
20119                         {
20120                             tag: 'a',
20121                             href: '#',
20122                             cls: 'btn',
20123                             cn: [
20124                                 {
20125                                     tag: 'span',
20126                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20127                                 }
20128                             ]
20129                         } 
20130                     ]
20131                 },
20132                 {
20133                     tag: 'td',
20134                     cls: 'separator'
20135                 },
20136                 {
20137                     tag: 'td',
20138                     cn: [
20139                         {
20140                             tag: 'a',
20141                             href: '#',
20142                             cls: 'btn',
20143                             cn: [
20144                                 {
20145                                     tag: 'span',
20146                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20147                                 }
20148                             ]
20149                         }
20150                     ]
20151                 },
20152                 {
20153                     tag: 'td',
20154                     cls: 'separator'
20155                 }
20156             ]
20157         });
20158         
20159         time.createChild({
20160             tag: 'tr',
20161             cn: [
20162                 {
20163                     tag: 'td',
20164                     cn: [
20165                         {
20166                             tag: 'span',
20167                             cls: 'timepicker-hour',
20168                             html: '00'
20169                         }  
20170                     ]
20171                 },
20172                 {
20173                     tag: 'td',
20174                     cls: 'separator',
20175                     html: ':'
20176                 },
20177                 {
20178                     tag: 'td',
20179                     cn: [
20180                         {
20181                             tag: 'span',
20182                             cls: 'timepicker-minute',
20183                             html: '00'
20184                         }  
20185                     ]
20186                 },
20187                 {
20188                     tag: 'td',
20189                     cls: 'separator'
20190                 },
20191                 {
20192                     tag: 'td',
20193                     cn: [
20194                         {
20195                             tag: 'button',
20196                             type: 'button',
20197                             cls: 'btn btn-primary period',
20198                             html: 'AM'
20199                             
20200                         }
20201                     ]
20202                 }
20203             ]
20204         });
20205         
20206         time.createChild({
20207             tag: 'tr',
20208             cn: [
20209                 {
20210                     tag: 'td',
20211                     cn: [
20212                         {
20213                             tag: 'a',
20214                             href: '#',
20215                             cls: 'btn',
20216                             cn: [
20217                                 {
20218                                     tag: 'span',
20219                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20220                                 }
20221                             ]
20222                         }
20223                     ]
20224                 },
20225                 {
20226                     tag: 'td',
20227                     cls: 'separator'
20228                 },
20229                 {
20230                     tag: 'td',
20231                     cn: [
20232                         {
20233                             tag: 'a',
20234                             href: '#',
20235                             cls: 'btn',
20236                             cn: [
20237                                 {
20238                                     tag: 'span',
20239                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20240                                 }
20241                             ]
20242                         }
20243                     ]
20244                 },
20245                 {
20246                     tag: 'td',
20247                     cls: 'separator'
20248                 }
20249             ]
20250         });
20251         
20252     },
20253     
20254     update: function()
20255     {
20256         
20257         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20258         
20259         this.fill();
20260     },
20261     
20262     fill: function() 
20263     {
20264         var hours = this.time.getHours();
20265         var minutes = this.time.getMinutes();
20266         var period = 'AM';
20267         
20268         if(hours > 11){
20269             period = 'PM';
20270         }
20271         
20272         if(hours == 0){
20273             hours = 12;
20274         }
20275         
20276         
20277         if(hours > 12){
20278             hours = hours - 12;
20279         }
20280         
20281         if(hours < 10){
20282             hours = '0' + hours;
20283         }
20284         
20285         if(minutes < 10){
20286             minutes = '0' + minutes;
20287         }
20288         
20289         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20290         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20291         this.pop.select('button', true).first().dom.innerHTML = period;
20292         
20293     },
20294     
20295     place: function()
20296     {   
20297         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20298         
20299         var cls = ['bottom'];
20300         
20301         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20302             cls.pop();
20303             cls.push('top');
20304         }
20305         
20306         cls.push('right');
20307         
20308         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20309             cls.pop();
20310             cls.push('left');
20311         }
20312         
20313         this.picker().addClass(cls.join('-'));
20314         
20315         var _this = this;
20316         
20317         Roo.each(cls, function(c){
20318             if(c == 'bottom'){
20319                 _this.picker().setTop(_this.inputEl().getHeight());
20320                 return;
20321             }
20322             if(c == 'top'){
20323                 _this.picker().setTop(0 - _this.picker().getHeight());
20324                 return;
20325             }
20326             
20327             if(c == 'left'){
20328                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20329                 return;
20330             }
20331             if(c == 'right'){
20332                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20333                 return;
20334             }
20335         });
20336         
20337     },
20338   
20339     onFocus : function()
20340     {
20341         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20342         this.show();
20343     },
20344     
20345     onBlur : function()
20346     {
20347         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20348         this.hide();
20349     },
20350     
20351     show : function()
20352     {
20353         this.picker().show();
20354         this.pop.show();
20355         this.update();
20356         this.place();
20357         
20358         this.fireEvent('show', this, this.date);
20359     },
20360     
20361     hide : function()
20362     {
20363         this.picker().hide();
20364         this.pop.hide();
20365         
20366         this.fireEvent('hide', this, this.date);
20367     },
20368     
20369     setTime : function()
20370     {
20371         this.hide();
20372         this.setValue(this.time.format(this.format));
20373         
20374         this.fireEvent('select', this, this.date);
20375         
20376         
20377     },
20378     
20379     onMousedown: function(e){
20380         e.stopPropagation();
20381         e.preventDefault();
20382     },
20383     
20384     onIncrementHours: function()
20385     {
20386         Roo.log('onIncrementHours');
20387         this.time = this.time.add(Date.HOUR, 1);
20388         this.update();
20389         
20390     },
20391     
20392     onDecrementHours: function()
20393     {
20394         Roo.log('onDecrementHours');
20395         this.time = this.time.add(Date.HOUR, -1);
20396         this.update();
20397     },
20398     
20399     onIncrementMinutes: function()
20400     {
20401         Roo.log('onIncrementMinutes');
20402         this.time = this.time.add(Date.MINUTE, 1);
20403         this.update();
20404     },
20405     
20406     onDecrementMinutes: function()
20407     {
20408         Roo.log('onDecrementMinutes');
20409         this.time = this.time.add(Date.MINUTE, -1);
20410         this.update();
20411     },
20412     
20413     onTogglePeriod: function()
20414     {
20415         Roo.log('onTogglePeriod');
20416         this.time = this.time.add(Date.HOUR, 12);
20417         this.update();
20418     }
20419     
20420    
20421 });
20422
20423 Roo.apply(Roo.bootstrap.TimeField,  {
20424     
20425     content : {
20426         tag: 'tbody',
20427         cn: [
20428             {
20429                 tag: 'tr',
20430                 cn: [
20431                 {
20432                     tag: 'td',
20433                     colspan: '7'
20434                 }
20435                 ]
20436             }
20437         ]
20438     },
20439     
20440     footer : {
20441         tag: 'tfoot',
20442         cn: [
20443             {
20444                 tag: 'tr',
20445                 cn: [
20446                 {
20447                     tag: 'th',
20448                     colspan: '7',
20449                     cls: '',
20450                     cn: [
20451                         {
20452                             tag: 'button',
20453                             cls: 'btn btn-info ok',
20454                             html: 'OK'
20455                         }
20456                     ]
20457                 }
20458
20459                 ]
20460             }
20461         ]
20462     }
20463 });
20464
20465 Roo.apply(Roo.bootstrap.TimeField,  {
20466   
20467     template : {
20468         tag: 'div',
20469         cls: 'datepicker dropdown-menu',
20470         cn: [
20471             {
20472                 tag: 'div',
20473                 cls: 'datepicker-time',
20474                 cn: [
20475                 {
20476                     tag: 'table',
20477                     cls: 'table-condensed',
20478                     cn:[
20479                     Roo.bootstrap.TimeField.content,
20480                     Roo.bootstrap.TimeField.footer
20481                     ]
20482                 }
20483                 ]
20484             }
20485         ]
20486     }
20487 });
20488
20489  
20490
20491  /*
20492  * - LGPL
20493  *
20494  * MonthField
20495  * 
20496  */
20497
20498 /**
20499  * @class Roo.bootstrap.MonthField
20500  * @extends Roo.bootstrap.Input
20501  * Bootstrap MonthField class
20502  * 
20503  * @cfg {String} language default en
20504  * 
20505  * @constructor
20506  * Create a new MonthField
20507  * @param {Object} config The config object
20508  */
20509
20510 Roo.bootstrap.MonthField = function(config){
20511     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20512     
20513     this.addEvents({
20514         /**
20515          * @event show
20516          * Fires when this field show.
20517          * @param {Roo.bootstrap.MonthField} this
20518          * @param {Mixed} date The date value
20519          */
20520         show : true,
20521         /**
20522          * @event show
20523          * Fires when this field hide.
20524          * @param {Roo.bootstrap.MonthField} this
20525          * @param {Mixed} date The date value
20526          */
20527         hide : true,
20528         /**
20529          * @event select
20530          * Fires when select a date.
20531          * @param {Roo.bootstrap.MonthField} this
20532          * @param {String} oldvalue The old value
20533          * @param {String} newvalue The new value
20534          */
20535         select : true
20536     });
20537 };
20538
20539 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20540     
20541     onRender: function(ct, position)
20542     {
20543         
20544         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20545         
20546         this.language = this.language || 'en';
20547         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20548         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20549         
20550         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20551         this.isInline = false;
20552         this.isInput = true;
20553         this.component = this.el.select('.add-on', true).first() || false;
20554         this.component = (this.component && this.component.length === 0) ? false : this.component;
20555         this.hasInput = this.component && this.inputEL().length;
20556         
20557         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20558         
20559         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20560         
20561         this.picker().on('mousedown', this.onMousedown, this);
20562         this.picker().on('click', this.onClick, this);
20563         
20564         this.picker().addClass('datepicker-dropdown');
20565         
20566         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20567             v.setStyle('width', '189px');
20568         });
20569         
20570         this.fillMonths();
20571         
20572         this.update();
20573         
20574         if(this.isInline) {
20575             this.show();
20576         }
20577         
20578     },
20579     
20580     setValue: function(v, suppressEvent)
20581     {   
20582         var o = this.getValue();
20583         
20584         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20585         
20586         this.update();
20587
20588         if(suppressEvent !== true){
20589             this.fireEvent('select', this, o, v);
20590         }
20591         
20592     },
20593     
20594     getValue: function()
20595     {
20596         return this.value;
20597     },
20598     
20599     onClick: function(e) 
20600     {
20601         e.stopPropagation();
20602         e.preventDefault();
20603         
20604         var target = e.getTarget();
20605         
20606         if(target.nodeName.toLowerCase() === 'i'){
20607             target = Roo.get(target).dom.parentNode;
20608         }
20609         
20610         var nodeName = target.nodeName;
20611         var className = target.className;
20612         var html = target.innerHTML;
20613         
20614         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20615             return;
20616         }
20617         
20618         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20619         
20620         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20621         
20622         this.hide();
20623                         
20624     },
20625     
20626     picker : function()
20627     {
20628         return this.pickerEl;
20629     },
20630     
20631     fillMonths: function()
20632     {    
20633         var i = 0;
20634         var months = this.picker().select('>.datepicker-months td', true).first();
20635         
20636         months.dom.innerHTML = '';
20637         
20638         while (i < 12) {
20639             var month = {
20640                 tag: 'span',
20641                 cls: 'month',
20642                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20643             };
20644             
20645             months.createChild(month);
20646         }
20647         
20648     },
20649     
20650     update: function()
20651     {
20652         var _this = this;
20653         
20654         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20655             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20656         }
20657         
20658         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20659             e.removeClass('active');
20660             
20661             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20662                 e.addClass('active');
20663             }
20664         })
20665     },
20666     
20667     place: function()
20668     {
20669         if(this.isInline) {
20670             return;
20671         }
20672         
20673         this.picker().removeClass(['bottom', 'top']);
20674         
20675         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20676             /*
20677              * place to the top of element!
20678              *
20679              */
20680             
20681             this.picker().addClass('top');
20682             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20683             
20684             return;
20685         }
20686         
20687         this.picker().addClass('bottom');
20688         
20689         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20690     },
20691     
20692     onFocus : function()
20693     {
20694         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20695         this.show();
20696     },
20697     
20698     onBlur : function()
20699     {
20700         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20701         
20702         var d = this.inputEl().getValue();
20703         
20704         this.setValue(d);
20705                 
20706         this.hide();
20707     },
20708     
20709     show : function()
20710     {
20711         this.picker().show();
20712         this.picker().select('>.datepicker-months', true).first().show();
20713         this.update();
20714         this.place();
20715         
20716         this.fireEvent('show', this, this.date);
20717     },
20718     
20719     hide : function()
20720     {
20721         if(this.isInline) {
20722             return;
20723         }
20724         this.picker().hide();
20725         this.fireEvent('hide', this, this.date);
20726         
20727     },
20728     
20729     onMousedown: function(e)
20730     {
20731         e.stopPropagation();
20732         e.preventDefault();
20733     },
20734     
20735     keyup: function(e)
20736     {
20737         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20738         this.update();
20739     },
20740
20741     fireKey: function(e)
20742     {
20743         if (!this.picker().isVisible()){
20744             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20745                 this.show();
20746             }
20747             return;
20748         }
20749         
20750         var dir;
20751         
20752         switch(e.keyCode){
20753             case 27: // escape
20754                 this.hide();
20755                 e.preventDefault();
20756                 break;
20757             case 37: // left
20758             case 39: // right
20759                 dir = e.keyCode == 37 ? -1 : 1;
20760                 
20761                 this.vIndex = this.vIndex + dir;
20762                 
20763                 if(this.vIndex < 0){
20764                     this.vIndex = 0;
20765                 }
20766                 
20767                 if(this.vIndex > 11){
20768                     this.vIndex = 11;
20769                 }
20770                 
20771                 if(isNaN(this.vIndex)){
20772                     this.vIndex = 0;
20773                 }
20774                 
20775                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20776                 
20777                 break;
20778             case 38: // up
20779             case 40: // down
20780                 
20781                 dir = e.keyCode == 38 ? -1 : 1;
20782                 
20783                 this.vIndex = this.vIndex + dir * 4;
20784                 
20785                 if(this.vIndex < 0){
20786                     this.vIndex = 0;
20787                 }
20788                 
20789                 if(this.vIndex > 11){
20790                     this.vIndex = 11;
20791                 }
20792                 
20793                 if(isNaN(this.vIndex)){
20794                     this.vIndex = 0;
20795                 }
20796                 
20797                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20798                 break;
20799                 
20800             case 13: // enter
20801                 
20802                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20803                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20804                 }
20805                 
20806                 this.hide();
20807                 e.preventDefault();
20808                 break;
20809             case 9: // tab
20810                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20811                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20812                 }
20813                 this.hide();
20814                 break;
20815             case 16: // shift
20816             case 17: // ctrl
20817             case 18: // alt
20818                 break;
20819             default :
20820                 this.hide();
20821                 
20822         }
20823     },
20824     
20825     remove: function() 
20826     {
20827         this.picker().remove();
20828     }
20829    
20830 });
20831
20832 Roo.apply(Roo.bootstrap.MonthField,  {
20833     
20834     content : {
20835         tag: 'tbody',
20836         cn: [
20837         {
20838             tag: 'tr',
20839             cn: [
20840             {
20841                 tag: 'td',
20842                 colspan: '7'
20843             }
20844             ]
20845         }
20846         ]
20847     },
20848     
20849     dates:{
20850         en: {
20851             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20852             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20853         }
20854     }
20855 });
20856
20857 Roo.apply(Roo.bootstrap.MonthField,  {
20858   
20859     template : {
20860         tag: 'div',
20861         cls: 'datepicker dropdown-menu roo-dynamic',
20862         cn: [
20863             {
20864                 tag: 'div',
20865                 cls: 'datepicker-months',
20866                 cn: [
20867                 {
20868                     tag: 'table',
20869                     cls: 'table-condensed',
20870                     cn:[
20871                         Roo.bootstrap.DateField.content
20872                     ]
20873                 }
20874                 ]
20875             }
20876         ]
20877     }
20878 });
20879
20880  
20881
20882  
20883  /*
20884  * - LGPL
20885  *
20886  * CheckBox
20887  * 
20888  */
20889
20890 /**
20891  * @class Roo.bootstrap.CheckBox
20892  * @extends Roo.bootstrap.Input
20893  * Bootstrap CheckBox class
20894  * 
20895  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20896  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20897  * @cfg {String} boxLabel The text that appears beside the checkbox
20898  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20899  * @cfg {Boolean} checked initnal the element
20900  * @cfg {Boolean} inline inline the element (default false)
20901  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20902  * @cfg {String} tooltip label tooltip
20903  * 
20904  * @constructor
20905  * Create a new CheckBox
20906  * @param {Object} config The config object
20907  */
20908
20909 Roo.bootstrap.CheckBox = function(config){
20910     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20911    
20912     this.addEvents({
20913         /**
20914         * @event check
20915         * Fires when the element is checked or unchecked.
20916         * @param {Roo.bootstrap.CheckBox} this This input
20917         * @param {Boolean} checked The new checked value
20918         */
20919        check : true,
20920        /**
20921         * @event click
20922         * Fires when the element is click.
20923         * @param {Roo.bootstrap.CheckBox} this This input
20924         */
20925        click : true
20926     });
20927     
20928 };
20929
20930 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20931   
20932     inputType: 'checkbox',
20933     inputValue: 1,
20934     valueOff: 0,
20935     boxLabel: false,
20936     checked: false,
20937     weight : false,
20938     inline: false,
20939     tooltip : '',
20940     
20941     // checkbox success does not make any sense really.. 
20942     invalidClass : "",
20943     validClass : "",
20944     
20945     
20946     getAutoCreate : function()
20947     {
20948         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20949         
20950         var id = Roo.id();
20951         
20952         var cfg = {};
20953         
20954         cfg.cls = 'form-group ' + this.inputType; //input-group
20955         
20956         if(this.inline){
20957             cfg.cls += ' ' + this.inputType + '-inline';
20958         }
20959         
20960         var input =  {
20961             tag: 'input',
20962             id : id,
20963             type : this.inputType,
20964             value : this.inputValue,
20965             cls : 'roo-' + this.inputType, //'form-box',
20966             placeholder : this.placeholder || ''
20967             
20968         };
20969         
20970         if(this.inputType != 'radio'){
20971             var hidden =  {
20972                 tag: 'input',
20973                 type : 'hidden',
20974                 cls : 'roo-hidden-value',
20975                 value : this.checked ? this.inputValue : this.valueOff
20976             };
20977         }
20978         
20979             
20980         if (this.weight) { // Validity check?
20981             cfg.cls += " " + this.inputType + "-" + this.weight;
20982         }
20983         
20984         if (this.disabled) {
20985             input.disabled=true;
20986         }
20987         
20988         if(this.checked){
20989             input.checked = this.checked;
20990         }
20991         
20992         if (this.name) {
20993             
20994             input.name = this.name;
20995             
20996             if(this.inputType != 'radio'){
20997                 hidden.name = this.name;
20998                 input.name = '_hidden_' + this.name;
20999             }
21000         }
21001         
21002         if (this.size) {
21003             input.cls += ' input-' + this.size;
21004         }
21005         
21006         var settings=this;
21007         
21008         ['xs','sm','md','lg'].map(function(size){
21009             if (settings[size]) {
21010                 cfg.cls += ' col-' + size + '-' + settings[size];
21011             }
21012         });
21013         
21014         var inputblock = input;
21015          
21016         if (this.before || this.after) {
21017             
21018             inputblock = {
21019                 cls : 'input-group',
21020                 cn :  [] 
21021             };
21022             
21023             if (this.before) {
21024                 inputblock.cn.push({
21025                     tag :'span',
21026                     cls : 'input-group-addon',
21027                     html : this.before
21028                 });
21029             }
21030             
21031             inputblock.cn.push(input);
21032             
21033             if(this.inputType != 'radio'){
21034                 inputblock.cn.push(hidden);
21035             }
21036             
21037             if (this.after) {
21038                 inputblock.cn.push({
21039                     tag :'span',
21040                     cls : 'input-group-addon',
21041                     html : this.after
21042                 });
21043             }
21044             
21045         }
21046         var boxLabelCfg = false;
21047         
21048         if(this.boxLabel){
21049            
21050             boxLabelCfg = {
21051                 tag: 'label',
21052                 //'for': id, // box label is handled by onclick - so no for...
21053                 cls: 'box-label',
21054                 html: this.boxLabel
21055             };
21056             if(this.tooltip){
21057                 boxLabelCfg.tooltip = this.tooltip;
21058             }
21059              
21060         }
21061         
21062         
21063         if (align ==='left' && this.fieldLabel.length) {
21064 //                Roo.log("left and has label");
21065             cfg.cn = [
21066                 {
21067                     tag: 'label',
21068                     'for' :  id,
21069                     cls : 'control-label',
21070                     html : this.fieldLabel
21071                 },
21072                 {
21073                     cls : "", 
21074                     cn: [
21075                         inputblock
21076                     ]
21077                 }
21078             ];
21079             
21080             if (boxLabelCfg) {
21081                 cfg.cn[1].cn.push(boxLabelCfg);
21082             }
21083             
21084             if(this.labelWidth > 12){
21085                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21086             }
21087             
21088             if(this.labelWidth < 13 && this.labelmd == 0){
21089                 this.labelmd = this.labelWidth;
21090             }
21091             
21092             if(this.labellg > 0){
21093                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21094                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21095             }
21096             
21097             if(this.labelmd > 0){
21098                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21099                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21100             }
21101             
21102             if(this.labelsm > 0){
21103                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21104                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21105             }
21106             
21107             if(this.labelxs > 0){
21108                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21109                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21110             }
21111             
21112         } else if ( this.fieldLabel.length) {
21113 //                Roo.log(" label");
21114                 cfg.cn = [
21115                    
21116                     {
21117                         tag: this.boxLabel ? 'span' : 'label',
21118                         'for': id,
21119                         cls: 'control-label box-input-label',
21120                         //cls : 'input-group-addon',
21121                         html : this.fieldLabel
21122                     },
21123                     
21124                     inputblock
21125                     
21126                 ];
21127                 if (boxLabelCfg) {
21128                     cfg.cn.push(boxLabelCfg);
21129                 }
21130
21131         } else {
21132             
21133 //                Roo.log(" no label && no align");
21134                 cfg.cn = [  inputblock ] ;
21135                 if (boxLabelCfg) {
21136                     cfg.cn.push(boxLabelCfg);
21137                 }
21138
21139                 
21140         }
21141         
21142        
21143         
21144         if(this.inputType != 'radio'){
21145             cfg.cn.push(hidden);
21146         }
21147         
21148         return cfg;
21149         
21150     },
21151     
21152     /**
21153      * return the real input element.
21154      */
21155     inputEl: function ()
21156     {
21157         return this.el.select('input.roo-' + this.inputType,true).first();
21158     },
21159     hiddenEl: function ()
21160     {
21161         return this.el.select('input.roo-hidden-value',true).first();
21162     },
21163     
21164     labelEl: function()
21165     {
21166         return this.el.select('label.control-label',true).first();
21167     },
21168     /* depricated... */
21169     
21170     label: function()
21171     {
21172         return this.labelEl();
21173     },
21174     
21175     boxLabelEl: function()
21176     {
21177         return this.el.select('label.box-label',true).first();
21178     },
21179     
21180     initEvents : function()
21181     {
21182 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21183         
21184         this.inputEl().on('click', this.onClick,  this);
21185         
21186         if (this.boxLabel) { 
21187             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21188         }
21189         
21190         this.startValue = this.getValue();
21191         
21192         if(this.groupId){
21193             Roo.bootstrap.CheckBox.register(this);
21194         }
21195     },
21196     
21197     onClick : function(e)
21198     {   
21199         if(this.fireEvent('click', this, e) !== false){
21200             this.setChecked(!this.checked);
21201         }
21202         
21203     },
21204     
21205     setChecked : function(state,suppressEvent)
21206     {
21207         this.startValue = this.getValue();
21208
21209         if(this.inputType == 'radio'){
21210             
21211             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21212                 e.dom.checked = false;
21213             });
21214             
21215             this.inputEl().dom.checked = true;
21216             
21217             this.inputEl().dom.value = this.inputValue;
21218             
21219             if(suppressEvent !== true){
21220                 this.fireEvent('check', this, true);
21221             }
21222             
21223             this.validate();
21224             
21225             return;
21226         }
21227         
21228         this.checked = state;
21229         
21230         this.inputEl().dom.checked = state;
21231         
21232         
21233         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21234         
21235         if(suppressEvent !== true){
21236             this.fireEvent('check', this, state);
21237         }
21238         
21239         this.validate();
21240     },
21241     
21242     getValue : function()
21243     {
21244         if(this.inputType == 'radio'){
21245             return this.getGroupValue();
21246         }
21247         
21248         return this.hiddenEl().dom.value;
21249         
21250     },
21251     
21252     getGroupValue : function()
21253     {
21254         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21255             return '';
21256         }
21257         
21258         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21259     },
21260     
21261     setValue : function(v,suppressEvent)
21262     {
21263         if(this.inputType == 'radio'){
21264             this.setGroupValue(v, suppressEvent);
21265             return;
21266         }
21267         
21268         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21269         
21270         this.validate();
21271     },
21272     
21273     setGroupValue : function(v, suppressEvent)
21274     {
21275         this.startValue = this.getValue();
21276         
21277         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21278             e.dom.checked = false;
21279             
21280             if(e.dom.value == v){
21281                 e.dom.checked = true;
21282             }
21283         });
21284         
21285         if(suppressEvent !== true){
21286             this.fireEvent('check', this, true);
21287         }
21288
21289         this.validate();
21290         
21291         return;
21292     },
21293     
21294     validate : function()
21295     {
21296         if(this.getVisibilityEl().hasClass('hidden')){
21297             return true;
21298         }
21299         
21300         if(
21301                 this.disabled || 
21302                 (this.inputType == 'radio' && this.validateRadio()) ||
21303                 (this.inputType == 'checkbox' && this.validateCheckbox())
21304         ){
21305             this.markValid();
21306             return true;
21307         }
21308         
21309         this.markInvalid();
21310         return false;
21311     },
21312     
21313     validateRadio : function()
21314     {
21315         if(this.getVisibilityEl().hasClass('hidden')){
21316             return true;
21317         }
21318         
21319         if(this.allowBlank){
21320             return true;
21321         }
21322         
21323         var valid = false;
21324         
21325         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21326             if(!e.dom.checked){
21327                 return;
21328             }
21329             
21330             valid = true;
21331             
21332             return false;
21333         });
21334         
21335         return valid;
21336     },
21337     
21338     validateCheckbox : function()
21339     {
21340         if(!this.groupId){
21341             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21342             //return (this.getValue() == this.inputValue) ? true : false;
21343         }
21344         
21345         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21346         
21347         if(!group){
21348             return false;
21349         }
21350         
21351         var r = false;
21352         
21353         for(var i in group){
21354             if(group[i].el.isVisible(true)){
21355                 r = false;
21356                 break;
21357             }
21358             
21359             r = true;
21360         }
21361         
21362         for(var i in group){
21363             if(r){
21364                 break;
21365             }
21366             
21367             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21368         }
21369         
21370         return r;
21371     },
21372     
21373     /**
21374      * Mark this field as valid
21375      */
21376     markValid : function()
21377     {
21378         var _this = this;
21379         
21380         this.fireEvent('valid', this);
21381         
21382         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21383         
21384         if(this.groupId){
21385             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21386         }
21387         
21388         if(label){
21389             label.markValid();
21390         }
21391
21392         if(this.inputType == 'radio'){
21393             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21394                 var fg = e.findParent('.form-group', false, true);
21395                 if (Roo.bootstrap.version == 3) {
21396                     fg.removeClass([_this.invalidClass, _this.validClass]);
21397                     fg.addClass(_this.validClass);
21398                 } else {
21399                     fg.removeClass(['is-valid', 'is-invalid']);
21400                     fg.addClass('is-valid');
21401                 }
21402             });
21403             
21404             return;
21405         }
21406
21407         if(!this.groupId){
21408             var fg = this.el.findParent('.form-group', false, true);
21409             if (Roo.bootstrap.version == 3) {
21410                 fg.removeClass([this.invalidClass, this.validClass]);
21411                 fg.addClass(this.validClass);
21412             } else {
21413                 fg.removeClass(['is-valid', 'is-invalid']);
21414                 fg.addClass('is-valid');
21415             }
21416             return;
21417         }
21418         
21419         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21420         
21421         if(!group){
21422             return;
21423         }
21424         
21425         for(var i in group){
21426             var fg = group[i].el.findParent('.form-group', false, true);
21427             if (Roo.bootstrap.version == 3) {
21428                 fg.removeClass([this.invalidClass, this.validClass]);
21429                 fg.addClass(this.validClass);
21430             } else {
21431                 fg.removeClass(['is-valid', 'is-invalid']);
21432                 fg.addClass('is-valid');
21433             }
21434         }
21435     },
21436     
21437      /**
21438      * Mark this field as invalid
21439      * @param {String} msg The validation message
21440      */
21441     markInvalid : function(msg)
21442     {
21443         if(this.allowBlank){
21444             return;
21445         }
21446         
21447         var _this = this;
21448         
21449         this.fireEvent('invalid', this, msg);
21450         
21451         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21452         
21453         if(this.groupId){
21454             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21455         }
21456         
21457         if(label){
21458             label.markInvalid();
21459         }
21460             
21461         if(this.inputType == 'radio'){
21462             
21463             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21464                 var fg = e.findParent('.form-group', false, true);
21465                 if (Roo.bootstrap.version == 3) {
21466                     fg.removeClass([_this.invalidClass, _this.validClass]);
21467                     fg.addClass(_this.invalidClass);
21468                 } else {
21469                     fg.removeClass(['is-invalid', 'is-valid']);
21470                     fg.addClass('is-invalid');
21471                 }
21472             });
21473             
21474             return;
21475         }
21476         
21477         if(!this.groupId){
21478             var fg = this.el.findParent('.form-group', false, true);
21479             if (Roo.bootstrap.version == 3) {
21480                 fg.removeClass([_this.invalidClass, _this.validClass]);
21481                 fg.addClass(_this.invalidClass);
21482             } else {
21483                 fg.removeClass(['is-invalid', 'is-valid']);
21484                 fg.addClass('is-invalid');
21485             }
21486             return;
21487         }
21488         
21489         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21490         
21491         if(!group){
21492             return;
21493         }
21494         
21495         for(var i in group){
21496             var fg = group[i].el.findParent('.form-group', false, true);
21497             if (Roo.bootstrap.version == 3) {
21498                 fg.removeClass([_this.invalidClass, _this.validClass]);
21499                 fg.addClass(_this.invalidClass);
21500             } else {
21501                 fg.removeClass(['is-invalid', 'is-valid']);
21502                 fg.addClass('is-invalid');
21503             }
21504         }
21505         
21506     },
21507     
21508     clearInvalid : function()
21509     {
21510         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21511         
21512         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21513         
21514         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21515         
21516         if (label && label.iconEl) {
21517             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21518             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21519         }
21520     },
21521     
21522     disable : function()
21523     {
21524         if(this.inputType != 'radio'){
21525             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21526             return;
21527         }
21528         
21529         var _this = this;
21530         
21531         if(this.rendered){
21532             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21533                 _this.getActionEl().addClass(this.disabledClass);
21534                 e.dom.disabled = true;
21535             });
21536         }
21537         
21538         this.disabled = true;
21539         this.fireEvent("disable", this);
21540         return this;
21541     },
21542
21543     enable : function()
21544     {
21545         if(this.inputType != 'radio'){
21546             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21547             return;
21548         }
21549         
21550         var _this = this;
21551         
21552         if(this.rendered){
21553             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21554                 _this.getActionEl().removeClass(this.disabledClass);
21555                 e.dom.disabled = false;
21556             });
21557         }
21558         
21559         this.disabled = false;
21560         this.fireEvent("enable", this);
21561         return this;
21562     },
21563     
21564     setBoxLabel : function(v)
21565     {
21566         this.boxLabel = v;
21567         
21568         if(this.rendered){
21569             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21570         }
21571     }
21572
21573 });
21574
21575 Roo.apply(Roo.bootstrap.CheckBox, {
21576     
21577     groups: {},
21578     
21579      /**
21580     * register a CheckBox Group
21581     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21582     */
21583     register : function(checkbox)
21584     {
21585         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21586             this.groups[checkbox.groupId] = {};
21587         }
21588         
21589         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21590             return;
21591         }
21592         
21593         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21594         
21595     },
21596     /**
21597     * fetch a CheckBox Group based on the group ID
21598     * @param {string} the group ID
21599     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21600     */
21601     get: function(groupId) {
21602         if (typeof(this.groups[groupId]) == 'undefined') {
21603             return false;
21604         }
21605         
21606         return this.groups[groupId] ;
21607     }
21608     
21609     
21610 });
21611 /*
21612  * - LGPL
21613  *
21614  * RadioItem
21615  * 
21616  */
21617
21618 /**
21619  * @class Roo.bootstrap.Radio
21620  * @extends Roo.bootstrap.Component
21621  * Bootstrap Radio class
21622  * @cfg {String} boxLabel - the label associated
21623  * @cfg {String} value - the value of radio
21624  * 
21625  * @constructor
21626  * Create a new Radio
21627  * @param {Object} config The config object
21628  */
21629 Roo.bootstrap.Radio = function(config){
21630     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21631     
21632 };
21633
21634 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21635     
21636     boxLabel : '',
21637     
21638     value : '',
21639     
21640     getAutoCreate : function()
21641     {
21642         var cfg = {
21643             tag : 'div',
21644             cls : 'form-group radio',
21645             cn : [
21646                 {
21647                     tag : 'label',
21648                     cls : 'box-label',
21649                     html : this.boxLabel
21650                 }
21651             ]
21652         };
21653         
21654         return cfg;
21655     },
21656     
21657     initEvents : function() 
21658     {
21659         this.parent().register(this);
21660         
21661         this.el.on('click', this.onClick, this);
21662         
21663     },
21664     
21665     onClick : function(e)
21666     {
21667         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21668             this.setChecked(true);
21669         }
21670     },
21671     
21672     setChecked : function(state, suppressEvent)
21673     {
21674         this.parent().setValue(this.value, suppressEvent);
21675         
21676     },
21677     
21678     setBoxLabel : function(v)
21679     {
21680         this.boxLabel = v;
21681         
21682         if(this.rendered){
21683             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21684         }
21685     }
21686     
21687 });
21688  
21689
21690  /*
21691  * - LGPL
21692  *
21693  * Input
21694  * 
21695  */
21696
21697 /**
21698  * @class Roo.bootstrap.SecurePass
21699  * @extends Roo.bootstrap.Input
21700  * Bootstrap SecurePass class
21701  *
21702  * 
21703  * @constructor
21704  * Create a new SecurePass
21705  * @param {Object} config The config object
21706  */
21707  
21708 Roo.bootstrap.SecurePass = function (config) {
21709     // these go here, so the translation tool can replace them..
21710     this.errors = {
21711         PwdEmpty: "Please type a password, and then retype it to confirm.",
21712         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21713         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21714         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21715         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21716         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21717         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21718         TooWeak: "Your password is Too Weak."
21719     },
21720     this.meterLabel = "Password strength:";
21721     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21722     this.meterClass = [
21723         "roo-password-meter-tooweak", 
21724         "roo-password-meter-weak", 
21725         "roo-password-meter-medium", 
21726         "roo-password-meter-strong", 
21727         "roo-password-meter-grey"
21728     ];
21729     
21730     this.errors = {};
21731     
21732     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21733 }
21734
21735 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21736     /**
21737      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21738      * {
21739      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21740      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21741      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21742      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21743      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21744      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21745      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21746      * })
21747      */
21748     // private
21749     
21750     meterWidth: 300,
21751     errorMsg :'',    
21752     errors: false,
21753     imageRoot: '/',
21754     /**
21755      * @cfg {String/Object} Label for the strength meter (defaults to
21756      * 'Password strength:')
21757      */
21758     // private
21759     meterLabel: '',
21760     /**
21761      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21762      * ['Weak', 'Medium', 'Strong'])
21763      */
21764     // private    
21765     pwdStrengths: false,    
21766     // private
21767     strength: 0,
21768     // private
21769     _lastPwd: null,
21770     // private
21771     kCapitalLetter: 0,
21772     kSmallLetter: 1,
21773     kDigit: 2,
21774     kPunctuation: 3,
21775     
21776     insecure: false,
21777     // private
21778     initEvents: function ()
21779     {
21780         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21781
21782         if (this.el.is('input[type=password]') && Roo.isSafari) {
21783             this.el.on('keydown', this.SafariOnKeyDown, this);
21784         }
21785
21786         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21787     },
21788     // private
21789     onRender: function (ct, position)
21790     {
21791         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21792         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21793         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21794
21795         this.trigger.createChild({
21796                    cn: [
21797                     {
21798                     //id: 'PwdMeter',
21799                     tag: 'div',
21800                     cls: 'roo-password-meter-grey col-xs-12',
21801                     style: {
21802                         //width: 0,
21803                         //width: this.meterWidth + 'px'                                                
21804                         }
21805                     },
21806                     {                            
21807                          cls: 'roo-password-meter-text'                          
21808                     }
21809                 ]            
21810         });
21811
21812          
21813         if (this.hideTrigger) {
21814             this.trigger.setDisplayed(false);
21815         }
21816         this.setSize(this.width || '', this.height || '');
21817     },
21818     // private
21819     onDestroy: function ()
21820     {
21821         if (this.trigger) {
21822             this.trigger.removeAllListeners();
21823             this.trigger.remove();
21824         }
21825         if (this.wrap) {
21826             this.wrap.remove();
21827         }
21828         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21829     },
21830     // private
21831     checkStrength: function ()
21832     {
21833         var pwd = this.inputEl().getValue();
21834         if (pwd == this._lastPwd) {
21835             return;
21836         }
21837
21838         var strength;
21839         if (this.ClientSideStrongPassword(pwd)) {
21840             strength = 3;
21841         } else if (this.ClientSideMediumPassword(pwd)) {
21842             strength = 2;
21843         } else if (this.ClientSideWeakPassword(pwd)) {
21844             strength = 1;
21845         } else {
21846             strength = 0;
21847         }
21848         
21849         Roo.log('strength1: ' + strength);
21850         
21851         //var pm = this.trigger.child('div/div/div').dom;
21852         var pm = this.trigger.child('div/div');
21853         pm.removeClass(this.meterClass);
21854         pm.addClass(this.meterClass[strength]);
21855                 
21856         
21857         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21858                 
21859         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21860         
21861         this._lastPwd = pwd;
21862     },
21863     reset: function ()
21864     {
21865         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21866         
21867         this._lastPwd = '';
21868         
21869         var pm = this.trigger.child('div/div');
21870         pm.removeClass(this.meterClass);
21871         pm.addClass('roo-password-meter-grey');        
21872         
21873         
21874         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21875         
21876         pt.innerHTML = '';
21877         this.inputEl().dom.type='password';
21878     },
21879     // private
21880     validateValue: function (value)
21881     {
21882         
21883         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21884             return false;
21885         }
21886         if (value.length == 0) {
21887             if (this.allowBlank) {
21888                 this.clearInvalid();
21889                 return true;
21890             }
21891
21892             this.markInvalid(this.errors.PwdEmpty);
21893             this.errorMsg = this.errors.PwdEmpty;
21894             return false;
21895         }
21896         
21897         if(this.insecure){
21898             return true;
21899         }
21900         
21901         if ('[\x21-\x7e]*'.match(value)) {
21902             this.markInvalid(this.errors.PwdBadChar);
21903             this.errorMsg = this.errors.PwdBadChar;
21904             return false;
21905         }
21906         if (value.length < 6) {
21907             this.markInvalid(this.errors.PwdShort);
21908             this.errorMsg = this.errors.PwdShort;
21909             return false;
21910         }
21911         if (value.length > 16) {
21912             this.markInvalid(this.errors.PwdLong);
21913             this.errorMsg = this.errors.PwdLong;
21914             return false;
21915         }
21916         var strength;
21917         if (this.ClientSideStrongPassword(value)) {
21918             strength = 3;
21919         } else if (this.ClientSideMediumPassword(value)) {
21920             strength = 2;
21921         } else if (this.ClientSideWeakPassword(value)) {
21922             strength = 1;
21923         } else {
21924             strength = 0;
21925         }
21926
21927         
21928         if (strength < 2) {
21929             //this.markInvalid(this.errors.TooWeak);
21930             this.errorMsg = this.errors.TooWeak;
21931             //return false;
21932         }
21933         
21934         
21935         console.log('strength2: ' + strength);
21936         
21937         //var pm = this.trigger.child('div/div/div').dom;
21938         
21939         var pm = this.trigger.child('div/div');
21940         pm.removeClass(this.meterClass);
21941         pm.addClass(this.meterClass[strength]);
21942                 
21943         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21944                 
21945         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21946         
21947         this.errorMsg = ''; 
21948         return true;
21949     },
21950     // private
21951     CharacterSetChecks: function (type)
21952     {
21953         this.type = type;
21954         this.fResult = false;
21955     },
21956     // private
21957     isctype: function (character, type)
21958     {
21959         switch (type) {  
21960             case this.kCapitalLetter:
21961                 if (character >= 'A' && character <= 'Z') {
21962                     return true;
21963                 }
21964                 break;
21965             
21966             case this.kSmallLetter:
21967                 if (character >= 'a' && character <= 'z') {
21968                     return true;
21969                 }
21970                 break;
21971             
21972             case this.kDigit:
21973                 if (character >= '0' && character <= '9') {
21974                     return true;
21975                 }
21976                 break;
21977             
21978             case this.kPunctuation:
21979                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21980                     return true;
21981                 }
21982                 break;
21983             
21984             default:
21985                 return false;
21986         }
21987
21988     },
21989     // private
21990     IsLongEnough: function (pwd, size)
21991     {
21992         return !(pwd == null || isNaN(size) || pwd.length < size);
21993     },
21994     // private
21995     SpansEnoughCharacterSets: function (word, nb)
21996     {
21997         if (!this.IsLongEnough(word, nb))
21998         {
21999             return false;
22000         }
22001
22002         var characterSetChecks = new Array(
22003             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22004             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22005         );
22006         
22007         for (var index = 0; index < word.length; ++index) {
22008             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22009                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22010                     characterSetChecks[nCharSet].fResult = true;
22011                     break;
22012                 }
22013             }
22014         }
22015
22016         var nCharSets = 0;
22017         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22018             if (characterSetChecks[nCharSet].fResult) {
22019                 ++nCharSets;
22020             }
22021         }
22022
22023         if (nCharSets < nb) {
22024             return false;
22025         }
22026         return true;
22027     },
22028     // private
22029     ClientSideStrongPassword: function (pwd)
22030     {
22031         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22032     },
22033     // private
22034     ClientSideMediumPassword: function (pwd)
22035     {
22036         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22037     },
22038     // private
22039     ClientSideWeakPassword: function (pwd)
22040     {
22041         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22042     }
22043           
22044 })//<script type="text/javascript">
22045
22046 /*
22047  * Based  Ext JS Library 1.1.1
22048  * Copyright(c) 2006-2007, Ext JS, LLC.
22049  * LGPL
22050  *
22051  */
22052  
22053 /**
22054  * @class Roo.HtmlEditorCore
22055  * @extends Roo.Component
22056  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22057  *
22058  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22059  */
22060
22061 Roo.HtmlEditorCore = function(config){
22062     
22063     
22064     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22065     
22066     
22067     this.addEvents({
22068         /**
22069          * @event initialize
22070          * Fires when the editor is fully initialized (including the iframe)
22071          * @param {Roo.HtmlEditorCore} this
22072          */
22073         initialize: true,
22074         /**
22075          * @event activate
22076          * Fires when the editor is first receives the focus. Any insertion must wait
22077          * until after this event.
22078          * @param {Roo.HtmlEditorCore} this
22079          */
22080         activate: true,
22081          /**
22082          * @event beforesync
22083          * Fires before the textarea is updated with content from the editor iframe. Return false
22084          * to cancel the sync.
22085          * @param {Roo.HtmlEditorCore} this
22086          * @param {String} html
22087          */
22088         beforesync: true,
22089          /**
22090          * @event beforepush
22091          * Fires before the iframe editor is updated with content from the textarea. Return false
22092          * to cancel the push.
22093          * @param {Roo.HtmlEditorCore} this
22094          * @param {String} html
22095          */
22096         beforepush: true,
22097          /**
22098          * @event sync
22099          * Fires when the textarea is updated with content from the editor iframe.
22100          * @param {Roo.HtmlEditorCore} this
22101          * @param {String} html
22102          */
22103         sync: true,
22104          /**
22105          * @event push
22106          * Fires when the iframe editor is updated with content from the textarea.
22107          * @param {Roo.HtmlEditorCore} this
22108          * @param {String} html
22109          */
22110         push: true,
22111         
22112         /**
22113          * @event editorevent
22114          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22115          * @param {Roo.HtmlEditorCore} this
22116          */
22117         editorevent: true
22118         
22119     });
22120     
22121     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22122     
22123     // defaults : white / black...
22124     this.applyBlacklists();
22125     
22126     
22127     
22128 };
22129
22130
22131 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22132
22133
22134      /**
22135      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22136      */
22137     
22138     owner : false,
22139     
22140      /**
22141      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22142      *                        Roo.resizable.
22143      */
22144     resizable : false,
22145      /**
22146      * @cfg {Number} height (in pixels)
22147      */   
22148     height: 300,
22149    /**
22150      * @cfg {Number} width (in pixels)
22151      */   
22152     width: 500,
22153     
22154     /**
22155      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22156      * 
22157      */
22158     stylesheets: false,
22159     
22160     // id of frame..
22161     frameId: false,
22162     
22163     // private properties
22164     validationEvent : false,
22165     deferHeight: true,
22166     initialized : false,
22167     activated : false,
22168     sourceEditMode : false,
22169     onFocus : Roo.emptyFn,
22170     iframePad:3,
22171     hideMode:'offsets',
22172     
22173     clearUp: true,
22174     
22175     // blacklist + whitelisted elements..
22176     black: false,
22177     white: false,
22178      
22179     bodyCls : '',
22180
22181     /**
22182      * Protected method that will not generally be called directly. It
22183      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22184      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22185      */
22186     getDocMarkup : function(){
22187         // body styles..
22188         var st = '';
22189         
22190         // inherit styels from page...?? 
22191         if (this.stylesheets === false) {
22192             
22193             Roo.get(document.head).select('style').each(function(node) {
22194                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22195             });
22196             
22197             Roo.get(document.head).select('link').each(function(node) { 
22198                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22199             });
22200             
22201         } else if (!this.stylesheets.length) {
22202                 // simple..
22203                 st = '<style type="text/css">' +
22204                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22205                    '</style>';
22206         } else { 
22207             st = '<style type="text/css">' +
22208                     this.stylesheets +
22209                 '</style>';
22210         }
22211         
22212         st +=  '<style type="text/css">' +
22213             'IMG { cursor: pointer } ' +
22214         '</style>';
22215
22216         var cls = 'roo-htmleditor-body';
22217         
22218         if(this.bodyCls.length){
22219             cls += ' ' + this.bodyCls;
22220         }
22221         
22222         return '<html><head>' + st  +
22223             //<style type="text/css">' +
22224             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22225             //'</style>' +
22226             ' </head><body class="' +  cls + '"></body></html>';
22227     },
22228
22229     // private
22230     onRender : function(ct, position)
22231     {
22232         var _t = this;
22233         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22234         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22235         
22236         
22237         this.el.dom.style.border = '0 none';
22238         this.el.dom.setAttribute('tabIndex', -1);
22239         this.el.addClass('x-hidden hide');
22240         
22241         
22242         
22243         if(Roo.isIE){ // fix IE 1px bogus margin
22244             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22245         }
22246        
22247         
22248         this.frameId = Roo.id();
22249         
22250          
22251         
22252         var iframe = this.owner.wrap.createChild({
22253             tag: 'iframe',
22254             cls: 'form-control', // bootstrap..
22255             id: this.frameId,
22256             name: this.frameId,
22257             frameBorder : 'no',
22258             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22259         }, this.el
22260         );
22261         
22262         
22263         this.iframe = iframe.dom;
22264
22265          this.assignDocWin();
22266         
22267         this.doc.designMode = 'on';
22268        
22269         this.doc.open();
22270         this.doc.write(this.getDocMarkup());
22271         this.doc.close();
22272
22273         
22274         var task = { // must defer to wait for browser to be ready
22275             run : function(){
22276                 //console.log("run task?" + this.doc.readyState);
22277                 this.assignDocWin();
22278                 if(this.doc.body || this.doc.readyState == 'complete'){
22279                     try {
22280                         this.doc.designMode="on";
22281                     } catch (e) {
22282                         return;
22283                     }
22284                     Roo.TaskMgr.stop(task);
22285                     this.initEditor.defer(10, this);
22286                 }
22287             },
22288             interval : 10,
22289             duration: 10000,
22290             scope: this
22291         };
22292         Roo.TaskMgr.start(task);
22293
22294     },
22295
22296     // private
22297     onResize : function(w, h)
22298     {
22299          Roo.log('resize: ' +w + ',' + h );
22300         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22301         if(!this.iframe){
22302             return;
22303         }
22304         if(typeof w == 'number'){
22305             
22306             this.iframe.style.width = w + 'px';
22307         }
22308         if(typeof h == 'number'){
22309             
22310             this.iframe.style.height = h + 'px';
22311             if(this.doc){
22312                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22313             }
22314         }
22315         
22316     },
22317
22318     /**
22319      * Toggles the editor between standard and source edit mode.
22320      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22321      */
22322     toggleSourceEdit : function(sourceEditMode){
22323         
22324         this.sourceEditMode = sourceEditMode === true;
22325         
22326         if(this.sourceEditMode){
22327  
22328             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22329             
22330         }else{
22331             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22332             //this.iframe.className = '';
22333             this.deferFocus();
22334         }
22335         //this.setSize(this.owner.wrap.getSize());
22336         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22337     },
22338
22339     
22340   
22341
22342     /**
22343      * Protected method that will not generally be called directly. If you need/want
22344      * custom HTML cleanup, this is the method you should override.
22345      * @param {String} html The HTML to be cleaned
22346      * return {String} The cleaned HTML
22347      */
22348     cleanHtml : function(html){
22349         html = String(html);
22350         if(html.length > 5){
22351             if(Roo.isSafari){ // strip safari nonsense
22352                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22353             }
22354         }
22355         if(html == '&nbsp;'){
22356             html = '';
22357         }
22358         return html;
22359     },
22360
22361     /**
22362      * HTML Editor -> Textarea
22363      * Protected method that will not generally be called directly. Syncs the contents
22364      * of the editor iframe with the textarea.
22365      */
22366     syncValue : function(){
22367         if(this.initialized){
22368             var bd = (this.doc.body || this.doc.documentElement);
22369             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22370             var html = bd.innerHTML;
22371             if(Roo.isSafari){
22372                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22373                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22374                 if(m && m[1]){
22375                     html = '<div style="'+m[0]+'">' + html + '</div>';
22376                 }
22377             }
22378             html = this.cleanHtml(html);
22379             // fix up the special chars.. normaly like back quotes in word...
22380             // however we do not want to do this with chinese..
22381             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22382                 
22383                 var cc = match.charCodeAt();
22384
22385                 // Get the character value, handling surrogate pairs
22386                 if (match.length == 2) {
22387                     // It's a surrogate pair, calculate the Unicode code point
22388                     var high = match.charCodeAt(0) - 0xD800;
22389                     var low  = match.charCodeAt(1) - 0xDC00;
22390                     cc = (high * 0x400) + low + 0x10000;
22391                 }  else if (
22392                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22393                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22394                     (cc >= 0xf900 && cc < 0xfb00 )
22395                 ) {
22396                         return match;
22397                 }  
22398          
22399                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22400                 return "&#" + cc + ";";
22401                 
22402                 
22403             });
22404             
22405             
22406              
22407             if(this.owner.fireEvent('beforesync', this, html) !== false){
22408                 this.el.dom.value = html;
22409                 this.owner.fireEvent('sync', this, html);
22410             }
22411         }
22412     },
22413
22414     /**
22415      * Protected method that will not generally be called directly. Pushes the value of the textarea
22416      * into the iframe editor.
22417      */
22418     pushValue : function(){
22419         if(this.initialized){
22420             var v = this.el.dom.value.trim();
22421             
22422 //            if(v.length < 1){
22423 //                v = '&#160;';
22424 //            }
22425             
22426             if(this.owner.fireEvent('beforepush', this, v) !== false){
22427                 var d = (this.doc.body || this.doc.documentElement);
22428                 d.innerHTML = v;
22429                 this.cleanUpPaste();
22430                 this.el.dom.value = d.innerHTML;
22431                 this.owner.fireEvent('push', this, v);
22432             }
22433         }
22434     },
22435
22436     // private
22437     deferFocus : function(){
22438         this.focus.defer(10, this);
22439     },
22440
22441     // doc'ed in Field
22442     focus : function(){
22443         if(this.win && !this.sourceEditMode){
22444             this.win.focus();
22445         }else{
22446             this.el.focus();
22447         }
22448     },
22449     
22450     assignDocWin: function()
22451     {
22452         var iframe = this.iframe;
22453         
22454          if(Roo.isIE){
22455             this.doc = iframe.contentWindow.document;
22456             this.win = iframe.contentWindow;
22457         } else {
22458 //            if (!Roo.get(this.frameId)) {
22459 //                return;
22460 //            }
22461 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22462 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22463             
22464             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22465                 return;
22466             }
22467             
22468             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22469             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22470         }
22471     },
22472     
22473     // private
22474     initEditor : function(){
22475         //console.log("INIT EDITOR");
22476         this.assignDocWin();
22477         
22478         
22479         
22480         this.doc.designMode="on";
22481         this.doc.open();
22482         this.doc.write(this.getDocMarkup());
22483         this.doc.close();
22484         
22485         var dbody = (this.doc.body || this.doc.documentElement);
22486         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22487         // this copies styles from the containing element into thsi one..
22488         // not sure why we need all of this..
22489         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22490         
22491         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22492         //ss['background-attachment'] = 'fixed'; // w3c
22493         dbody.bgProperties = 'fixed'; // ie
22494         //Roo.DomHelper.applyStyles(dbody, ss);
22495         Roo.EventManager.on(this.doc, {
22496             //'mousedown': this.onEditorEvent,
22497             'mouseup': this.onEditorEvent,
22498             'dblclick': this.onEditorEvent,
22499             'click': this.onEditorEvent,
22500             'keyup': this.onEditorEvent,
22501             buffer:100,
22502             scope: this
22503         });
22504         if(Roo.isGecko){
22505             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22506         }
22507         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22508             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22509         }
22510         this.initialized = true;
22511
22512         this.owner.fireEvent('initialize', this);
22513         this.pushValue();
22514     },
22515
22516     // private
22517     onDestroy : function(){
22518         
22519         
22520         
22521         if(this.rendered){
22522             
22523             //for (var i =0; i < this.toolbars.length;i++) {
22524             //    // fixme - ask toolbars for heights?
22525             //    this.toolbars[i].onDestroy();
22526            // }
22527             
22528             //this.wrap.dom.innerHTML = '';
22529             //this.wrap.remove();
22530         }
22531     },
22532
22533     // private
22534     onFirstFocus : function(){
22535         
22536         this.assignDocWin();
22537         
22538         
22539         this.activated = true;
22540          
22541     
22542         if(Roo.isGecko){ // prevent silly gecko errors
22543             this.win.focus();
22544             var s = this.win.getSelection();
22545             if(!s.focusNode || s.focusNode.nodeType != 3){
22546                 var r = s.getRangeAt(0);
22547                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22548                 r.collapse(true);
22549                 this.deferFocus();
22550             }
22551             try{
22552                 this.execCmd('useCSS', true);
22553                 this.execCmd('styleWithCSS', false);
22554             }catch(e){}
22555         }
22556         this.owner.fireEvent('activate', this);
22557     },
22558
22559     // private
22560     adjustFont: function(btn){
22561         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22562         //if(Roo.isSafari){ // safari
22563         //    adjust *= 2;
22564        // }
22565         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22566         if(Roo.isSafari){ // safari
22567             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22568             v =  (v < 10) ? 10 : v;
22569             v =  (v > 48) ? 48 : v;
22570             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22571             
22572         }
22573         
22574         
22575         v = Math.max(1, v+adjust);
22576         
22577         this.execCmd('FontSize', v  );
22578     },
22579
22580     onEditorEvent : function(e)
22581     {
22582         this.owner.fireEvent('editorevent', this, e);
22583       //  this.updateToolbar();
22584         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22585     },
22586
22587     insertTag : function(tg)
22588     {
22589         // could be a bit smarter... -> wrap the current selected tRoo..
22590         if (tg.toLowerCase() == 'span' ||
22591             tg.toLowerCase() == 'code' ||
22592             tg.toLowerCase() == 'sup' ||
22593             tg.toLowerCase() == 'sub' 
22594             ) {
22595             
22596             range = this.createRange(this.getSelection());
22597             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22598             wrappingNode.appendChild(range.extractContents());
22599             range.insertNode(wrappingNode);
22600
22601             return;
22602             
22603             
22604             
22605         }
22606         this.execCmd("formatblock",   tg);
22607         
22608     },
22609     
22610     insertText : function(txt)
22611     {
22612         
22613         
22614         var range = this.createRange();
22615         range.deleteContents();
22616                //alert(Sender.getAttribute('label'));
22617                
22618         range.insertNode(this.doc.createTextNode(txt));
22619     } ,
22620     
22621      
22622
22623     /**
22624      * Executes a Midas editor command on the editor document and performs necessary focus and
22625      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22626      * @param {String} cmd The Midas command
22627      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22628      */
22629     relayCmd : function(cmd, value){
22630         this.win.focus();
22631         this.execCmd(cmd, value);
22632         this.owner.fireEvent('editorevent', this);
22633         //this.updateToolbar();
22634         this.owner.deferFocus();
22635     },
22636
22637     /**
22638      * Executes a Midas editor command directly on the editor document.
22639      * For visual commands, you should use {@link #relayCmd} instead.
22640      * <b>This should only be called after the editor is initialized.</b>
22641      * @param {String} cmd The Midas command
22642      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22643      */
22644     execCmd : function(cmd, value){
22645         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22646         this.syncValue();
22647     },
22648  
22649  
22650    
22651     /**
22652      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22653      * to insert tRoo.
22654      * @param {String} text | dom node.. 
22655      */
22656     insertAtCursor : function(text)
22657     {
22658         
22659         if(!this.activated){
22660             return;
22661         }
22662         /*
22663         if(Roo.isIE){
22664             this.win.focus();
22665             var r = this.doc.selection.createRange();
22666             if(r){
22667                 r.collapse(true);
22668                 r.pasteHTML(text);
22669                 this.syncValue();
22670                 this.deferFocus();
22671             
22672             }
22673             return;
22674         }
22675         */
22676         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22677             this.win.focus();
22678             
22679             
22680             // from jquery ui (MIT licenced)
22681             var range, node;
22682             var win = this.win;
22683             
22684             if (win.getSelection && win.getSelection().getRangeAt) {
22685                 range = win.getSelection().getRangeAt(0);
22686                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22687                 range.insertNode(node);
22688             } else if (win.document.selection && win.document.selection.createRange) {
22689                 // no firefox support
22690                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22691                 win.document.selection.createRange().pasteHTML(txt);
22692             } else {
22693                 // no firefox support
22694                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22695                 this.execCmd('InsertHTML', txt);
22696             } 
22697             
22698             this.syncValue();
22699             
22700             this.deferFocus();
22701         }
22702     },
22703  // private
22704     mozKeyPress : function(e){
22705         if(e.ctrlKey){
22706             var c = e.getCharCode(), cmd;
22707           
22708             if(c > 0){
22709                 c = String.fromCharCode(c).toLowerCase();
22710                 switch(c){
22711                     case 'b':
22712                         cmd = 'bold';
22713                         break;
22714                     case 'i':
22715                         cmd = 'italic';
22716                         break;
22717                     
22718                     case 'u':
22719                         cmd = 'underline';
22720                         break;
22721                     
22722                     case 'v':
22723                         this.cleanUpPaste.defer(100, this);
22724                         return;
22725                         
22726                 }
22727                 if(cmd){
22728                     this.win.focus();
22729                     this.execCmd(cmd);
22730                     this.deferFocus();
22731                     e.preventDefault();
22732                 }
22733                 
22734             }
22735         }
22736     },
22737
22738     // private
22739     fixKeys : function(){ // load time branching for fastest keydown performance
22740         if(Roo.isIE){
22741             return function(e){
22742                 var k = e.getKey(), r;
22743                 if(k == e.TAB){
22744                     e.stopEvent();
22745                     r = this.doc.selection.createRange();
22746                     if(r){
22747                         r.collapse(true);
22748                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22749                         this.deferFocus();
22750                     }
22751                     return;
22752                 }
22753                 
22754                 if(k == e.ENTER){
22755                     r = this.doc.selection.createRange();
22756                     if(r){
22757                         var target = r.parentElement();
22758                         if(!target || target.tagName.toLowerCase() != 'li'){
22759                             e.stopEvent();
22760                             r.pasteHTML('<br />');
22761                             r.collapse(false);
22762                             r.select();
22763                         }
22764                     }
22765                 }
22766                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22767                     this.cleanUpPaste.defer(100, this);
22768                     return;
22769                 }
22770                 
22771                 
22772             };
22773         }else if(Roo.isOpera){
22774             return function(e){
22775                 var k = e.getKey();
22776                 if(k == e.TAB){
22777                     e.stopEvent();
22778                     this.win.focus();
22779                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22780                     this.deferFocus();
22781                 }
22782                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22783                     this.cleanUpPaste.defer(100, this);
22784                     return;
22785                 }
22786                 
22787             };
22788         }else if(Roo.isSafari){
22789             return function(e){
22790                 var k = e.getKey();
22791                 
22792                 if(k == e.TAB){
22793                     e.stopEvent();
22794                     this.execCmd('InsertText','\t');
22795                     this.deferFocus();
22796                     return;
22797                 }
22798                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22799                     this.cleanUpPaste.defer(100, this);
22800                     return;
22801                 }
22802                 
22803              };
22804         }
22805     }(),
22806     
22807     getAllAncestors: function()
22808     {
22809         var p = this.getSelectedNode();
22810         var a = [];
22811         if (!p) {
22812             a.push(p); // push blank onto stack..
22813             p = this.getParentElement();
22814         }
22815         
22816         
22817         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22818             a.push(p);
22819             p = p.parentNode;
22820         }
22821         a.push(this.doc.body);
22822         return a;
22823     },
22824     lastSel : false,
22825     lastSelNode : false,
22826     
22827     
22828     getSelection : function() 
22829     {
22830         this.assignDocWin();
22831         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22832     },
22833     
22834     getSelectedNode: function() 
22835     {
22836         // this may only work on Gecko!!!
22837         
22838         // should we cache this!!!!
22839         
22840         
22841         
22842          
22843         var range = this.createRange(this.getSelection()).cloneRange();
22844         
22845         if (Roo.isIE) {
22846             var parent = range.parentElement();
22847             while (true) {
22848                 var testRange = range.duplicate();
22849                 testRange.moveToElementText(parent);
22850                 if (testRange.inRange(range)) {
22851                     break;
22852                 }
22853                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22854                     break;
22855                 }
22856                 parent = parent.parentElement;
22857             }
22858             return parent;
22859         }
22860         
22861         // is ancestor a text element.
22862         var ac =  range.commonAncestorContainer;
22863         if (ac.nodeType == 3) {
22864             ac = ac.parentNode;
22865         }
22866         
22867         var ar = ac.childNodes;
22868          
22869         var nodes = [];
22870         var other_nodes = [];
22871         var has_other_nodes = false;
22872         for (var i=0;i<ar.length;i++) {
22873             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22874                 continue;
22875             }
22876             // fullly contained node.
22877             
22878             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22879                 nodes.push(ar[i]);
22880                 continue;
22881             }
22882             
22883             // probably selected..
22884             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22885                 other_nodes.push(ar[i]);
22886                 continue;
22887             }
22888             // outer..
22889             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22890                 continue;
22891             }
22892             
22893             
22894             has_other_nodes = true;
22895         }
22896         if (!nodes.length && other_nodes.length) {
22897             nodes= other_nodes;
22898         }
22899         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22900             return false;
22901         }
22902         
22903         return nodes[0];
22904     },
22905     createRange: function(sel)
22906     {
22907         // this has strange effects when using with 
22908         // top toolbar - not sure if it's a great idea.
22909         //this.editor.contentWindow.focus();
22910         if (typeof sel != "undefined") {
22911             try {
22912                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22913             } catch(e) {
22914                 return this.doc.createRange();
22915             }
22916         } else {
22917             return this.doc.createRange();
22918         }
22919     },
22920     getParentElement: function()
22921     {
22922         
22923         this.assignDocWin();
22924         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22925         
22926         var range = this.createRange(sel);
22927          
22928         try {
22929             var p = range.commonAncestorContainer;
22930             while (p.nodeType == 3) { // text node
22931                 p = p.parentNode;
22932             }
22933             return p;
22934         } catch (e) {
22935             return null;
22936         }
22937     
22938     },
22939     /***
22940      *
22941      * Range intersection.. the hard stuff...
22942      *  '-1' = before
22943      *  '0' = hits..
22944      *  '1' = after.
22945      *         [ -- selected range --- ]
22946      *   [fail]                        [fail]
22947      *
22948      *    basically..
22949      *      if end is before start or  hits it. fail.
22950      *      if start is after end or hits it fail.
22951      *
22952      *   if either hits (but other is outside. - then it's not 
22953      *   
22954      *    
22955      **/
22956     
22957     
22958     // @see http://www.thismuchiknow.co.uk/?p=64.
22959     rangeIntersectsNode : function(range, node)
22960     {
22961         var nodeRange = node.ownerDocument.createRange();
22962         try {
22963             nodeRange.selectNode(node);
22964         } catch (e) {
22965             nodeRange.selectNodeContents(node);
22966         }
22967     
22968         var rangeStartRange = range.cloneRange();
22969         rangeStartRange.collapse(true);
22970     
22971         var rangeEndRange = range.cloneRange();
22972         rangeEndRange.collapse(false);
22973     
22974         var nodeStartRange = nodeRange.cloneRange();
22975         nodeStartRange.collapse(true);
22976     
22977         var nodeEndRange = nodeRange.cloneRange();
22978         nodeEndRange.collapse(false);
22979     
22980         return rangeStartRange.compareBoundaryPoints(
22981                  Range.START_TO_START, nodeEndRange) == -1 &&
22982                rangeEndRange.compareBoundaryPoints(
22983                  Range.START_TO_START, nodeStartRange) == 1;
22984         
22985          
22986     },
22987     rangeCompareNode : function(range, node)
22988     {
22989         var nodeRange = node.ownerDocument.createRange();
22990         try {
22991             nodeRange.selectNode(node);
22992         } catch (e) {
22993             nodeRange.selectNodeContents(node);
22994         }
22995         
22996         
22997         range.collapse(true);
22998     
22999         nodeRange.collapse(true);
23000      
23001         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23002         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
23003          
23004         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23005         
23006         var nodeIsBefore   =  ss == 1;
23007         var nodeIsAfter    = ee == -1;
23008         
23009         if (nodeIsBefore && nodeIsAfter) {
23010             return 0; // outer
23011         }
23012         if (!nodeIsBefore && nodeIsAfter) {
23013             return 1; //right trailed.
23014         }
23015         
23016         if (nodeIsBefore && !nodeIsAfter) {
23017             return 2;  // left trailed.
23018         }
23019         // fully contined.
23020         return 3;
23021     },
23022
23023     // private? - in a new class?
23024     cleanUpPaste :  function()
23025     {
23026         // cleans up the whole document..
23027         Roo.log('cleanuppaste');
23028         
23029         this.cleanUpChildren(this.doc.body);
23030         var clean = this.cleanWordChars(this.doc.body.innerHTML);
23031         if (clean != this.doc.body.innerHTML) {
23032             this.doc.body.innerHTML = clean;
23033         }
23034         
23035     },
23036     
23037     cleanWordChars : function(input) {// change the chars to hex code
23038         var he = Roo.HtmlEditorCore;
23039         
23040         var output = input;
23041         Roo.each(he.swapCodes, function(sw) { 
23042             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23043             
23044             output = output.replace(swapper, sw[1]);
23045         });
23046         
23047         return output;
23048     },
23049     
23050     
23051     cleanUpChildren : function (n)
23052     {
23053         if (!n.childNodes.length) {
23054             return;
23055         }
23056         for (var i = n.childNodes.length-1; i > -1 ; i--) {
23057            this.cleanUpChild(n.childNodes[i]);
23058         }
23059     },
23060     
23061     
23062         
23063     
23064     cleanUpChild : function (node)
23065     {
23066         var ed = this;
23067         //console.log(node);
23068         if (node.nodeName == "#text") {
23069             // clean up silly Windows -- stuff?
23070             return; 
23071         }
23072         if (node.nodeName == "#comment") {
23073             node.parentNode.removeChild(node);
23074             // clean up silly Windows -- stuff?
23075             return; 
23076         }
23077         var lcname = node.tagName.toLowerCase();
23078         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23079         // whitelist of tags..
23080         
23081         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23082             // remove node.
23083             node.parentNode.removeChild(node);
23084             return;
23085             
23086         }
23087         
23088         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23089         
23090         // spans with no attributes - just remove them..
23091         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
23092             remove_keep_children = true;
23093         }
23094         
23095         // remove <a name=....> as rendering on yahoo mailer is borked with this.
23096         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23097         
23098         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23099         //    remove_keep_children = true;
23100         //}
23101         
23102         if (remove_keep_children) {
23103             this.cleanUpChildren(node);
23104             // inserts everything just before this node...
23105             while (node.childNodes.length) {
23106                 var cn = node.childNodes[0];
23107                 node.removeChild(cn);
23108                 node.parentNode.insertBefore(cn, node);
23109             }
23110             node.parentNode.removeChild(node);
23111             return;
23112         }
23113         
23114         if (!node.attributes || !node.attributes.length) {
23115             
23116           
23117             
23118             
23119             this.cleanUpChildren(node);
23120             return;
23121         }
23122         
23123         function cleanAttr(n,v)
23124         {
23125             
23126             if (v.match(/^\./) || v.match(/^\//)) {
23127                 return;
23128             }
23129             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23130                 return;
23131             }
23132             if (v.match(/^#/)) {
23133                 return;
23134             }
23135 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23136             node.removeAttribute(n);
23137             
23138         }
23139         
23140         var cwhite = this.cwhite;
23141         var cblack = this.cblack;
23142             
23143         function cleanStyle(n,v)
23144         {
23145             if (v.match(/expression/)) { //XSS?? should we even bother..
23146                 node.removeAttribute(n);
23147                 return;
23148             }
23149             
23150             var parts = v.split(/;/);
23151             var clean = [];
23152             
23153             Roo.each(parts, function(p) {
23154                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23155                 if (!p.length) {
23156                     return true;
23157                 }
23158                 var l = p.split(':').shift().replace(/\s+/g,'');
23159                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23160                 
23161                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23162 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23163                     //node.removeAttribute(n);
23164                     return true;
23165                 }
23166                 //Roo.log()
23167                 // only allow 'c whitelisted system attributes'
23168                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23169 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23170                     //node.removeAttribute(n);
23171                     return true;
23172                 }
23173                 
23174                 
23175                  
23176                 
23177                 clean.push(p);
23178                 return true;
23179             });
23180             if (clean.length) { 
23181                 node.setAttribute(n, clean.join(';'));
23182             } else {
23183                 node.removeAttribute(n);
23184             }
23185             
23186         }
23187         
23188         
23189         for (var i = node.attributes.length-1; i > -1 ; i--) {
23190             var a = node.attributes[i];
23191             //console.log(a);
23192             
23193             if (a.name.toLowerCase().substr(0,2)=='on')  {
23194                 node.removeAttribute(a.name);
23195                 continue;
23196             }
23197             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23198                 node.removeAttribute(a.name);
23199                 continue;
23200             }
23201             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23202                 cleanAttr(a.name,a.value); // fixme..
23203                 continue;
23204             }
23205             if (a.name == 'style') {
23206                 cleanStyle(a.name,a.value);
23207                 continue;
23208             }
23209             /// clean up MS crap..
23210             // tecnically this should be a list of valid class'es..
23211             
23212             
23213             if (a.name == 'class') {
23214                 if (a.value.match(/^Mso/)) {
23215                     node.removeAttribute('class');
23216                 }
23217                 
23218                 if (a.value.match(/^body$/)) {
23219                     node.removeAttribute('class');
23220                 }
23221                 continue;
23222             }
23223             
23224             // style cleanup!?
23225             // class cleanup?
23226             
23227         }
23228         
23229         
23230         this.cleanUpChildren(node);
23231         
23232         
23233     },
23234     
23235     /**
23236      * Clean up MS wordisms...
23237      */
23238     cleanWord : function(node)
23239     {
23240         if (!node) {
23241             this.cleanWord(this.doc.body);
23242             return;
23243         }
23244         
23245         if(
23246                 node.nodeName == 'SPAN' &&
23247                 !node.hasAttributes() &&
23248                 node.childNodes.length == 1 &&
23249                 node.firstChild.nodeName == "#text"  
23250         ) {
23251             var textNode = node.firstChild;
23252             node.removeChild(textNode);
23253             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
23254                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
23255             }
23256             node.parentNode.insertBefore(textNode, node);
23257             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
23258                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
23259             }
23260             node.parentNode.removeChild(node);
23261         }
23262         
23263         if (node.nodeName == "#text") {
23264             // clean up silly Windows -- stuff?
23265             return; 
23266         }
23267         if (node.nodeName == "#comment") {
23268             node.parentNode.removeChild(node);
23269             // clean up silly Windows -- stuff?
23270             return; 
23271         }
23272         
23273         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23274             node.parentNode.removeChild(node);
23275             return;
23276         }
23277         //Roo.log(node.tagName);
23278         // remove - but keep children..
23279         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
23280             //Roo.log('-- removed');
23281             while (node.childNodes.length) {
23282                 var cn = node.childNodes[0];
23283                 node.removeChild(cn);
23284                 node.parentNode.insertBefore(cn, node);
23285                 // move node to parent - and clean it..
23286                 this.cleanWord(cn);
23287             }
23288             node.parentNode.removeChild(node);
23289             /// no need to iterate chidlren = it's got none..
23290             //this.iterateChildren(node, this.cleanWord);
23291             return;
23292         }
23293         // clean styles
23294         if (node.className.length) {
23295             
23296             var cn = node.className.split(/\W+/);
23297             var cna = [];
23298             Roo.each(cn, function(cls) {
23299                 if (cls.match(/Mso[a-zA-Z]+/)) {
23300                     return;
23301                 }
23302                 cna.push(cls);
23303             });
23304             node.className = cna.length ? cna.join(' ') : '';
23305             if (!cna.length) {
23306                 node.removeAttribute("class");
23307             }
23308         }
23309         
23310         if (node.hasAttribute("lang")) {
23311             node.removeAttribute("lang");
23312         }
23313         
23314         if (node.hasAttribute("style")) {
23315             
23316             var styles = node.getAttribute("style").split(";");
23317             var nstyle = [];
23318             Roo.each(styles, function(s) {
23319                 if (!s.match(/:/)) {
23320                     return;
23321                 }
23322                 var kv = s.split(":");
23323                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23324                     return;
23325                 }
23326                 // what ever is left... we allow.
23327                 nstyle.push(s);
23328             });
23329             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23330             if (!nstyle.length) {
23331                 node.removeAttribute('style');
23332             }
23333         }
23334         this.iterateChildren(node, this.cleanWord);
23335         
23336         
23337         
23338     },
23339     /**
23340      * iterateChildren of a Node, calling fn each time, using this as the scole..
23341      * @param {DomNode} node node to iterate children of.
23342      * @param {Function} fn method of this class to call on each item.
23343      */
23344     iterateChildren : function(node, fn)
23345     {
23346         if (!node.childNodes.length) {
23347                 return;
23348         }
23349         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23350            fn.call(this, node.childNodes[i])
23351         }
23352     },
23353     
23354     
23355     /**
23356      * cleanTableWidths.
23357      *
23358      * Quite often pasting from word etc.. results in tables with column and widths.
23359      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23360      *
23361      */
23362     cleanTableWidths : function(node)
23363     {
23364          
23365          
23366         if (!node) {
23367             this.cleanTableWidths(this.doc.body);
23368             return;
23369         }
23370         
23371         // ignore list...
23372         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23373             return; 
23374         }
23375         Roo.log(node.tagName);
23376         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23377             this.iterateChildren(node, this.cleanTableWidths);
23378             return;
23379         }
23380         if (node.hasAttribute('width')) {
23381             node.removeAttribute('width');
23382         }
23383         
23384          
23385         if (node.hasAttribute("style")) {
23386             // pretty basic...
23387             
23388             var styles = node.getAttribute("style").split(";");
23389             var nstyle = [];
23390             Roo.each(styles, function(s) {
23391                 if (!s.match(/:/)) {
23392                     return;
23393                 }
23394                 var kv = s.split(":");
23395                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23396                     return;
23397                 }
23398                 // what ever is left... we allow.
23399                 nstyle.push(s);
23400             });
23401             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23402             if (!nstyle.length) {
23403                 node.removeAttribute('style');
23404             }
23405         }
23406         
23407         this.iterateChildren(node, this.cleanTableWidths);
23408         
23409         
23410     },
23411     
23412     
23413     
23414     
23415     domToHTML : function(currentElement, depth, nopadtext) {
23416         
23417         depth = depth || 0;
23418         nopadtext = nopadtext || false;
23419     
23420         if (!currentElement) {
23421             return this.domToHTML(this.doc.body);
23422         }
23423         
23424         //Roo.log(currentElement);
23425         var j;
23426         var allText = false;
23427         var nodeName = currentElement.nodeName;
23428         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23429         
23430         if  (nodeName == '#text') {
23431             
23432             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23433         }
23434         
23435         
23436         var ret = '';
23437         if (nodeName != 'BODY') {
23438              
23439             var i = 0;
23440             // Prints the node tagName, such as <A>, <IMG>, etc
23441             if (tagName) {
23442                 var attr = [];
23443                 for(i = 0; i < currentElement.attributes.length;i++) {
23444                     // quoting?
23445                     var aname = currentElement.attributes.item(i).name;
23446                     if (!currentElement.attributes.item(i).value.length) {
23447                         continue;
23448                     }
23449                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23450                 }
23451                 
23452                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23453             } 
23454             else {
23455                 
23456                 // eack
23457             }
23458         } else {
23459             tagName = false;
23460         }
23461         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23462             return ret;
23463         }
23464         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23465             nopadtext = true;
23466         }
23467         
23468         
23469         // Traverse the tree
23470         i = 0;
23471         var currentElementChild = currentElement.childNodes.item(i);
23472         var allText = true;
23473         var innerHTML  = '';
23474         lastnode = '';
23475         while (currentElementChild) {
23476             // Formatting code (indent the tree so it looks nice on the screen)
23477             var nopad = nopadtext;
23478             if (lastnode == 'SPAN') {
23479                 nopad  = true;
23480             }
23481             // text
23482             if  (currentElementChild.nodeName == '#text') {
23483                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23484                 toadd = nopadtext ? toadd : toadd.trim();
23485                 if (!nopad && toadd.length > 80) {
23486                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23487                 }
23488                 innerHTML  += toadd;
23489                 
23490                 i++;
23491                 currentElementChild = currentElement.childNodes.item(i);
23492                 lastNode = '';
23493                 continue;
23494             }
23495             allText = false;
23496             
23497             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23498                 
23499             // Recursively traverse the tree structure of the child node
23500             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23501             lastnode = currentElementChild.nodeName;
23502             i++;
23503             currentElementChild=currentElement.childNodes.item(i);
23504         }
23505         
23506         ret += innerHTML;
23507         
23508         if (!allText) {
23509                 // The remaining code is mostly for formatting the tree
23510             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23511         }
23512         
23513         
23514         if (tagName) {
23515             ret+= "</"+tagName+">";
23516         }
23517         return ret;
23518         
23519     },
23520         
23521     applyBlacklists : function()
23522     {
23523         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23524         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23525         
23526         this.white = [];
23527         this.black = [];
23528         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23529             if (b.indexOf(tag) > -1) {
23530                 return;
23531             }
23532             this.white.push(tag);
23533             
23534         }, this);
23535         
23536         Roo.each(w, function(tag) {
23537             if (b.indexOf(tag) > -1) {
23538                 return;
23539             }
23540             if (this.white.indexOf(tag) > -1) {
23541                 return;
23542             }
23543             this.white.push(tag);
23544             
23545         }, this);
23546         
23547         
23548         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23549             if (w.indexOf(tag) > -1) {
23550                 return;
23551             }
23552             this.black.push(tag);
23553             
23554         }, this);
23555         
23556         Roo.each(b, function(tag) {
23557             if (w.indexOf(tag) > -1) {
23558                 return;
23559             }
23560             if (this.black.indexOf(tag) > -1) {
23561                 return;
23562             }
23563             this.black.push(tag);
23564             
23565         }, this);
23566         
23567         
23568         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23569         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23570         
23571         this.cwhite = [];
23572         this.cblack = [];
23573         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23574             if (b.indexOf(tag) > -1) {
23575                 return;
23576             }
23577             this.cwhite.push(tag);
23578             
23579         }, this);
23580         
23581         Roo.each(w, function(tag) {
23582             if (b.indexOf(tag) > -1) {
23583                 return;
23584             }
23585             if (this.cwhite.indexOf(tag) > -1) {
23586                 return;
23587             }
23588             this.cwhite.push(tag);
23589             
23590         }, this);
23591         
23592         
23593         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23594             if (w.indexOf(tag) > -1) {
23595                 return;
23596             }
23597             this.cblack.push(tag);
23598             
23599         }, this);
23600         
23601         Roo.each(b, function(tag) {
23602             if (w.indexOf(tag) > -1) {
23603                 return;
23604             }
23605             if (this.cblack.indexOf(tag) > -1) {
23606                 return;
23607             }
23608             this.cblack.push(tag);
23609             
23610         }, this);
23611     },
23612     
23613     setStylesheets : function(stylesheets)
23614     {
23615         if(typeof(stylesheets) == 'string'){
23616             Roo.get(this.iframe.contentDocument.head).createChild({
23617                 tag : 'link',
23618                 rel : 'stylesheet',
23619                 type : 'text/css',
23620                 href : stylesheets
23621             });
23622             
23623             return;
23624         }
23625         var _this = this;
23626      
23627         Roo.each(stylesheets, function(s) {
23628             if(!s.length){
23629                 return;
23630             }
23631             
23632             Roo.get(_this.iframe.contentDocument.head).createChild({
23633                 tag : 'link',
23634                 rel : 'stylesheet',
23635                 type : 'text/css',
23636                 href : s
23637             });
23638         });
23639
23640         
23641     },
23642     
23643     removeStylesheets : function()
23644     {
23645         var _this = this;
23646         
23647         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23648             s.remove();
23649         });
23650     },
23651     
23652     setStyle : function(style)
23653     {
23654         Roo.get(this.iframe.contentDocument.head).createChild({
23655             tag : 'style',
23656             type : 'text/css',
23657             html : style
23658         });
23659
23660         return;
23661     }
23662     
23663     // hide stuff that is not compatible
23664     /**
23665      * @event blur
23666      * @hide
23667      */
23668     /**
23669      * @event change
23670      * @hide
23671      */
23672     /**
23673      * @event focus
23674      * @hide
23675      */
23676     /**
23677      * @event specialkey
23678      * @hide
23679      */
23680     /**
23681      * @cfg {String} fieldClass @hide
23682      */
23683     /**
23684      * @cfg {String} focusClass @hide
23685      */
23686     /**
23687      * @cfg {String} autoCreate @hide
23688      */
23689     /**
23690      * @cfg {String} inputType @hide
23691      */
23692     /**
23693      * @cfg {String} invalidClass @hide
23694      */
23695     /**
23696      * @cfg {String} invalidText @hide
23697      */
23698     /**
23699      * @cfg {String} msgFx @hide
23700      */
23701     /**
23702      * @cfg {String} validateOnBlur @hide
23703      */
23704 });
23705
23706 Roo.HtmlEditorCore.white = [
23707         'area', 'br', 'img', 'input', 'hr', 'wbr',
23708         
23709        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23710        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23711        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23712        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23713        'table',   'ul',         'xmp', 
23714        
23715        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23716       'thead',   'tr', 
23717      
23718       'dir', 'menu', 'ol', 'ul', 'dl',
23719        
23720       'embed',  'object'
23721 ];
23722
23723
23724 Roo.HtmlEditorCore.black = [
23725     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23726         'applet', // 
23727         'base',   'basefont', 'bgsound', 'blink',  'body', 
23728         'frame',  'frameset', 'head',    'html',   'ilayer', 
23729         'iframe', 'layer',  'link',     'meta',    'object',   
23730         'script', 'style' ,'title',  'xml' // clean later..
23731 ];
23732 Roo.HtmlEditorCore.clean = [
23733     'script', 'style', 'title', 'xml'
23734 ];
23735 Roo.HtmlEditorCore.remove = [
23736     'font'
23737 ];
23738 // attributes..
23739
23740 Roo.HtmlEditorCore.ablack = [
23741     'on'
23742 ];
23743     
23744 Roo.HtmlEditorCore.aclean = [ 
23745     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23746 ];
23747
23748 // protocols..
23749 Roo.HtmlEditorCore.pwhite= [
23750         'http',  'https',  'mailto'
23751 ];
23752
23753 // white listed style attributes.
23754 Roo.HtmlEditorCore.cwhite= [
23755       //  'text-align', /// default is to allow most things..
23756       
23757          
23758 //        'font-size'//??
23759 ];
23760
23761 // black listed style attributes.
23762 Roo.HtmlEditorCore.cblack= [
23763       //  'font-size' -- this can be set by the project 
23764 ];
23765
23766
23767 Roo.HtmlEditorCore.swapCodes   =[ 
23768     [    8211, "--" ], 
23769     [    8212, "--" ], 
23770     [    8216,  "'" ],  
23771     [    8217, "'" ],  
23772     [    8220, '"' ],  
23773     [    8221, '"' ],  
23774     [    8226, "*" ],  
23775     [    8230, "..." ]
23776 ]; 
23777
23778     /*
23779  * - LGPL
23780  *
23781  * HtmlEditor
23782  * 
23783  */
23784
23785 /**
23786  * @class Roo.bootstrap.HtmlEditor
23787  * @extends Roo.bootstrap.TextArea
23788  * Bootstrap HtmlEditor class
23789
23790  * @constructor
23791  * Create a new HtmlEditor
23792  * @param {Object} config The config object
23793  */
23794
23795 Roo.bootstrap.HtmlEditor = function(config){
23796     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23797     if (!this.toolbars) {
23798         this.toolbars = [];
23799     }
23800     
23801     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23802     this.addEvents({
23803             /**
23804              * @event initialize
23805              * Fires when the editor is fully initialized (including the iframe)
23806              * @param {HtmlEditor} this
23807              */
23808             initialize: true,
23809             /**
23810              * @event activate
23811              * Fires when the editor is first receives the focus. Any insertion must wait
23812              * until after this event.
23813              * @param {HtmlEditor} this
23814              */
23815             activate: true,
23816              /**
23817              * @event beforesync
23818              * Fires before the textarea is updated with content from the editor iframe. Return false
23819              * to cancel the sync.
23820              * @param {HtmlEditor} this
23821              * @param {String} html
23822              */
23823             beforesync: true,
23824              /**
23825              * @event beforepush
23826              * Fires before the iframe editor is updated with content from the textarea. Return false
23827              * to cancel the push.
23828              * @param {HtmlEditor} this
23829              * @param {String} html
23830              */
23831             beforepush: true,
23832              /**
23833              * @event sync
23834              * Fires when the textarea is updated with content from the editor iframe.
23835              * @param {HtmlEditor} this
23836              * @param {String} html
23837              */
23838             sync: true,
23839              /**
23840              * @event push
23841              * Fires when the iframe editor is updated with content from the textarea.
23842              * @param {HtmlEditor} this
23843              * @param {String} html
23844              */
23845             push: true,
23846              /**
23847              * @event editmodechange
23848              * Fires when the editor switches edit modes
23849              * @param {HtmlEditor} this
23850              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23851              */
23852             editmodechange: true,
23853             /**
23854              * @event editorevent
23855              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23856              * @param {HtmlEditor} this
23857              */
23858             editorevent: true,
23859             /**
23860              * @event firstfocus
23861              * Fires when on first focus - needed by toolbars..
23862              * @param {HtmlEditor} this
23863              */
23864             firstfocus: true,
23865             /**
23866              * @event autosave
23867              * Auto save the htmlEditor value as a file into Events
23868              * @param {HtmlEditor} this
23869              */
23870             autosave: true,
23871             /**
23872              * @event savedpreview
23873              * preview the saved version of htmlEditor
23874              * @param {HtmlEditor} this
23875              */
23876             savedpreview: true
23877         });
23878 };
23879
23880
23881 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23882     
23883     
23884       /**
23885      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23886      */
23887     toolbars : false,
23888     
23889      /**
23890     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23891     */
23892     btns : [],
23893    
23894      /**
23895      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23896      *                        Roo.resizable.
23897      */
23898     resizable : false,
23899      /**
23900      * @cfg {Number} height (in pixels)
23901      */   
23902     height: 300,
23903    /**
23904      * @cfg {Number} width (in pixels)
23905      */   
23906     width: false,
23907     
23908     /**
23909      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23910      * 
23911      */
23912     stylesheets: false,
23913     
23914     // id of frame..
23915     frameId: false,
23916     
23917     // private properties
23918     validationEvent : false,
23919     deferHeight: true,
23920     initialized : false,
23921     activated : false,
23922     
23923     onFocus : Roo.emptyFn,
23924     iframePad:3,
23925     hideMode:'offsets',
23926     
23927     tbContainer : false,
23928     
23929     bodyCls : '',
23930     
23931     toolbarContainer :function() {
23932         return this.wrap.select('.x-html-editor-tb',true).first();
23933     },
23934
23935     /**
23936      * Protected method that will not generally be called directly. It
23937      * is called when the editor creates its toolbar. Override this method if you need to
23938      * add custom toolbar buttons.
23939      * @param {HtmlEditor} editor
23940      */
23941     createToolbar : function(){
23942         Roo.log('renewing');
23943         Roo.log("create toolbars");
23944         
23945         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23946         this.toolbars[0].render(this.toolbarContainer());
23947         
23948         return;
23949         
23950 //        if (!editor.toolbars || !editor.toolbars.length) {
23951 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23952 //        }
23953 //        
23954 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23955 //            editor.toolbars[i] = Roo.factory(
23956 //                    typeof(editor.toolbars[i]) == 'string' ?
23957 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23958 //                Roo.bootstrap.HtmlEditor);
23959 //            editor.toolbars[i].init(editor);
23960 //        }
23961     },
23962
23963      
23964     // private
23965     onRender : function(ct, position)
23966     {
23967        // Roo.log("Call onRender: " + this.xtype);
23968         var _t = this;
23969         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23970       
23971         this.wrap = this.inputEl().wrap({
23972             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23973         });
23974         
23975         this.editorcore.onRender(ct, position);
23976          
23977         if (this.resizable) {
23978             this.resizeEl = new Roo.Resizable(this.wrap, {
23979                 pinned : true,
23980                 wrap: true,
23981                 dynamic : true,
23982                 minHeight : this.height,
23983                 height: this.height,
23984                 handles : this.resizable,
23985                 width: this.width,
23986                 listeners : {
23987                     resize : function(r, w, h) {
23988                         _t.onResize(w,h); // -something
23989                     }
23990                 }
23991             });
23992             
23993         }
23994         this.createToolbar(this);
23995        
23996         
23997         if(!this.width && this.resizable){
23998             this.setSize(this.wrap.getSize());
23999         }
24000         if (this.resizeEl) {
24001             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24002             // should trigger onReize..
24003         }
24004         
24005     },
24006
24007     // private
24008     onResize : function(w, h)
24009     {
24010         Roo.log('resize: ' +w + ',' + h );
24011         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24012         var ew = false;
24013         var eh = false;
24014         
24015         if(this.inputEl() ){
24016             if(typeof w == 'number'){
24017                 var aw = w - this.wrap.getFrameWidth('lr');
24018                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24019                 ew = aw;
24020             }
24021             if(typeof h == 'number'){
24022                  var tbh = -11;  // fixme it needs to tool bar size!
24023                 for (var i =0; i < this.toolbars.length;i++) {
24024                     // fixme - ask toolbars for heights?
24025                     tbh += this.toolbars[i].el.getHeight();
24026                     //if (this.toolbars[i].footer) {
24027                     //    tbh += this.toolbars[i].footer.el.getHeight();
24028                     //}
24029                 }
24030               
24031                 
24032                 
24033                 
24034                 
24035                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24036                 ah -= 5; // knock a few pixes off for look..
24037                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24038                 var eh = ah;
24039             }
24040         }
24041         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24042         this.editorcore.onResize(ew,eh);
24043         
24044     },
24045
24046     /**
24047      * Toggles the editor between standard and source edit mode.
24048      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24049      */
24050     toggleSourceEdit : function(sourceEditMode)
24051     {
24052         this.editorcore.toggleSourceEdit(sourceEditMode);
24053         
24054         if(this.editorcore.sourceEditMode){
24055             Roo.log('editor - showing textarea');
24056             
24057 //            Roo.log('in');
24058 //            Roo.log(this.syncValue());
24059             this.syncValue();
24060             this.inputEl().removeClass(['hide', 'x-hidden']);
24061             this.inputEl().dom.removeAttribute('tabIndex');
24062             this.inputEl().focus();
24063         }else{
24064             Roo.log('editor - hiding textarea');
24065 //            Roo.log('out')
24066 //            Roo.log(this.pushValue()); 
24067             this.pushValue();
24068             
24069             this.inputEl().addClass(['hide', 'x-hidden']);
24070             this.inputEl().dom.setAttribute('tabIndex', -1);
24071             //this.deferFocus();
24072         }
24073          
24074         if(this.resizable){
24075             this.setSize(this.wrap.getSize());
24076         }
24077         
24078         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24079     },
24080  
24081     // private (for BoxComponent)
24082     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24083
24084     // private (for BoxComponent)
24085     getResizeEl : function(){
24086         return this.wrap;
24087     },
24088
24089     // private (for BoxComponent)
24090     getPositionEl : function(){
24091         return this.wrap;
24092     },
24093
24094     // private
24095     initEvents : function(){
24096         this.originalValue = this.getValue();
24097     },
24098
24099 //    /**
24100 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24101 //     * @method
24102 //     */
24103 //    markInvalid : Roo.emptyFn,
24104 //    /**
24105 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24106 //     * @method
24107 //     */
24108 //    clearInvalid : Roo.emptyFn,
24109
24110     setValue : function(v){
24111         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24112         this.editorcore.pushValue();
24113     },
24114
24115      
24116     // private
24117     deferFocus : function(){
24118         this.focus.defer(10, this);
24119     },
24120
24121     // doc'ed in Field
24122     focus : function(){
24123         this.editorcore.focus();
24124         
24125     },
24126       
24127
24128     // private
24129     onDestroy : function(){
24130         
24131         
24132         
24133         if(this.rendered){
24134             
24135             for (var i =0; i < this.toolbars.length;i++) {
24136                 // fixme - ask toolbars for heights?
24137                 this.toolbars[i].onDestroy();
24138             }
24139             
24140             this.wrap.dom.innerHTML = '';
24141             this.wrap.remove();
24142         }
24143     },
24144
24145     // private
24146     onFirstFocus : function(){
24147         //Roo.log("onFirstFocus");
24148         this.editorcore.onFirstFocus();
24149          for (var i =0; i < this.toolbars.length;i++) {
24150             this.toolbars[i].onFirstFocus();
24151         }
24152         
24153     },
24154     
24155     // private
24156     syncValue : function()
24157     {   
24158         this.editorcore.syncValue();
24159     },
24160     
24161     pushValue : function()
24162     {   
24163         this.editorcore.pushValue();
24164     }
24165      
24166     
24167     // hide stuff that is not compatible
24168     /**
24169      * @event blur
24170      * @hide
24171      */
24172     /**
24173      * @event change
24174      * @hide
24175      */
24176     /**
24177      * @event focus
24178      * @hide
24179      */
24180     /**
24181      * @event specialkey
24182      * @hide
24183      */
24184     /**
24185      * @cfg {String} fieldClass @hide
24186      */
24187     /**
24188      * @cfg {String} focusClass @hide
24189      */
24190     /**
24191      * @cfg {String} autoCreate @hide
24192      */
24193     /**
24194      * @cfg {String} inputType @hide
24195      */
24196      
24197     /**
24198      * @cfg {String} invalidText @hide
24199      */
24200     /**
24201      * @cfg {String} msgFx @hide
24202      */
24203     /**
24204      * @cfg {String} validateOnBlur @hide
24205      */
24206 });
24207  
24208     
24209    
24210    
24211    
24212       
24213 Roo.namespace('Roo.bootstrap.htmleditor');
24214 /**
24215  * @class Roo.bootstrap.HtmlEditorToolbar1
24216  * Basic Toolbar
24217  * 
24218  * @example
24219  * Usage:
24220  *
24221  new Roo.bootstrap.HtmlEditor({
24222     ....
24223     toolbars : [
24224         new Roo.bootstrap.HtmlEditorToolbar1({
24225             disable : { fonts: 1 , format: 1, ..., ... , ...],
24226             btns : [ .... ]
24227         })
24228     }
24229      
24230  * 
24231  * @cfg {Object} disable List of elements to disable..
24232  * @cfg {Array} btns List of additional buttons.
24233  * 
24234  * 
24235  * NEEDS Extra CSS? 
24236  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24237  */
24238  
24239 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24240 {
24241     
24242     Roo.apply(this, config);
24243     
24244     // default disabled, based on 'good practice'..
24245     this.disable = this.disable || {};
24246     Roo.applyIf(this.disable, {
24247         fontSize : true,
24248         colors : true,
24249         specialElements : true
24250     });
24251     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24252     
24253     this.editor = config.editor;
24254     this.editorcore = config.editor.editorcore;
24255     
24256     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24257     
24258     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24259     // dont call parent... till later.
24260 }
24261 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24262      
24263     bar : true,
24264     
24265     editor : false,
24266     editorcore : false,
24267     
24268     
24269     formats : [
24270         "p" ,  
24271         "h1","h2","h3","h4","h5","h6", 
24272         "pre", "code", 
24273         "abbr", "acronym", "address", "cite", "samp", "var",
24274         'div','span'
24275     ],
24276     
24277     onRender : function(ct, position)
24278     {
24279        // Roo.log("Call onRender: " + this.xtype);
24280         
24281        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24282        Roo.log(this.el);
24283        this.el.dom.style.marginBottom = '0';
24284        var _this = this;
24285        var editorcore = this.editorcore;
24286        var editor= this.editor;
24287        
24288        var children = [];
24289        var btn = function(id,cmd , toggle, handler, html){
24290        
24291             var  event = toggle ? 'toggle' : 'click';
24292        
24293             var a = {
24294                 size : 'sm',
24295                 xtype: 'Button',
24296                 xns: Roo.bootstrap,
24297                 //glyphicon : id,
24298                 fa: id,
24299                 cmd : id || cmd,
24300                 enableToggle:toggle !== false,
24301                 html : html || '',
24302                 pressed : toggle ? false : null,
24303                 listeners : {}
24304             };
24305             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24306                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24307             };
24308             children.push(a);
24309             return a;
24310        }
24311        
24312     //    var cb_box = function...
24313         
24314         var style = {
24315                 xtype: 'Button',
24316                 size : 'sm',
24317                 xns: Roo.bootstrap,
24318                 fa : 'font',
24319                 //html : 'submit'
24320                 menu : {
24321                     xtype: 'Menu',
24322                     xns: Roo.bootstrap,
24323                     items:  []
24324                 }
24325         };
24326         Roo.each(this.formats, function(f) {
24327             style.menu.items.push({
24328                 xtype :'MenuItem',
24329                 xns: Roo.bootstrap,
24330                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24331                 tagname : f,
24332                 listeners : {
24333                     click : function()
24334                     {
24335                         editorcore.insertTag(this.tagname);
24336                         editor.focus();
24337                     }
24338                 }
24339                 
24340             });
24341         });
24342         children.push(style);   
24343         
24344         btn('bold',false,true);
24345         btn('italic',false,true);
24346         btn('align-left', 'justifyleft',true);
24347         btn('align-center', 'justifycenter',true);
24348         btn('align-right' , 'justifyright',true);
24349         btn('link', false, false, function(btn) {
24350             //Roo.log("create link?");
24351             var url = prompt(this.createLinkText, this.defaultLinkValue);
24352             if(url && url != 'http:/'+'/'){
24353                 this.editorcore.relayCmd('createlink', url);
24354             }
24355         }),
24356         btn('list','insertunorderedlist',true);
24357         btn('pencil', false,true, function(btn){
24358                 Roo.log(this);
24359                 this.toggleSourceEdit(btn.pressed);
24360         });
24361         
24362         if (this.editor.btns.length > 0) {
24363             for (var i = 0; i<this.editor.btns.length; i++) {
24364                 children.push(this.editor.btns[i]);
24365             }
24366         }
24367         
24368         /*
24369         var cog = {
24370                 xtype: 'Button',
24371                 size : 'sm',
24372                 xns: Roo.bootstrap,
24373                 glyphicon : 'cog',
24374                 //html : 'submit'
24375                 menu : {
24376                     xtype: 'Menu',
24377                     xns: Roo.bootstrap,
24378                     items:  []
24379                 }
24380         };
24381         
24382         cog.menu.items.push({
24383             xtype :'MenuItem',
24384             xns: Roo.bootstrap,
24385             html : Clean styles,
24386             tagname : f,
24387             listeners : {
24388                 click : function()
24389                 {
24390                     editorcore.insertTag(this.tagname);
24391                     editor.focus();
24392                 }
24393             }
24394             
24395         });
24396        */
24397         
24398          
24399        this.xtype = 'NavSimplebar';
24400         
24401         for(var i=0;i< children.length;i++) {
24402             
24403             this.buttons.add(this.addxtypeChild(children[i]));
24404             
24405         }
24406         
24407         editor.on('editorevent', this.updateToolbar, this);
24408     },
24409     onBtnClick : function(id)
24410     {
24411        this.editorcore.relayCmd(id);
24412        this.editorcore.focus();
24413     },
24414     
24415     /**
24416      * Protected method that will not generally be called directly. It triggers
24417      * a toolbar update by reading the markup state of the current selection in the editor.
24418      */
24419     updateToolbar: function(){
24420
24421         if(!this.editorcore.activated){
24422             this.editor.onFirstFocus(); // is this neeed?
24423             return;
24424         }
24425
24426         var btns = this.buttons; 
24427         var doc = this.editorcore.doc;
24428         btns.get('bold').setActive(doc.queryCommandState('bold'));
24429         btns.get('italic').setActive(doc.queryCommandState('italic'));
24430         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24431         
24432         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24433         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24434         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24435         
24436         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24437         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24438          /*
24439         
24440         var ans = this.editorcore.getAllAncestors();
24441         if (this.formatCombo) {
24442             
24443             
24444             var store = this.formatCombo.store;
24445             this.formatCombo.setValue("");
24446             for (var i =0; i < ans.length;i++) {
24447                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24448                     // select it..
24449                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24450                     break;
24451                 }
24452             }
24453         }
24454         
24455         
24456         
24457         // hides menus... - so this cant be on a menu...
24458         Roo.bootstrap.MenuMgr.hideAll();
24459         */
24460         Roo.bootstrap.MenuMgr.hideAll();
24461         //this.editorsyncValue();
24462     },
24463     onFirstFocus: function() {
24464         this.buttons.each(function(item){
24465            item.enable();
24466         });
24467     },
24468     toggleSourceEdit : function(sourceEditMode){
24469         
24470           
24471         if(sourceEditMode){
24472             Roo.log("disabling buttons");
24473            this.buttons.each( function(item){
24474                 if(item.cmd != 'pencil'){
24475                     item.disable();
24476                 }
24477             });
24478           
24479         }else{
24480             Roo.log("enabling buttons");
24481             if(this.editorcore.initialized){
24482                 this.buttons.each( function(item){
24483                     item.enable();
24484                 });
24485             }
24486             
24487         }
24488         Roo.log("calling toggole on editor");
24489         // tell the editor that it's been pressed..
24490         this.editor.toggleSourceEdit(sourceEditMode);
24491        
24492     }
24493 });
24494
24495
24496
24497
24498
24499 /**
24500  * @class Roo.bootstrap.Table.AbstractSelectionModel
24501  * @extends Roo.util.Observable
24502  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24503  * implemented by descendant classes.  This class should not be directly instantiated.
24504  * @constructor
24505  */
24506 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24507     this.locked = false;
24508     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24509 };
24510
24511
24512 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24513     /** @ignore Called by the grid automatically. Do not call directly. */
24514     init : function(grid){
24515         this.grid = grid;
24516         this.initEvents();
24517     },
24518
24519     /**
24520      * Locks the selections.
24521      */
24522     lock : function(){
24523         this.locked = true;
24524     },
24525
24526     /**
24527      * Unlocks the selections.
24528      */
24529     unlock : function(){
24530         this.locked = false;
24531     },
24532
24533     /**
24534      * Returns true if the selections are locked.
24535      * @return {Boolean}
24536      */
24537     isLocked : function(){
24538         return this.locked;
24539     }
24540 });
24541 /**
24542  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24543  * @class Roo.bootstrap.Table.RowSelectionModel
24544  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24545  * It supports multiple selections and keyboard selection/navigation. 
24546  * @constructor
24547  * @param {Object} config
24548  */
24549
24550 Roo.bootstrap.Table.RowSelectionModel = function(config){
24551     Roo.apply(this, config);
24552     this.selections = new Roo.util.MixedCollection(false, function(o){
24553         return o.id;
24554     });
24555
24556     this.last = false;
24557     this.lastActive = false;
24558
24559     this.addEvents({
24560         /**
24561              * @event selectionchange
24562              * Fires when the selection changes
24563              * @param {SelectionModel} this
24564              */
24565             "selectionchange" : true,
24566         /**
24567              * @event afterselectionchange
24568              * Fires after the selection changes (eg. by key press or clicking)
24569              * @param {SelectionModel} this
24570              */
24571             "afterselectionchange" : true,
24572         /**
24573              * @event beforerowselect
24574              * Fires when a row is selected being selected, return false to cancel.
24575              * @param {SelectionModel} this
24576              * @param {Number} rowIndex The selected index
24577              * @param {Boolean} keepExisting False if other selections will be cleared
24578              */
24579             "beforerowselect" : true,
24580         /**
24581              * @event rowselect
24582              * Fires when a row is selected.
24583              * @param {SelectionModel} this
24584              * @param {Number} rowIndex The selected index
24585              * @param {Roo.data.Record} r The record
24586              */
24587             "rowselect" : true,
24588         /**
24589              * @event rowdeselect
24590              * Fires when a row is deselected.
24591              * @param {SelectionModel} this
24592              * @param {Number} rowIndex The selected index
24593              */
24594         "rowdeselect" : true
24595     });
24596     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24597     this.locked = false;
24598  };
24599
24600 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24601     /**
24602      * @cfg {Boolean} singleSelect
24603      * True to allow selection of only one row at a time (defaults to false)
24604      */
24605     singleSelect : false,
24606
24607     // private
24608     initEvents : function()
24609     {
24610
24611         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24612         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24613         //}else{ // allow click to work like normal
24614          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24615         //}
24616         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24617         this.grid.on("rowclick", this.handleMouseDown, this);
24618         
24619         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24620             "up" : function(e){
24621                 if(!e.shiftKey){
24622                     this.selectPrevious(e.shiftKey);
24623                 }else if(this.last !== false && this.lastActive !== false){
24624                     var last = this.last;
24625                     this.selectRange(this.last,  this.lastActive-1);
24626                     this.grid.getView().focusRow(this.lastActive);
24627                     if(last !== false){
24628                         this.last = last;
24629                     }
24630                 }else{
24631                     this.selectFirstRow();
24632                 }
24633                 this.fireEvent("afterselectionchange", this);
24634             },
24635             "down" : function(e){
24636                 if(!e.shiftKey){
24637                     this.selectNext(e.shiftKey);
24638                 }else if(this.last !== false && this.lastActive !== false){
24639                     var last = this.last;
24640                     this.selectRange(this.last,  this.lastActive+1);
24641                     this.grid.getView().focusRow(this.lastActive);
24642                     if(last !== false){
24643                         this.last = last;
24644                     }
24645                 }else{
24646                     this.selectFirstRow();
24647                 }
24648                 this.fireEvent("afterselectionchange", this);
24649             },
24650             scope: this
24651         });
24652         this.grid.store.on('load', function(){
24653             this.selections.clear();
24654         },this);
24655         /*
24656         var view = this.grid.view;
24657         view.on("refresh", this.onRefresh, this);
24658         view.on("rowupdated", this.onRowUpdated, this);
24659         view.on("rowremoved", this.onRemove, this);
24660         */
24661     },
24662
24663     // private
24664     onRefresh : function()
24665     {
24666         var ds = this.grid.store, i, v = this.grid.view;
24667         var s = this.selections;
24668         s.each(function(r){
24669             if((i = ds.indexOfId(r.id)) != -1){
24670                 v.onRowSelect(i);
24671             }else{
24672                 s.remove(r);
24673             }
24674         });
24675     },
24676
24677     // private
24678     onRemove : function(v, index, r){
24679         this.selections.remove(r);
24680     },
24681
24682     // private
24683     onRowUpdated : function(v, index, r){
24684         if(this.isSelected(r)){
24685             v.onRowSelect(index);
24686         }
24687     },
24688
24689     /**
24690      * Select records.
24691      * @param {Array} records The records to select
24692      * @param {Boolean} keepExisting (optional) True to keep existing selections
24693      */
24694     selectRecords : function(records, keepExisting)
24695     {
24696         if(!keepExisting){
24697             this.clearSelections();
24698         }
24699             var ds = this.grid.store;
24700         for(var i = 0, len = records.length; i < len; i++){
24701             this.selectRow(ds.indexOf(records[i]), true);
24702         }
24703     },
24704
24705     /**
24706      * Gets the number of selected rows.
24707      * @return {Number}
24708      */
24709     getCount : function(){
24710         return this.selections.length;
24711     },
24712
24713     /**
24714      * Selects the first row in the grid.
24715      */
24716     selectFirstRow : function(){
24717         this.selectRow(0);
24718     },
24719
24720     /**
24721      * Select the last row.
24722      * @param {Boolean} keepExisting (optional) True to keep existing selections
24723      */
24724     selectLastRow : function(keepExisting){
24725         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24726         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24727     },
24728
24729     /**
24730      * Selects the row immediately following the last selected row.
24731      * @param {Boolean} keepExisting (optional) True to keep existing selections
24732      */
24733     selectNext : function(keepExisting)
24734     {
24735             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24736             this.selectRow(this.last+1, keepExisting);
24737             this.grid.getView().focusRow(this.last);
24738         }
24739     },
24740
24741     /**
24742      * Selects the row that precedes the last selected row.
24743      * @param {Boolean} keepExisting (optional) True to keep existing selections
24744      */
24745     selectPrevious : function(keepExisting){
24746         if(this.last){
24747             this.selectRow(this.last-1, keepExisting);
24748             this.grid.getView().focusRow(this.last);
24749         }
24750     },
24751
24752     /**
24753      * Returns the selected records
24754      * @return {Array} Array of selected records
24755      */
24756     getSelections : function(){
24757         return [].concat(this.selections.items);
24758     },
24759
24760     /**
24761      * Returns the first selected record.
24762      * @return {Record}
24763      */
24764     getSelected : function(){
24765         return this.selections.itemAt(0);
24766     },
24767
24768
24769     /**
24770      * Clears all selections.
24771      */
24772     clearSelections : function(fast)
24773     {
24774         if(this.locked) {
24775             return;
24776         }
24777         if(fast !== true){
24778                 var ds = this.grid.store;
24779             var s = this.selections;
24780             s.each(function(r){
24781                 this.deselectRow(ds.indexOfId(r.id));
24782             }, this);
24783             s.clear();
24784         }else{
24785             this.selections.clear();
24786         }
24787         this.last = false;
24788     },
24789
24790
24791     /**
24792      * Selects all rows.
24793      */
24794     selectAll : function(){
24795         if(this.locked) {
24796             return;
24797         }
24798         this.selections.clear();
24799         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24800             this.selectRow(i, true);
24801         }
24802     },
24803
24804     /**
24805      * Returns True if there is a selection.
24806      * @return {Boolean}
24807      */
24808     hasSelection : function(){
24809         return this.selections.length > 0;
24810     },
24811
24812     /**
24813      * Returns True if the specified row is selected.
24814      * @param {Number/Record} record The record or index of the record to check
24815      * @return {Boolean}
24816      */
24817     isSelected : function(index){
24818             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24819         return (r && this.selections.key(r.id) ? true : false);
24820     },
24821
24822     /**
24823      * Returns True if the specified record id is selected.
24824      * @param {String} id The id of record to check
24825      * @return {Boolean}
24826      */
24827     isIdSelected : function(id){
24828         return (this.selections.key(id) ? true : false);
24829     },
24830
24831
24832     // private
24833     handleMouseDBClick : function(e, t){
24834         
24835     },
24836     // private
24837     handleMouseDown : function(e, t)
24838     {
24839             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24840         if(this.isLocked() || rowIndex < 0 ){
24841             return;
24842         };
24843         if(e.shiftKey && this.last !== false){
24844             var last = this.last;
24845             this.selectRange(last, rowIndex, e.ctrlKey);
24846             this.last = last; // reset the last
24847             t.focus();
24848     
24849         }else{
24850             var isSelected = this.isSelected(rowIndex);
24851             //Roo.log("select row:" + rowIndex);
24852             if(isSelected){
24853                 this.deselectRow(rowIndex);
24854             } else {
24855                         this.selectRow(rowIndex, true);
24856             }
24857     
24858             /*
24859                 if(e.button !== 0 && isSelected){
24860                 alert('rowIndex 2: ' + rowIndex);
24861                     view.focusRow(rowIndex);
24862                 }else if(e.ctrlKey && isSelected){
24863                     this.deselectRow(rowIndex);
24864                 }else if(!isSelected){
24865                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24866                     view.focusRow(rowIndex);
24867                 }
24868             */
24869         }
24870         this.fireEvent("afterselectionchange", this);
24871     },
24872     // private
24873     handleDragableRowClick :  function(grid, rowIndex, e) 
24874     {
24875         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24876             this.selectRow(rowIndex, false);
24877             grid.view.focusRow(rowIndex);
24878              this.fireEvent("afterselectionchange", this);
24879         }
24880     },
24881     
24882     /**
24883      * Selects multiple rows.
24884      * @param {Array} rows Array of the indexes of the row to select
24885      * @param {Boolean} keepExisting (optional) True to keep existing selections
24886      */
24887     selectRows : function(rows, keepExisting){
24888         if(!keepExisting){
24889             this.clearSelections();
24890         }
24891         for(var i = 0, len = rows.length; i < len; i++){
24892             this.selectRow(rows[i], true);
24893         }
24894     },
24895
24896     /**
24897      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24898      * @param {Number} startRow The index of the first row in the range
24899      * @param {Number} endRow The index of the last row in the range
24900      * @param {Boolean} keepExisting (optional) True to retain existing selections
24901      */
24902     selectRange : function(startRow, endRow, keepExisting){
24903         if(this.locked) {
24904             return;
24905         }
24906         if(!keepExisting){
24907             this.clearSelections();
24908         }
24909         if(startRow <= endRow){
24910             for(var i = startRow; i <= endRow; i++){
24911                 this.selectRow(i, true);
24912             }
24913         }else{
24914             for(var i = startRow; i >= endRow; i--){
24915                 this.selectRow(i, true);
24916             }
24917         }
24918     },
24919
24920     /**
24921      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24922      * @param {Number} startRow The index of the first row in the range
24923      * @param {Number} endRow The index of the last row in the range
24924      */
24925     deselectRange : function(startRow, endRow, preventViewNotify){
24926         if(this.locked) {
24927             return;
24928         }
24929         for(var i = startRow; i <= endRow; i++){
24930             this.deselectRow(i, preventViewNotify);
24931         }
24932     },
24933
24934     /**
24935      * Selects a row.
24936      * @param {Number} row The index of the row to select
24937      * @param {Boolean} keepExisting (optional) True to keep existing selections
24938      */
24939     selectRow : function(index, keepExisting, preventViewNotify)
24940     {
24941             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24942             return;
24943         }
24944         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24945             if(!keepExisting || this.singleSelect){
24946                 this.clearSelections();
24947             }
24948             
24949             var r = this.grid.store.getAt(index);
24950             //console.log('selectRow - record id :' + r.id);
24951             
24952             this.selections.add(r);
24953             this.last = this.lastActive = index;
24954             if(!preventViewNotify){
24955                 var proxy = new Roo.Element(
24956                                 this.grid.getRowDom(index)
24957                 );
24958                 proxy.addClass('bg-info info');
24959             }
24960             this.fireEvent("rowselect", this, index, r);
24961             this.fireEvent("selectionchange", this);
24962         }
24963     },
24964
24965     /**
24966      * Deselects a row.
24967      * @param {Number} row The index of the row to deselect
24968      */
24969     deselectRow : function(index, preventViewNotify)
24970     {
24971         if(this.locked) {
24972             return;
24973         }
24974         if(this.last == index){
24975             this.last = false;
24976         }
24977         if(this.lastActive == index){
24978             this.lastActive = false;
24979         }
24980         
24981         var r = this.grid.store.getAt(index);
24982         if (!r) {
24983             return;
24984         }
24985         
24986         this.selections.remove(r);
24987         //.console.log('deselectRow - record id :' + r.id);
24988         if(!preventViewNotify){
24989         
24990             var proxy = new Roo.Element(
24991                 this.grid.getRowDom(index)
24992             );
24993             proxy.removeClass('bg-info info');
24994         }
24995         this.fireEvent("rowdeselect", this, index);
24996         this.fireEvent("selectionchange", this);
24997     },
24998
24999     // private
25000     restoreLast : function(){
25001         if(this._last){
25002             this.last = this._last;
25003         }
25004     },
25005
25006     // private
25007     acceptsNav : function(row, col, cm){
25008         return !cm.isHidden(col) && cm.isCellEditable(col, row);
25009     },
25010
25011     // private
25012     onEditorKey : function(field, e){
25013         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25014         if(k == e.TAB){
25015             e.stopEvent();
25016             ed.completeEdit();
25017             if(e.shiftKey){
25018                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25019             }else{
25020                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25021             }
25022         }else if(k == e.ENTER && !e.ctrlKey){
25023             e.stopEvent();
25024             ed.completeEdit();
25025             if(e.shiftKey){
25026                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25027             }else{
25028                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25029             }
25030         }else if(k == e.ESC){
25031             ed.cancelEdit();
25032         }
25033         if(newCell){
25034             g.startEditing(newCell[0], newCell[1]);
25035         }
25036     }
25037 });
25038 /*
25039  * Based on:
25040  * Ext JS Library 1.1.1
25041  * Copyright(c) 2006-2007, Ext JS, LLC.
25042  *
25043  * Originally Released Under LGPL - original licence link has changed is not relivant.
25044  *
25045  * Fork - LGPL
25046  * <script type="text/javascript">
25047  */
25048  
25049 /**
25050  * @class Roo.bootstrap.PagingToolbar
25051  * @extends Roo.bootstrap.NavSimplebar
25052  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25053  * @constructor
25054  * Create a new PagingToolbar
25055  * @param {Object} config The config object
25056  * @param {Roo.data.Store} store
25057  */
25058 Roo.bootstrap.PagingToolbar = function(config)
25059 {
25060     // old args format still supported... - xtype is prefered..
25061         // created from xtype...
25062     
25063     this.ds = config.dataSource;
25064     
25065     if (config.store && !this.ds) {
25066         this.store= Roo.factory(config.store, Roo.data);
25067         this.ds = this.store;
25068         this.ds.xmodule = this.xmodule || false;
25069     }
25070     
25071     this.toolbarItems = [];
25072     if (config.items) {
25073         this.toolbarItems = config.items;
25074     }
25075     
25076     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25077     
25078     this.cursor = 0;
25079     
25080     if (this.ds) { 
25081         this.bind(this.ds);
25082     }
25083     
25084     if (Roo.bootstrap.version == 4) {
25085         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25086     } else {
25087         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25088     }
25089     
25090 };
25091
25092 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25093     /**
25094      * @cfg {Roo.data.Store} dataSource
25095      * The underlying data store providing the paged data
25096      */
25097     /**
25098      * @cfg {String/HTMLElement/Element} container
25099      * container The id or element that will contain the toolbar
25100      */
25101     /**
25102      * @cfg {Boolean} displayInfo
25103      * True to display the displayMsg (defaults to false)
25104      */
25105     /**
25106      * @cfg {Number} pageSize
25107      * The number of records to display per page (defaults to 20)
25108      */
25109     pageSize: 20,
25110     /**
25111      * @cfg {String} displayMsg
25112      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25113      */
25114     displayMsg : 'Displaying {0} - {1} of {2}',
25115     /**
25116      * @cfg {String} emptyMsg
25117      * The message to display when no records are found (defaults to "No data to display")
25118      */
25119     emptyMsg : 'No data to display',
25120     /**
25121      * Customizable piece of the default paging text (defaults to "Page")
25122      * @type String
25123      */
25124     beforePageText : "Page",
25125     /**
25126      * Customizable piece of the default paging text (defaults to "of %0")
25127      * @type String
25128      */
25129     afterPageText : "of {0}",
25130     /**
25131      * Customizable piece of the default paging text (defaults to "First Page")
25132      * @type String
25133      */
25134     firstText : "First Page",
25135     /**
25136      * Customizable piece of the default paging text (defaults to "Previous Page")
25137      * @type String
25138      */
25139     prevText : "Previous Page",
25140     /**
25141      * Customizable piece of the default paging text (defaults to "Next Page")
25142      * @type String
25143      */
25144     nextText : "Next Page",
25145     /**
25146      * Customizable piece of the default paging text (defaults to "Last Page")
25147      * @type String
25148      */
25149     lastText : "Last Page",
25150     /**
25151      * Customizable piece of the default paging text (defaults to "Refresh")
25152      * @type String
25153      */
25154     refreshText : "Refresh",
25155
25156     buttons : false,
25157     // private
25158     onRender : function(ct, position) 
25159     {
25160         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25161         this.navgroup.parentId = this.id;
25162         this.navgroup.onRender(this.el, null);
25163         // add the buttons to the navgroup
25164         
25165         if(this.displayInfo){
25166             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25167             this.displayEl = this.el.select('.x-paging-info', true).first();
25168 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25169 //            this.displayEl = navel.el.select('span',true).first();
25170         }
25171         
25172         var _this = this;
25173         
25174         if(this.buttons){
25175             Roo.each(_this.buttons, function(e){ // this might need to use render????
25176                Roo.factory(e).render(_this.el);
25177             });
25178         }
25179             
25180         Roo.each(_this.toolbarItems, function(e) {
25181             _this.navgroup.addItem(e);
25182         });
25183         
25184         
25185         this.first = this.navgroup.addItem({
25186             tooltip: this.firstText,
25187             cls: "prev btn-outline-secondary",
25188             html : ' <i class="fa fa-step-backward"></i>',
25189             disabled: true,
25190             preventDefault: true,
25191             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25192         });
25193         
25194         this.prev =  this.navgroup.addItem({
25195             tooltip: this.prevText,
25196             cls: "prev btn-outline-secondary",
25197             html : ' <i class="fa fa-backward"></i>',
25198             disabled: true,
25199             preventDefault: true,
25200             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25201         });
25202     //this.addSeparator();
25203         
25204         
25205         var field = this.navgroup.addItem( {
25206             tagtype : 'span',
25207             cls : 'x-paging-position  btn-outline-secondary',
25208              disabled: true,
25209             html : this.beforePageText  +
25210                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25211                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25212          } ); //?? escaped?
25213         
25214         this.field = field.el.select('input', true).first();
25215         this.field.on("keydown", this.onPagingKeydown, this);
25216         this.field.on("focus", function(){this.dom.select();});
25217     
25218     
25219         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25220         //this.field.setHeight(18);
25221         //this.addSeparator();
25222         this.next = this.navgroup.addItem({
25223             tooltip: this.nextText,
25224             cls: "next btn-outline-secondary",
25225             html : ' <i class="fa fa-forward"></i>',
25226             disabled: true,
25227             preventDefault: true,
25228             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25229         });
25230         this.last = this.navgroup.addItem({
25231             tooltip: this.lastText,
25232             html : ' <i class="fa fa-step-forward"></i>',
25233             cls: "next btn-outline-secondary",
25234             disabled: true,
25235             preventDefault: true,
25236             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25237         });
25238     //this.addSeparator();
25239         this.loading = this.navgroup.addItem({
25240             tooltip: this.refreshText,
25241             cls: "btn-outline-secondary",
25242             html : ' <i class="fa fa-refresh"></i>',
25243             preventDefault: true,
25244             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25245         });
25246         
25247     },
25248
25249     // private
25250     updateInfo : function(){
25251         if(this.displayEl){
25252             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25253             var msg = count == 0 ?
25254                 this.emptyMsg :
25255                 String.format(
25256                     this.displayMsg,
25257                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25258                 );
25259             this.displayEl.update(msg);
25260         }
25261     },
25262
25263     // private
25264     onLoad : function(ds, r, o)
25265     {
25266         this.cursor = o.params.start ? o.params.start : 0;
25267         
25268         var d = this.getPageData(),
25269             ap = d.activePage,
25270             ps = d.pages;
25271         
25272         
25273         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25274         this.field.dom.value = ap;
25275         this.first.setDisabled(ap == 1);
25276         this.prev.setDisabled(ap == 1);
25277         this.next.setDisabled(ap == ps);
25278         this.last.setDisabled(ap == ps);
25279         this.loading.enable();
25280         this.updateInfo();
25281     },
25282
25283     // private
25284     getPageData : function(){
25285         var total = this.ds.getTotalCount();
25286         return {
25287             total : total,
25288             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25289             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25290         };
25291     },
25292
25293     // private
25294     onLoadError : function(){
25295         this.loading.enable();
25296     },
25297
25298     // private
25299     onPagingKeydown : function(e){
25300         var k = e.getKey();
25301         var d = this.getPageData();
25302         if(k == e.RETURN){
25303             var v = this.field.dom.value, pageNum;
25304             if(!v || isNaN(pageNum = parseInt(v, 10))){
25305                 this.field.dom.value = d.activePage;
25306                 return;
25307             }
25308             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25309             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25310             e.stopEvent();
25311         }
25312         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))
25313         {
25314           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25315           this.field.dom.value = pageNum;
25316           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25317           e.stopEvent();
25318         }
25319         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25320         {
25321           var v = this.field.dom.value, pageNum; 
25322           var increment = (e.shiftKey) ? 10 : 1;
25323           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25324                 increment *= -1;
25325           }
25326           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25327             this.field.dom.value = d.activePage;
25328             return;
25329           }
25330           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25331           {
25332             this.field.dom.value = parseInt(v, 10) + increment;
25333             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25334             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25335           }
25336           e.stopEvent();
25337         }
25338     },
25339
25340     // private
25341     beforeLoad : function(){
25342         if(this.loading){
25343             this.loading.disable();
25344         }
25345     },
25346
25347     // private
25348     onClick : function(which){
25349         
25350         var ds = this.ds;
25351         if (!ds) {
25352             return;
25353         }
25354         
25355         switch(which){
25356             case "first":
25357                 ds.load({params:{start: 0, limit: this.pageSize}});
25358             break;
25359             case "prev":
25360                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25361             break;
25362             case "next":
25363                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25364             break;
25365             case "last":
25366                 var total = ds.getTotalCount();
25367                 var extra = total % this.pageSize;
25368                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25369                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25370             break;
25371             case "refresh":
25372                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25373             break;
25374         }
25375     },
25376
25377     /**
25378      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25379      * @param {Roo.data.Store} store The data store to unbind
25380      */
25381     unbind : function(ds){
25382         ds.un("beforeload", this.beforeLoad, this);
25383         ds.un("load", this.onLoad, this);
25384         ds.un("loadexception", this.onLoadError, this);
25385         ds.un("remove", this.updateInfo, this);
25386         ds.un("add", this.updateInfo, this);
25387         this.ds = undefined;
25388     },
25389
25390     /**
25391      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25392      * @param {Roo.data.Store} store The data store to bind
25393      */
25394     bind : function(ds){
25395         ds.on("beforeload", this.beforeLoad, this);
25396         ds.on("load", this.onLoad, this);
25397         ds.on("loadexception", this.onLoadError, this);
25398         ds.on("remove", this.updateInfo, this);
25399         ds.on("add", this.updateInfo, this);
25400         this.ds = ds;
25401     }
25402 });/*
25403  * - LGPL
25404  *
25405  * element
25406  * 
25407  */
25408
25409 /**
25410  * @class Roo.bootstrap.MessageBar
25411  * @extends Roo.bootstrap.Component
25412  * Bootstrap MessageBar class
25413  * @cfg {String} html contents of the MessageBar
25414  * @cfg {String} weight (info | success | warning | danger) default info
25415  * @cfg {String} beforeClass insert the bar before the given class
25416  * @cfg {Boolean} closable (true | false) default false
25417  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25418  * 
25419  * @constructor
25420  * Create a new Element
25421  * @param {Object} config The config object
25422  */
25423
25424 Roo.bootstrap.MessageBar = function(config){
25425     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25426 };
25427
25428 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25429     
25430     html: '',
25431     weight: 'info',
25432     closable: false,
25433     fixed: false,
25434     beforeClass: 'bootstrap-sticky-wrap',
25435     
25436     getAutoCreate : function(){
25437         
25438         var cfg = {
25439             tag: 'div',
25440             cls: 'alert alert-dismissable alert-' + this.weight,
25441             cn: [
25442                 {
25443                     tag: 'span',
25444                     cls: 'message',
25445                     html: this.html || ''
25446                 }
25447             ]
25448         };
25449         
25450         if(this.fixed){
25451             cfg.cls += ' alert-messages-fixed';
25452         }
25453         
25454         if(this.closable){
25455             cfg.cn.push({
25456                 tag: 'button',
25457                 cls: 'close',
25458                 html: 'x'
25459             });
25460         }
25461         
25462         return cfg;
25463     },
25464     
25465     onRender : function(ct, position)
25466     {
25467         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25468         
25469         if(!this.el){
25470             var cfg = Roo.apply({},  this.getAutoCreate());
25471             cfg.id = Roo.id();
25472             
25473             if (this.cls) {
25474                 cfg.cls += ' ' + this.cls;
25475             }
25476             if (this.style) {
25477                 cfg.style = this.style;
25478             }
25479             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25480             
25481             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25482         }
25483         
25484         this.el.select('>button.close').on('click', this.hide, this);
25485         
25486     },
25487     
25488     show : function()
25489     {
25490         if (!this.rendered) {
25491             this.render();
25492         }
25493         
25494         this.el.show();
25495         
25496         this.fireEvent('show', this);
25497         
25498     },
25499     
25500     hide : function()
25501     {
25502         if (!this.rendered) {
25503             this.render();
25504         }
25505         
25506         this.el.hide();
25507         
25508         this.fireEvent('hide', this);
25509     },
25510     
25511     update : function()
25512     {
25513 //        var e = this.el.dom.firstChild;
25514 //        
25515 //        if(this.closable){
25516 //            e = e.nextSibling;
25517 //        }
25518 //        
25519 //        e.data = this.html || '';
25520
25521         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25522     }
25523    
25524 });
25525
25526  
25527
25528      /*
25529  * - LGPL
25530  *
25531  * Graph
25532  * 
25533  */
25534
25535
25536 /**
25537  * @class Roo.bootstrap.Graph
25538  * @extends Roo.bootstrap.Component
25539  * Bootstrap Graph class
25540 > Prameters
25541  -sm {number} sm 4
25542  -md {number} md 5
25543  @cfg {String} graphtype  bar | vbar | pie
25544  @cfg {number} g_x coodinator | centre x (pie)
25545  @cfg {number} g_y coodinator | centre y (pie)
25546  @cfg {number} g_r radius (pie)
25547  @cfg {number} g_height height of the chart (respected by all elements in the set)
25548  @cfg {number} g_width width of the chart (respected by all elements in the set)
25549  @cfg {Object} title The title of the chart
25550     
25551  -{Array}  values
25552  -opts (object) options for the chart 
25553      o {
25554      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25555      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25556      o vgutter (number)
25557      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.
25558      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25559      o to
25560      o stretch (boolean)
25561      o }
25562  -opts (object) options for the pie
25563      o{
25564      o cut
25565      o startAngle (number)
25566      o endAngle (number)
25567      } 
25568  *
25569  * @constructor
25570  * Create a new Input
25571  * @param {Object} config The config object
25572  */
25573
25574 Roo.bootstrap.Graph = function(config){
25575     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25576     
25577     this.addEvents({
25578         // img events
25579         /**
25580          * @event click
25581          * The img click event for the img.
25582          * @param {Roo.EventObject} e
25583          */
25584         "click" : true
25585     });
25586 };
25587
25588 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25589     
25590     sm: 4,
25591     md: 5,
25592     graphtype: 'bar',
25593     g_height: 250,
25594     g_width: 400,
25595     g_x: 50,
25596     g_y: 50,
25597     g_r: 30,
25598     opts:{
25599         //g_colors: this.colors,
25600         g_type: 'soft',
25601         g_gutter: '20%'
25602
25603     },
25604     title : false,
25605
25606     getAutoCreate : function(){
25607         
25608         var cfg = {
25609             tag: 'div',
25610             html : null
25611         };
25612         
25613         
25614         return  cfg;
25615     },
25616
25617     onRender : function(ct,position){
25618         
25619         
25620         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25621         
25622         if (typeof(Raphael) == 'undefined') {
25623             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25624             return;
25625         }
25626         
25627         this.raphael = Raphael(this.el.dom);
25628         
25629                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25630                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25631                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25632                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25633                 /*
25634                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25635                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25636                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25637                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25638                 
25639                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25640                 r.barchart(330, 10, 300, 220, data1);
25641                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25642                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25643                 */
25644                 
25645                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25646                 // r.barchart(30, 30, 560, 250,  xdata, {
25647                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25648                 //     axis : "0 0 1 1",
25649                 //     axisxlabels :  xdata
25650                 //     //yvalues : cols,
25651                    
25652                 // });
25653 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25654 //        
25655 //        this.load(null,xdata,{
25656 //                axis : "0 0 1 1",
25657 //                axisxlabels :  xdata
25658 //                });
25659
25660     },
25661
25662     load : function(graphtype,xdata,opts)
25663     {
25664         this.raphael.clear();
25665         if(!graphtype) {
25666             graphtype = this.graphtype;
25667         }
25668         if(!opts){
25669             opts = this.opts;
25670         }
25671         var r = this.raphael,
25672             fin = function () {
25673                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25674             },
25675             fout = function () {
25676                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25677             },
25678             pfin = function() {
25679                 this.sector.stop();
25680                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25681
25682                 if (this.label) {
25683                     this.label[0].stop();
25684                     this.label[0].attr({ r: 7.5 });
25685                     this.label[1].attr({ "font-weight": 800 });
25686                 }
25687             },
25688             pfout = function() {
25689                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25690
25691                 if (this.label) {
25692                     this.label[0].animate({ r: 5 }, 500, "bounce");
25693                     this.label[1].attr({ "font-weight": 400 });
25694                 }
25695             };
25696
25697         switch(graphtype){
25698             case 'bar':
25699                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25700                 break;
25701             case 'hbar':
25702                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25703                 break;
25704             case 'pie':
25705 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25706 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25707 //            
25708                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25709                 
25710                 break;
25711
25712         }
25713         
25714         if(this.title){
25715             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25716         }
25717         
25718     },
25719     
25720     setTitle: function(o)
25721     {
25722         this.title = o;
25723     },
25724     
25725     initEvents: function() {
25726         
25727         if(!this.href){
25728             this.el.on('click', this.onClick, this);
25729         }
25730     },
25731     
25732     onClick : function(e)
25733     {
25734         Roo.log('img onclick');
25735         this.fireEvent('click', this, e);
25736     }
25737    
25738 });
25739
25740  
25741 /*
25742  * - LGPL
25743  *
25744  * numberBox
25745  * 
25746  */
25747 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25748
25749 /**
25750  * @class Roo.bootstrap.dash.NumberBox
25751  * @extends Roo.bootstrap.Component
25752  * Bootstrap NumberBox class
25753  * @cfg {String} headline Box headline
25754  * @cfg {String} content Box content
25755  * @cfg {String} icon Box icon
25756  * @cfg {String} footer Footer text
25757  * @cfg {String} fhref Footer href
25758  * 
25759  * @constructor
25760  * Create a new NumberBox
25761  * @param {Object} config The config object
25762  */
25763
25764
25765 Roo.bootstrap.dash.NumberBox = function(config){
25766     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25767     
25768 };
25769
25770 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25771     
25772     headline : '',
25773     content : '',
25774     icon : '',
25775     footer : '',
25776     fhref : '',
25777     ficon : '',
25778     
25779     getAutoCreate : function(){
25780         
25781         var cfg = {
25782             tag : 'div',
25783             cls : 'small-box ',
25784             cn : [
25785                 {
25786                     tag : 'div',
25787                     cls : 'inner',
25788                     cn :[
25789                         {
25790                             tag : 'h3',
25791                             cls : 'roo-headline',
25792                             html : this.headline
25793                         },
25794                         {
25795                             tag : 'p',
25796                             cls : 'roo-content',
25797                             html : this.content
25798                         }
25799                     ]
25800                 }
25801             ]
25802         };
25803         
25804         if(this.icon){
25805             cfg.cn.push({
25806                 tag : 'div',
25807                 cls : 'icon',
25808                 cn :[
25809                     {
25810                         tag : 'i',
25811                         cls : 'ion ' + this.icon
25812                     }
25813                 ]
25814             });
25815         }
25816         
25817         if(this.footer){
25818             var footer = {
25819                 tag : 'a',
25820                 cls : 'small-box-footer',
25821                 href : this.fhref || '#',
25822                 html : this.footer
25823             };
25824             
25825             cfg.cn.push(footer);
25826             
25827         }
25828         
25829         return  cfg;
25830     },
25831
25832     onRender : function(ct,position){
25833         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25834
25835
25836        
25837                 
25838     },
25839
25840     setHeadline: function (value)
25841     {
25842         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25843     },
25844     
25845     setFooter: function (value, href)
25846     {
25847         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25848         
25849         if(href){
25850             this.el.select('a.small-box-footer',true).first().attr('href', href);
25851         }
25852         
25853     },
25854
25855     setContent: function (value)
25856     {
25857         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25858     },
25859
25860     initEvents: function() 
25861     {   
25862         
25863     }
25864     
25865 });
25866
25867  
25868 /*
25869  * - LGPL
25870  *
25871  * TabBox
25872  * 
25873  */
25874 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25875
25876 /**
25877  * @class Roo.bootstrap.dash.TabBox
25878  * @extends Roo.bootstrap.Component
25879  * Bootstrap TabBox class
25880  * @cfg {String} title Title of the TabBox
25881  * @cfg {String} icon Icon of the TabBox
25882  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25883  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25884  * 
25885  * @constructor
25886  * Create a new TabBox
25887  * @param {Object} config The config object
25888  */
25889
25890
25891 Roo.bootstrap.dash.TabBox = function(config){
25892     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25893     this.addEvents({
25894         // raw events
25895         /**
25896          * @event addpane
25897          * When a pane is added
25898          * @param {Roo.bootstrap.dash.TabPane} pane
25899          */
25900         "addpane" : true,
25901         /**
25902          * @event activatepane
25903          * When a pane is activated
25904          * @param {Roo.bootstrap.dash.TabPane} pane
25905          */
25906         "activatepane" : true
25907         
25908          
25909     });
25910     
25911     this.panes = [];
25912 };
25913
25914 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25915
25916     title : '',
25917     icon : false,
25918     showtabs : true,
25919     tabScrollable : false,
25920     
25921     getChildContainer : function()
25922     {
25923         return this.el.select('.tab-content', true).first();
25924     },
25925     
25926     getAutoCreate : function(){
25927         
25928         var header = {
25929             tag: 'li',
25930             cls: 'pull-left header',
25931             html: this.title,
25932             cn : []
25933         };
25934         
25935         if(this.icon){
25936             header.cn.push({
25937                 tag: 'i',
25938                 cls: 'fa ' + this.icon
25939             });
25940         }
25941         
25942         var h = {
25943             tag: 'ul',
25944             cls: 'nav nav-tabs pull-right',
25945             cn: [
25946                 header
25947             ]
25948         };
25949         
25950         if(this.tabScrollable){
25951             h = {
25952                 tag: 'div',
25953                 cls: 'tab-header',
25954                 cn: [
25955                     {
25956                         tag: 'ul',
25957                         cls: 'nav nav-tabs pull-right',
25958                         cn: [
25959                             header
25960                         ]
25961                     }
25962                 ]
25963             };
25964         }
25965         
25966         var cfg = {
25967             tag: 'div',
25968             cls: 'nav-tabs-custom',
25969             cn: [
25970                 h,
25971                 {
25972                     tag: 'div',
25973                     cls: 'tab-content no-padding',
25974                     cn: []
25975                 }
25976             ]
25977         };
25978
25979         return  cfg;
25980     },
25981     initEvents : function()
25982     {
25983         //Roo.log('add add pane handler');
25984         this.on('addpane', this.onAddPane, this);
25985     },
25986      /**
25987      * Updates the box title
25988      * @param {String} html to set the title to.
25989      */
25990     setTitle : function(value)
25991     {
25992         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25993     },
25994     onAddPane : function(pane)
25995     {
25996         this.panes.push(pane);
25997         //Roo.log('addpane');
25998         //Roo.log(pane);
25999         // tabs are rendere left to right..
26000         if(!this.showtabs){
26001             return;
26002         }
26003         
26004         var ctr = this.el.select('.nav-tabs', true).first();
26005          
26006          
26007         var existing = ctr.select('.nav-tab',true);
26008         var qty = existing.getCount();;
26009         
26010         
26011         var tab = ctr.createChild({
26012             tag : 'li',
26013             cls : 'nav-tab' + (qty ? '' : ' active'),
26014             cn : [
26015                 {
26016                     tag : 'a',
26017                     href:'#',
26018                     html : pane.title
26019                 }
26020             ]
26021         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26022         pane.tab = tab;
26023         
26024         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26025         if (!qty) {
26026             pane.el.addClass('active');
26027         }
26028         
26029                 
26030     },
26031     onTabClick : function(ev,un,ob,pane)
26032     {
26033         //Roo.log('tab - prev default');
26034         ev.preventDefault();
26035         
26036         
26037         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26038         pane.tab.addClass('active');
26039         //Roo.log(pane.title);
26040         this.getChildContainer().select('.tab-pane',true).removeClass('active');
26041         // technically we should have a deactivate event.. but maybe add later.
26042         // and it should not de-activate the selected tab...
26043         this.fireEvent('activatepane', pane);
26044         pane.el.addClass('active');
26045         pane.fireEvent('activate');
26046         
26047         
26048     },
26049     
26050     getActivePane : function()
26051     {
26052         var r = false;
26053         Roo.each(this.panes, function(p) {
26054             if(p.el.hasClass('active')){
26055                 r = p;
26056                 return false;
26057             }
26058             
26059             return;
26060         });
26061         
26062         return r;
26063     }
26064     
26065     
26066 });
26067
26068  
26069 /*
26070  * - LGPL
26071  *
26072  * Tab pane
26073  * 
26074  */
26075 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26076 /**
26077  * @class Roo.bootstrap.TabPane
26078  * @extends Roo.bootstrap.Component
26079  * Bootstrap TabPane class
26080  * @cfg {Boolean} active (false | true) Default false
26081  * @cfg {String} title title of panel
26082
26083  * 
26084  * @constructor
26085  * Create a new TabPane
26086  * @param {Object} config The config object
26087  */
26088
26089 Roo.bootstrap.dash.TabPane = function(config){
26090     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26091     
26092     this.addEvents({
26093         // raw events
26094         /**
26095          * @event activate
26096          * When a pane is activated
26097          * @param {Roo.bootstrap.dash.TabPane} pane
26098          */
26099         "activate" : true
26100          
26101     });
26102 };
26103
26104 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
26105     
26106     active : false,
26107     title : '',
26108     
26109     // the tabBox that this is attached to.
26110     tab : false,
26111      
26112     getAutoCreate : function() 
26113     {
26114         var cfg = {
26115             tag: 'div',
26116             cls: 'tab-pane'
26117         };
26118         
26119         if(this.active){
26120             cfg.cls += ' active';
26121         }
26122         
26123         return cfg;
26124     },
26125     initEvents  : function()
26126     {
26127         //Roo.log('trigger add pane handler');
26128         this.parent().fireEvent('addpane', this)
26129     },
26130     
26131      /**
26132      * Updates the tab title 
26133      * @param {String} html to set the title to.
26134      */
26135     setTitle: function(str)
26136     {
26137         if (!this.tab) {
26138             return;
26139         }
26140         this.title = str;
26141         this.tab.select('a', true).first().dom.innerHTML = str;
26142         
26143     }
26144     
26145     
26146     
26147 });
26148
26149  
26150
26151
26152  /*
26153  * - LGPL
26154  *
26155  * menu
26156  * 
26157  */
26158 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26159
26160 /**
26161  * @class Roo.bootstrap.menu.Menu
26162  * @extends Roo.bootstrap.Component
26163  * Bootstrap Menu class - container for Menu
26164  * @cfg {String} html Text of the menu
26165  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26166  * @cfg {String} icon Font awesome icon
26167  * @cfg {String} pos Menu align to (top | bottom) default bottom
26168  * 
26169  * 
26170  * @constructor
26171  * Create a new Menu
26172  * @param {Object} config The config object
26173  */
26174
26175
26176 Roo.bootstrap.menu.Menu = function(config){
26177     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26178     
26179     this.addEvents({
26180         /**
26181          * @event beforeshow
26182          * Fires before this menu is displayed
26183          * @param {Roo.bootstrap.menu.Menu} this
26184          */
26185         beforeshow : true,
26186         /**
26187          * @event beforehide
26188          * Fires before this menu is hidden
26189          * @param {Roo.bootstrap.menu.Menu} this
26190          */
26191         beforehide : true,
26192         /**
26193          * @event show
26194          * Fires after this menu is displayed
26195          * @param {Roo.bootstrap.menu.Menu} this
26196          */
26197         show : true,
26198         /**
26199          * @event hide
26200          * Fires after this menu is hidden
26201          * @param {Roo.bootstrap.menu.Menu} this
26202          */
26203         hide : true,
26204         /**
26205          * @event click
26206          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26207          * @param {Roo.bootstrap.menu.Menu} this
26208          * @param {Roo.EventObject} e
26209          */
26210         click : true
26211     });
26212     
26213 };
26214
26215 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26216     
26217     submenu : false,
26218     html : '',
26219     weight : 'default',
26220     icon : false,
26221     pos : 'bottom',
26222     
26223     
26224     getChildContainer : function() {
26225         if(this.isSubMenu){
26226             return this.el;
26227         }
26228         
26229         return this.el.select('ul.dropdown-menu', true).first();  
26230     },
26231     
26232     getAutoCreate : function()
26233     {
26234         var text = [
26235             {
26236                 tag : 'span',
26237                 cls : 'roo-menu-text',
26238                 html : this.html
26239             }
26240         ];
26241         
26242         if(this.icon){
26243             text.unshift({
26244                 tag : 'i',
26245                 cls : 'fa ' + this.icon
26246             })
26247         }
26248         
26249         
26250         var cfg = {
26251             tag : 'div',
26252             cls : 'btn-group',
26253             cn : [
26254                 {
26255                     tag : 'button',
26256                     cls : 'dropdown-button btn btn-' + this.weight,
26257                     cn : text
26258                 },
26259                 {
26260                     tag : 'button',
26261                     cls : 'dropdown-toggle btn btn-' + this.weight,
26262                     cn : [
26263                         {
26264                             tag : 'span',
26265                             cls : 'caret'
26266                         }
26267                     ]
26268                 },
26269                 {
26270                     tag : 'ul',
26271                     cls : 'dropdown-menu'
26272                 }
26273             ]
26274             
26275         };
26276         
26277         if(this.pos == 'top'){
26278             cfg.cls += ' dropup';
26279         }
26280         
26281         if(this.isSubMenu){
26282             cfg = {
26283                 tag : 'ul',
26284                 cls : 'dropdown-menu'
26285             }
26286         }
26287         
26288         return cfg;
26289     },
26290     
26291     onRender : function(ct, position)
26292     {
26293         this.isSubMenu = ct.hasClass('dropdown-submenu');
26294         
26295         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26296     },
26297     
26298     initEvents : function() 
26299     {
26300         if(this.isSubMenu){
26301             return;
26302         }
26303         
26304         this.hidden = true;
26305         
26306         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26307         this.triggerEl.on('click', this.onTriggerPress, this);
26308         
26309         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26310         this.buttonEl.on('click', this.onClick, this);
26311         
26312     },
26313     
26314     list : function()
26315     {
26316         if(this.isSubMenu){
26317             return this.el;
26318         }
26319         
26320         return this.el.select('ul.dropdown-menu', true).first();
26321     },
26322     
26323     onClick : function(e)
26324     {
26325         this.fireEvent("click", this, e);
26326     },
26327     
26328     onTriggerPress  : function(e)
26329     {   
26330         if (this.isVisible()) {
26331             this.hide();
26332         } else {
26333             this.show();
26334         }
26335     },
26336     
26337     isVisible : function(){
26338         return !this.hidden;
26339     },
26340     
26341     show : function()
26342     {
26343         this.fireEvent("beforeshow", this);
26344         
26345         this.hidden = false;
26346         this.el.addClass('open');
26347         
26348         Roo.get(document).on("mouseup", this.onMouseUp, this);
26349         
26350         this.fireEvent("show", this);
26351         
26352         
26353     },
26354     
26355     hide : function()
26356     {
26357         this.fireEvent("beforehide", this);
26358         
26359         this.hidden = true;
26360         this.el.removeClass('open');
26361         
26362         Roo.get(document).un("mouseup", this.onMouseUp);
26363         
26364         this.fireEvent("hide", this);
26365     },
26366     
26367     onMouseUp : function()
26368     {
26369         this.hide();
26370     }
26371     
26372 });
26373
26374  
26375  /*
26376  * - LGPL
26377  *
26378  * menu item
26379  * 
26380  */
26381 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26382
26383 /**
26384  * @class Roo.bootstrap.menu.Item
26385  * @extends Roo.bootstrap.Component
26386  * Bootstrap MenuItem class
26387  * @cfg {Boolean} submenu (true | false) default false
26388  * @cfg {String} html text of the item
26389  * @cfg {String} href the link
26390  * @cfg {Boolean} disable (true | false) default false
26391  * @cfg {Boolean} preventDefault (true | false) default true
26392  * @cfg {String} icon Font awesome icon
26393  * @cfg {String} pos Submenu align to (left | right) default right 
26394  * 
26395  * 
26396  * @constructor
26397  * Create a new Item
26398  * @param {Object} config The config object
26399  */
26400
26401
26402 Roo.bootstrap.menu.Item = function(config){
26403     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26404     this.addEvents({
26405         /**
26406          * @event mouseover
26407          * Fires when the mouse is hovering over this menu
26408          * @param {Roo.bootstrap.menu.Item} this
26409          * @param {Roo.EventObject} e
26410          */
26411         mouseover : true,
26412         /**
26413          * @event mouseout
26414          * Fires when the mouse exits this menu
26415          * @param {Roo.bootstrap.menu.Item} this
26416          * @param {Roo.EventObject} e
26417          */
26418         mouseout : true,
26419         // raw events
26420         /**
26421          * @event click
26422          * The raw click event for the entire grid.
26423          * @param {Roo.EventObject} e
26424          */
26425         click : true
26426     });
26427 };
26428
26429 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26430     
26431     submenu : false,
26432     href : '',
26433     html : '',
26434     preventDefault: true,
26435     disable : false,
26436     icon : false,
26437     pos : 'right',
26438     
26439     getAutoCreate : function()
26440     {
26441         var text = [
26442             {
26443                 tag : 'span',
26444                 cls : 'roo-menu-item-text',
26445                 html : this.html
26446             }
26447         ];
26448         
26449         if(this.icon){
26450             text.unshift({
26451                 tag : 'i',
26452                 cls : 'fa ' + this.icon
26453             })
26454         }
26455         
26456         var cfg = {
26457             tag : 'li',
26458             cn : [
26459                 {
26460                     tag : 'a',
26461                     href : this.href || '#',
26462                     cn : text
26463                 }
26464             ]
26465         };
26466         
26467         if(this.disable){
26468             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26469         }
26470         
26471         if(this.submenu){
26472             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26473             
26474             if(this.pos == 'left'){
26475                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26476             }
26477         }
26478         
26479         return cfg;
26480     },
26481     
26482     initEvents : function() 
26483     {
26484         this.el.on('mouseover', this.onMouseOver, this);
26485         this.el.on('mouseout', this.onMouseOut, this);
26486         
26487         this.el.select('a', true).first().on('click', this.onClick, this);
26488         
26489     },
26490     
26491     onClick : function(e)
26492     {
26493         if(this.preventDefault){
26494             e.preventDefault();
26495         }
26496         
26497         this.fireEvent("click", this, e);
26498     },
26499     
26500     onMouseOver : function(e)
26501     {
26502         if(this.submenu && this.pos == 'left'){
26503             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26504         }
26505         
26506         this.fireEvent("mouseover", this, e);
26507     },
26508     
26509     onMouseOut : function(e)
26510     {
26511         this.fireEvent("mouseout", this, e);
26512     }
26513 });
26514
26515  
26516
26517  /*
26518  * - LGPL
26519  *
26520  * menu separator
26521  * 
26522  */
26523 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26524
26525 /**
26526  * @class Roo.bootstrap.menu.Separator
26527  * @extends Roo.bootstrap.Component
26528  * Bootstrap Separator class
26529  * 
26530  * @constructor
26531  * Create a new Separator
26532  * @param {Object} config The config object
26533  */
26534
26535
26536 Roo.bootstrap.menu.Separator = function(config){
26537     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26538 };
26539
26540 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26541     
26542     getAutoCreate : function(){
26543         var cfg = {
26544             tag : 'li',
26545             cls: 'divider'
26546         };
26547         
26548         return cfg;
26549     }
26550    
26551 });
26552
26553  
26554
26555  /*
26556  * - LGPL
26557  *
26558  * Tooltip
26559  * 
26560  */
26561
26562 /**
26563  * @class Roo.bootstrap.Tooltip
26564  * Bootstrap Tooltip class
26565  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26566  * to determine which dom element triggers the tooltip.
26567  * 
26568  * It needs to add support for additional attributes like tooltip-position
26569  * 
26570  * @constructor
26571  * Create a new Toolti
26572  * @param {Object} config The config object
26573  */
26574
26575 Roo.bootstrap.Tooltip = function(config){
26576     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26577     
26578     this.alignment = Roo.bootstrap.Tooltip.alignment;
26579     
26580     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26581         this.alignment = config.alignment;
26582     }
26583     
26584 };
26585
26586 Roo.apply(Roo.bootstrap.Tooltip, {
26587     /**
26588      * @function init initialize tooltip monitoring.
26589      * @static
26590      */
26591     currentEl : false,
26592     currentTip : false,
26593     currentRegion : false,
26594     
26595     //  init : delay?
26596     
26597     init : function()
26598     {
26599         Roo.get(document).on('mouseover', this.enter ,this);
26600         Roo.get(document).on('mouseout', this.leave, this);
26601          
26602         
26603         this.currentTip = new Roo.bootstrap.Tooltip();
26604     },
26605     
26606     enter : function(ev)
26607     {
26608         var dom = ev.getTarget();
26609         
26610         //Roo.log(['enter',dom]);
26611         var el = Roo.fly(dom);
26612         if (this.currentEl) {
26613             //Roo.log(dom);
26614             //Roo.log(this.currentEl);
26615             //Roo.log(this.currentEl.contains(dom));
26616             if (this.currentEl == el) {
26617                 return;
26618             }
26619             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26620                 return;
26621             }
26622
26623         }
26624         
26625         if (this.currentTip.el) {
26626             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26627         }    
26628         //Roo.log(ev);
26629         
26630         if(!el || el.dom == document){
26631             return;
26632         }
26633         
26634         var bindEl = el;
26635         
26636         // you can not look for children, as if el is the body.. then everythign is the child..
26637         if (!el.attr('tooltip')) { //
26638             if (!el.select("[tooltip]").elements.length) {
26639                 return;
26640             }
26641             // is the mouse over this child...?
26642             bindEl = el.select("[tooltip]").first();
26643             var xy = ev.getXY();
26644             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26645                 //Roo.log("not in region.");
26646                 return;
26647             }
26648             //Roo.log("child element over..");
26649             
26650         }
26651         this.currentEl = bindEl;
26652         this.currentTip.bind(bindEl);
26653         this.currentRegion = Roo.lib.Region.getRegion(dom);
26654         this.currentTip.enter();
26655         
26656     },
26657     leave : function(ev)
26658     {
26659         var dom = ev.getTarget();
26660         //Roo.log(['leave',dom]);
26661         if (!this.currentEl) {
26662             return;
26663         }
26664         
26665         
26666         if (dom != this.currentEl.dom) {
26667             return;
26668         }
26669         var xy = ev.getXY();
26670         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26671             return;
26672         }
26673         // only activate leave if mouse cursor is outside... bounding box..
26674         
26675         
26676         
26677         
26678         if (this.currentTip) {
26679             this.currentTip.leave();
26680         }
26681         //Roo.log('clear currentEl');
26682         this.currentEl = false;
26683         
26684         
26685     },
26686     alignment : {
26687         'left' : ['r-l', [-2,0], 'right'],
26688         'right' : ['l-r', [2,0], 'left'],
26689         'bottom' : ['t-b', [0,2], 'top'],
26690         'top' : [ 'b-t', [0,-2], 'bottom']
26691     }
26692     
26693 });
26694
26695
26696 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26697     
26698     
26699     bindEl : false,
26700     
26701     delay : null, // can be { show : 300 , hide: 500}
26702     
26703     timeout : null,
26704     
26705     hoverState : null, //???
26706     
26707     placement : 'bottom', 
26708     
26709     alignment : false,
26710     
26711     getAutoCreate : function(){
26712     
26713         var cfg = {
26714            cls : 'tooltip',
26715            role : 'tooltip',
26716            cn : [
26717                 {
26718                     cls : 'tooltip-arrow'
26719                 },
26720                 {
26721                     cls : 'tooltip-inner'
26722                 }
26723            ]
26724         };
26725         
26726         return cfg;
26727     },
26728     bind : function(el)
26729     {
26730         this.bindEl = el;
26731     },
26732       
26733     
26734     enter : function () {
26735        
26736         if (this.timeout != null) {
26737             clearTimeout(this.timeout);
26738         }
26739         
26740         this.hoverState = 'in';
26741          //Roo.log("enter - show");
26742         if (!this.delay || !this.delay.show) {
26743             this.show();
26744             return;
26745         }
26746         var _t = this;
26747         this.timeout = setTimeout(function () {
26748             if (_t.hoverState == 'in') {
26749                 _t.show();
26750             }
26751         }, this.delay.show);
26752     },
26753     leave : function()
26754     {
26755         clearTimeout(this.timeout);
26756     
26757         this.hoverState = 'out';
26758          if (!this.delay || !this.delay.hide) {
26759             this.hide();
26760             return;
26761         }
26762        
26763         var _t = this;
26764         this.timeout = setTimeout(function () {
26765             //Roo.log("leave - timeout");
26766             
26767             if (_t.hoverState == 'out') {
26768                 _t.hide();
26769                 Roo.bootstrap.Tooltip.currentEl = false;
26770             }
26771         }, delay);
26772     },
26773     
26774     show : function (msg)
26775     {
26776         if (!this.el) {
26777             this.render(document.body);
26778         }
26779         // set content.
26780         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26781         
26782         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26783         
26784         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26785         
26786         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26787         
26788         var placement = typeof this.placement == 'function' ?
26789             this.placement.call(this, this.el, on_el) :
26790             this.placement;
26791             
26792         var autoToken = /\s?auto?\s?/i;
26793         var autoPlace = autoToken.test(placement);
26794         if (autoPlace) {
26795             placement = placement.replace(autoToken, '') || 'top';
26796         }
26797         
26798         //this.el.detach()
26799         //this.el.setXY([0,0]);
26800         this.el.show();
26801         //this.el.dom.style.display='block';
26802         
26803         //this.el.appendTo(on_el);
26804         
26805         var p = this.getPosition();
26806         var box = this.el.getBox();
26807         
26808         if (autoPlace) {
26809             // fixme..
26810         }
26811         
26812         var align = this.alignment[placement];
26813         
26814         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26815         
26816         if(placement == 'top' || placement == 'bottom'){
26817             if(xy[0] < 0){
26818                 placement = 'right';
26819             }
26820             
26821             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26822                 placement = 'left';
26823             }
26824             
26825             var scroll = Roo.select('body', true).first().getScroll();
26826             
26827             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26828                 placement = 'top';
26829             }
26830             
26831             align = this.alignment[placement];
26832         }
26833         
26834         this.el.alignTo(this.bindEl, align[0],align[1]);
26835         //var arrow = this.el.select('.arrow',true).first();
26836         //arrow.set(align[2], 
26837         
26838         this.el.addClass(placement);
26839         
26840         this.el.addClass('in fade');
26841         
26842         this.hoverState = null;
26843         
26844         if (this.el.hasClass('fade')) {
26845             // fade it?
26846         }
26847         
26848     },
26849     hide : function()
26850     {
26851          
26852         if (!this.el) {
26853             return;
26854         }
26855         //this.el.setXY([0,0]);
26856         this.el.removeClass('in');
26857         //this.el.hide();
26858         
26859     }
26860     
26861 });
26862  
26863
26864  /*
26865  * - LGPL
26866  *
26867  * Location Picker
26868  * 
26869  */
26870
26871 /**
26872  * @class Roo.bootstrap.LocationPicker
26873  * @extends Roo.bootstrap.Component
26874  * Bootstrap LocationPicker class
26875  * @cfg {Number} latitude Position when init default 0
26876  * @cfg {Number} longitude Position when init default 0
26877  * @cfg {Number} zoom default 15
26878  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26879  * @cfg {Boolean} mapTypeControl default false
26880  * @cfg {Boolean} disableDoubleClickZoom default false
26881  * @cfg {Boolean} scrollwheel default true
26882  * @cfg {Boolean} streetViewControl default false
26883  * @cfg {Number} radius default 0
26884  * @cfg {String} locationName
26885  * @cfg {Boolean} draggable default true
26886  * @cfg {Boolean} enableAutocomplete default false
26887  * @cfg {Boolean} enableReverseGeocode default true
26888  * @cfg {String} markerTitle
26889  * 
26890  * @constructor
26891  * Create a new LocationPicker
26892  * @param {Object} config The config object
26893  */
26894
26895
26896 Roo.bootstrap.LocationPicker = function(config){
26897     
26898     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26899     
26900     this.addEvents({
26901         /**
26902          * @event initial
26903          * Fires when the picker initialized.
26904          * @param {Roo.bootstrap.LocationPicker} this
26905          * @param {Google Location} location
26906          */
26907         initial : true,
26908         /**
26909          * @event positionchanged
26910          * Fires when the picker position changed.
26911          * @param {Roo.bootstrap.LocationPicker} this
26912          * @param {Google Location} location
26913          */
26914         positionchanged : true,
26915         /**
26916          * @event resize
26917          * Fires when the map resize.
26918          * @param {Roo.bootstrap.LocationPicker} this
26919          */
26920         resize : true,
26921         /**
26922          * @event show
26923          * Fires when the map show.
26924          * @param {Roo.bootstrap.LocationPicker} this
26925          */
26926         show : true,
26927         /**
26928          * @event hide
26929          * Fires when the map hide.
26930          * @param {Roo.bootstrap.LocationPicker} this
26931          */
26932         hide : true,
26933         /**
26934          * @event mapClick
26935          * Fires when click the map.
26936          * @param {Roo.bootstrap.LocationPicker} this
26937          * @param {Map event} e
26938          */
26939         mapClick : true,
26940         /**
26941          * @event mapRightClick
26942          * Fires when right click the map.
26943          * @param {Roo.bootstrap.LocationPicker} this
26944          * @param {Map event} e
26945          */
26946         mapRightClick : true,
26947         /**
26948          * @event markerClick
26949          * Fires when click the marker.
26950          * @param {Roo.bootstrap.LocationPicker} this
26951          * @param {Map event} e
26952          */
26953         markerClick : true,
26954         /**
26955          * @event markerRightClick
26956          * Fires when right click the marker.
26957          * @param {Roo.bootstrap.LocationPicker} this
26958          * @param {Map event} e
26959          */
26960         markerRightClick : true,
26961         /**
26962          * @event OverlayViewDraw
26963          * Fires when OverlayView Draw
26964          * @param {Roo.bootstrap.LocationPicker} this
26965          */
26966         OverlayViewDraw : true,
26967         /**
26968          * @event OverlayViewOnAdd
26969          * Fires when OverlayView Draw
26970          * @param {Roo.bootstrap.LocationPicker} this
26971          */
26972         OverlayViewOnAdd : true,
26973         /**
26974          * @event OverlayViewOnRemove
26975          * Fires when OverlayView Draw
26976          * @param {Roo.bootstrap.LocationPicker} this
26977          */
26978         OverlayViewOnRemove : true,
26979         /**
26980          * @event OverlayViewShow
26981          * Fires when OverlayView Draw
26982          * @param {Roo.bootstrap.LocationPicker} this
26983          * @param {Pixel} cpx
26984          */
26985         OverlayViewShow : true,
26986         /**
26987          * @event OverlayViewHide
26988          * Fires when OverlayView Draw
26989          * @param {Roo.bootstrap.LocationPicker} this
26990          */
26991         OverlayViewHide : true,
26992         /**
26993          * @event loadexception
26994          * Fires when load google lib failed.
26995          * @param {Roo.bootstrap.LocationPicker} this
26996          */
26997         loadexception : true
26998     });
26999         
27000 };
27001
27002 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
27003     
27004     gMapContext: false,
27005     
27006     latitude: 0,
27007     longitude: 0,
27008     zoom: 15,
27009     mapTypeId: false,
27010     mapTypeControl: false,
27011     disableDoubleClickZoom: false,
27012     scrollwheel: true,
27013     streetViewControl: false,
27014     radius: 0,
27015     locationName: '',
27016     draggable: true,
27017     enableAutocomplete: false,
27018     enableReverseGeocode: true,
27019     markerTitle: '',
27020     
27021     getAutoCreate: function()
27022     {
27023
27024         var cfg = {
27025             tag: 'div',
27026             cls: 'roo-location-picker'
27027         };
27028         
27029         return cfg
27030     },
27031     
27032     initEvents: function(ct, position)
27033     {       
27034         if(!this.el.getWidth() || this.isApplied()){
27035             return;
27036         }
27037         
27038         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27039         
27040         this.initial();
27041     },
27042     
27043     initial: function()
27044     {
27045         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27046             this.fireEvent('loadexception', this);
27047             return;
27048         }
27049         
27050         if(!this.mapTypeId){
27051             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27052         }
27053         
27054         this.gMapContext = this.GMapContext();
27055         
27056         this.initOverlayView();
27057         
27058         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27059         
27060         var _this = this;
27061                 
27062         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27063             _this.setPosition(_this.gMapContext.marker.position);
27064         });
27065         
27066         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27067             _this.fireEvent('mapClick', this, event);
27068             
27069         });
27070
27071         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27072             _this.fireEvent('mapRightClick', this, event);
27073             
27074         });
27075         
27076         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27077             _this.fireEvent('markerClick', this, event);
27078             
27079         });
27080
27081         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27082             _this.fireEvent('markerRightClick', this, event);
27083             
27084         });
27085         
27086         this.setPosition(this.gMapContext.location);
27087         
27088         this.fireEvent('initial', this, this.gMapContext.location);
27089     },
27090     
27091     initOverlayView: function()
27092     {
27093         var _this = this;
27094         
27095         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27096             
27097             draw: function()
27098             {
27099                 _this.fireEvent('OverlayViewDraw', _this);
27100             },
27101             
27102             onAdd: function()
27103             {
27104                 _this.fireEvent('OverlayViewOnAdd', _this);
27105             },
27106             
27107             onRemove: function()
27108             {
27109                 _this.fireEvent('OverlayViewOnRemove', _this);
27110             },
27111             
27112             show: function(cpx)
27113             {
27114                 _this.fireEvent('OverlayViewShow', _this, cpx);
27115             },
27116             
27117             hide: function()
27118             {
27119                 _this.fireEvent('OverlayViewHide', _this);
27120             }
27121             
27122         });
27123     },
27124     
27125     fromLatLngToContainerPixel: function(event)
27126     {
27127         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27128     },
27129     
27130     isApplied: function() 
27131     {
27132         return this.getGmapContext() == false ? false : true;
27133     },
27134     
27135     getGmapContext: function() 
27136     {
27137         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27138     },
27139     
27140     GMapContext: function() 
27141     {
27142         var position = new google.maps.LatLng(this.latitude, this.longitude);
27143         
27144         var _map = new google.maps.Map(this.el.dom, {
27145             center: position,
27146             zoom: this.zoom,
27147             mapTypeId: this.mapTypeId,
27148             mapTypeControl: this.mapTypeControl,
27149             disableDoubleClickZoom: this.disableDoubleClickZoom,
27150             scrollwheel: this.scrollwheel,
27151             streetViewControl: this.streetViewControl,
27152             locationName: this.locationName,
27153             draggable: this.draggable,
27154             enableAutocomplete: this.enableAutocomplete,
27155             enableReverseGeocode: this.enableReverseGeocode
27156         });
27157         
27158         var _marker = new google.maps.Marker({
27159             position: position,
27160             map: _map,
27161             title: this.markerTitle,
27162             draggable: this.draggable
27163         });
27164         
27165         return {
27166             map: _map,
27167             marker: _marker,
27168             circle: null,
27169             location: position,
27170             radius: this.radius,
27171             locationName: this.locationName,
27172             addressComponents: {
27173                 formatted_address: null,
27174                 addressLine1: null,
27175                 addressLine2: null,
27176                 streetName: null,
27177                 streetNumber: null,
27178                 city: null,
27179                 district: null,
27180                 state: null,
27181                 stateOrProvince: null
27182             },
27183             settings: this,
27184             domContainer: this.el.dom,
27185             geodecoder: new google.maps.Geocoder()
27186         };
27187     },
27188     
27189     drawCircle: function(center, radius, options) 
27190     {
27191         if (this.gMapContext.circle != null) {
27192             this.gMapContext.circle.setMap(null);
27193         }
27194         if (radius > 0) {
27195             radius *= 1;
27196             options = Roo.apply({}, options, {
27197                 strokeColor: "#0000FF",
27198                 strokeOpacity: .35,
27199                 strokeWeight: 2,
27200                 fillColor: "#0000FF",
27201                 fillOpacity: .2
27202             });
27203             
27204             options.map = this.gMapContext.map;
27205             options.radius = radius;
27206             options.center = center;
27207             this.gMapContext.circle = new google.maps.Circle(options);
27208             return this.gMapContext.circle;
27209         }
27210         
27211         return null;
27212     },
27213     
27214     setPosition: function(location) 
27215     {
27216         this.gMapContext.location = location;
27217         this.gMapContext.marker.setPosition(location);
27218         this.gMapContext.map.panTo(location);
27219         this.drawCircle(location, this.gMapContext.radius, {});
27220         
27221         var _this = this;
27222         
27223         if (this.gMapContext.settings.enableReverseGeocode) {
27224             this.gMapContext.geodecoder.geocode({
27225                 latLng: this.gMapContext.location
27226             }, function(results, status) {
27227                 
27228                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27229                     _this.gMapContext.locationName = results[0].formatted_address;
27230                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27231                     
27232                     _this.fireEvent('positionchanged', this, location);
27233                 }
27234             });
27235             
27236             return;
27237         }
27238         
27239         this.fireEvent('positionchanged', this, location);
27240     },
27241     
27242     resize: function()
27243     {
27244         google.maps.event.trigger(this.gMapContext.map, "resize");
27245         
27246         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27247         
27248         this.fireEvent('resize', this);
27249     },
27250     
27251     setPositionByLatLng: function(latitude, longitude)
27252     {
27253         this.setPosition(new google.maps.LatLng(latitude, longitude));
27254     },
27255     
27256     getCurrentPosition: function() 
27257     {
27258         return {
27259             latitude: this.gMapContext.location.lat(),
27260             longitude: this.gMapContext.location.lng()
27261         };
27262     },
27263     
27264     getAddressName: function() 
27265     {
27266         return this.gMapContext.locationName;
27267     },
27268     
27269     getAddressComponents: function() 
27270     {
27271         return this.gMapContext.addressComponents;
27272     },
27273     
27274     address_component_from_google_geocode: function(address_components) 
27275     {
27276         var result = {};
27277         
27278         for (var i = 0; i < address_components.length; i++) {
27279             var component = address_components[i];
27280             if (component.types.indexOf("postal_code") >= 0) {
27281                 result.postalCode = component.short_name;
27282             } else if (component.types.indexOf("street_number") >= 0) {
27283                 result.streetNumber = component.short_name;
27284             } else if (component.types.indexOf("route") >= 0) {
27285                 result.streetName = component.short_name;
27286             } else if (component.types.indexOf("neighborhood") >= 0) {
27287                 result.city = component.short_name;
27288             } else if (component.types.indexOf("locality") >= 0) {
27289                 result.city = component.short_name;
27290             } else if (component.types.indexOf("sublocality") >= 0) {
27291                 result.district = component.short_name;
27292             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27293                 result.stateOrProvince = component.short_name;
27294             } else if (component.types.indexOf("country") >= 0) {
27295                 result.country = component.short_name;
27296             }
27297         }
27298         
27299         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27300         result.addressLine2 = "";
27301         return result;
27302     },
27303     
27304     setZoomLevel: function(zoom)
27305     {
27306         this.gMapContext.map.setZoom(zoom);
27307     },
27308     
27309     show: function()
27310     {
27311         if(!this.el){
27312             return;
27313         }
27314         
27315         this.el.show();
27316         
27317         this.resize();
27318         
27319         this.fireEvent('show', this);
27320     },
27321     
27322     hide: function()
27323     {
27324         if(!this.el){
27325             return;
27326         }
27327         
27328         this.el.hide();
27329         
27330         this.fireEvent('hide', this);
27331     }
27332     
27333 });
27334
27335 Roo.apply(Roo.bootstrap.LocationPicker, {
27336     
27337     OverlayView : function(map, options)
27338     {
27339         options = options || {};
27340         
27341         this.setMap(map);
27342     }
27343     
27344     
27345 });/**
27346  * @class Roo.bootstrap.Alert
27347  * @extends Roo.bootstrap.Component
27348  * Bootstrap Alert class - shows an alert area box
27349  * eg
27350  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27351   Enter a valid email address
27352 </div>
27353  * @licence LGPL
27354  * @cfg {String} title The title of alert
27355  * @cfg {String} html The content of alert
27356  * @cfg {String} weight (  success | info | warning | danger )
27357  * @cfg {String} faicon font-awesomeicon
27358  * 
27359  * @constructor
27360  * Create a new alert
27361  * @param {Object} config The config object
27362  */
27363
27364
27365 Roo.bootstrap.Alert = function(config){
27366     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27367     
27368 };
27369
27370 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27371     
27372     title: '',
27373     html: '',
27374     weight: false,
27375     faicon: false,
27376     
27377     getAutoCreate : function()
27378     {
27379         
27380         var cfg = {
27381             tag : 'div',
27382             cls : 'alert',
27383             cn : [
27384                 {
27385                     tag : 'i',
27386                     cls : 'roo-alert-icon'
27387                     
27388                 },
27389                 {
27390                     tag : 'b',
27391                     cls : 'roo-alert-title',
27392                     html : this.title
27393                 },
27394                 {
27395                     tag : 'span',
27396                     cls : 'roo-alert-text',
27397                     html : this.html
27398                 }
27399             ]
27400         };
27401         
27402         if(this.faicon){
27403             cfg.cn[0].cls += ' fa ' + this.faicon;
27404         }
27405         
27406         if(this.weight){
27407             cfg.cls += ' alert-' + this.weight;
27408         }
27409         
27410         return cfg;
27411     },
27412     
27413     initEvents: function() 
27414     {
27415         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27416     },
27417     
27418     setTitle : function(str)
27419     {
27420         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27421     },
27422     
27423     setText : function(str)
27424     {
27425         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27426     },
27427     
27428     setWeight : function(weight)
27429     {
27430         if(this.weight){
27431             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27432         }
27433         
27434         this.weight = weight;
27435         
27436         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27437     },
27438     
27439     setIcon : function(icon)
27440     {
27441         if(this.faicon){
27442             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27443         }
27444         
27445         this.faicon = icon;
27446         
27447         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27448     },
27449     
27450     hide: function() 
27451     {
27452         this.el.hide();   
27453     },
27454     
27455     show: function() 
27456     {  
27457         this.el.show();   
27458     }
27459     
27460 });
27461
27462  
27463 /*
27464 * Licence: LGPL
27465 */
27466
27467 /**
27468  * @class Roo.bootstrap.UploadCropbox
27469  * @extends Roo.bootstrap.Component
27470  * Bootstrap UploadCropbox class
27471  * @cfg {String} emptyText show when image has been loaded
27472  * @cfg {String} rotateNotify show when image too small to rotate
27473  * @cfg {Number} errorTimeout default 3000
27474  * @cfg {Number} minWidth default 300
27475  * @cfg {Number} minHeight default 300
27476  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27477  * @cfg {Boolean} isDocument (true|false) default false
27478  * @cfg {String} url action url
27479  * @cfg {String} paramName default 'imageUpload'
27480  * @cfg {String} method default POST
27481  * @cfg {Boolean} loadMask (true|false) default true
27482  * @cfg {Boolean} loadingText default 'Loading...'
27483  * 
27484  * @constructor
27485  * Create a new UploadCropbox
27486  * @param {Object} config The config object
27487  */
27488
27489 Roo.bootstrap.UploadCropbox = function(config){
27490     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27491     
27492     this.addEvents({
27493         /**
27494          * @event beforeselectfile
27495          * Fire before select file
27496          * @param {Roo.bootstrap.UploadCropbox} this
27497          */
27498         "beforeselectfile" : true,
27499         /**
27500          * @event initial
27501          * Fire after initEvent
27502          * @param {Roo.bootstrap.UploadCropbox} this
27503          */
27504         "initial" : true,
27505         /**
27506          * @event crop
27507          * Fire after initEvent
27508          * @param {Roo.bootstrap.UploadCropbox} this
27509          * @param {String} data
27510          */
27511         "crop" : true,
27512         /**
27513          * @event prepare
27514          * Fire when preparing the file data
27515          * @param {Roo.bootstrap.UploadCropbox} this
27516          * @param {Object} file
27517          */
27518         "prepare" : true,
27519         /**
27520          * @event exception
27521          * Fire when get exception
27522          * @param {Roo.bootstrap.UploadCropbox} this
27523          * @param {XMLHttpRequest} xhr
27524          */
27525         "exception" : true,
27526         /**
27527          * @event beforeloadcanvas
27528          * Fire before load the canvas
27529          * @param {Roo.bootstrap.UploadCropbox} this
27530          * @param {String} src
27531          */
27532         "beforeloadcanvas" : true,
27533         /**
27534          * @event trash
27535          * Fire when trash image
27536          * @param {Roo.bootstrap.UploadCropbox} this
27537          */
27538         "trash" : true,
27539         /**
27540          * @event download
27541          * Fire when download the image
27542          * @param {Roo.bootstrap.UploadCropbox} this
27543          */
27544         "download" : true,
27545         /**
27546          * @event footerbuttonclick
27547          * Fire when footerbuttonclick
27548          * @param {Roo.bootstrap.UploadCropbox} this
27549          * @param {String} type
27550          */
27551         "footerbuttonclick" : true,
27552         /**
27553          * @event resize
27554          * Fire when resize
27555          * @param {Roo.bootstrap.UploadCropbox} this
27556          */
27557         "resize" : true,
27558         /**
27559          * @event rotate
27560          * Fire when rotate the image
27561          * @param {Roo.bootstrap.UploadCropbox} this
27562          * @param {String} pos
27563          */
27564         "rotate" : true,
27565         /**
27566          * @event inspect
27567          * Fire when inspect the file
27568          * @param {Roo.bootstrap.UploadCropbox} this
27569          * @param {Object} file
27570          */
27571         "inspect" : true,
27572         /**
27573          * @event upload
27574          * Fire when xhr upload the file
27575          * @param {Roo.bootstrap.UploadCropbox} this
27576          * @param {Object} data
27577          */
27578         "upload" : true,
27579         /**
27580          * @event arrange
27581          * Fire when arrange the file data
27582          * @param {Roo.bootstrap.UploadCropbox} this
27583          * @param {Object} formData
27584          */
27585         "arrange" : true
27586     });
27587     
27588     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27589 };
27590
27591 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27592     
27593     emptyText : 'Click to upload image',
27594     rotateNotify : 'Image is too small to rotate',
27595     errorTimeout : 3000,
27596     scale : 0,
27597     baseScale : 1,
27598     rotate : 0,
27599     dragable : false,
27600     pinching : false,
27601     mouseX : 0,
27602     mouseY : 0,
27603     cropData : false,
27604     minWidth : 300,
27605     minHeight : 300,
27606     file : false,
27607     exif : {},
27608     baseRotate : 1,
27609     cropType : 'image/jpeg',
27610     buttons : false,
27611     canvasLoaded : false,
27612     isDocument : false,
27613     method : 'POST',
27614     paramName : 'imageUpload',
27615     loadMask : true,
27616     loadingText : 'Loading...',
27617     maskEl : false,
27618     
27619     getAutoCreate : function()
27620     {
27621         var cfg = {
27622             tag : 'div',
27623             cls : 'roo-upload-cropbox',
27624             cn : [
27625                 {
27626                     tag : 'input',
27627                     cls : 'roo-upload-cropbox-selector',
27628                     type : 'file'
27629                 },
27630                 {
27631                     tag : 'div',
27632                     cls : 'roo-upload-cropbox-body',
27633                     style : 'cursor:pointer',
27634                     cn : [
27635                         {
27636                             tag : 'div',
27637                             cls : 'roo-upload-cropbox-preview'
27638                         },
27639                         {
27640                             tag : 'div',
27641                             cls : 'roo-upload-cropbox-thumb'
27642                         },
27643                         {
27644                             tag : 'div',
27645                             cls : 'roo-upload-cropbox-empty-notify',
27646                             html : this.emptyText
27647                         },
27648                         {
27649                             tag : 'div',
27650                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27651                             html : this.rotateNotify
27652                         }
27653                     ]
27654                 },
27655                 {
27656                     tag : 'div',
27657                     cls : 'roo-upload-cropbox-footer',
27658                     cn : {
27659                         tag : 'div',
27660                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27661                         cn : []
27662                     }
27663                 }
27664             ]
27665         };
27666         
27667         return cfg;
27668     },
27669     
27670     onRender : function(ct, position)
27671     {
27672         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27673         
27674         if (this.buttons.length) {
27675             
27676             Roo.each(this.buttons, function(bb) {
27677                 
27678                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27679                 
27680                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27681                 
27682             }, this);
27683         }
27684         
27685         if(this.loadMask){
27686             this.maskEl = this.el;
27687         }
27688     },
27689     
27690     initEvents : function()
27691     {
27692         this.urlAPI = (window.createObjectURL && window) || 
27693                                 (window.URL && URL.revokeObjectURL && URL) || 
27694                                 (window.webkitURL && webkitURL);
27695                         
27696         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27697         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27698         
27699         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27700         this.selectorEl.hide();
27701         
27702         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27703         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27704         
27705         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27706         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27707         this.thumbEl.hide();
27708         
27709         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27710         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27711         
27712         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27713         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27714         this.errorEl.hide();
27715         
27716         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27717         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27718         this.footerEl.hide();
27719         
27720         this.setThumbBoxSize();
27721         
27722         this.bind();
27723         
27724         this.resize();
27725         
27726         this.fireEvent('initial', this);
27727     },
27728
27729     bind : function()
27730     {
27731         var _this = this;
27732         
27733         window.addEventListener("resize", function() { _this.resize(); } );
27734         
27735         this.bodyEl.on('click', this.beforeSelectFile, this);
27736         
27737         if(Roo.isTouch){
27738             this.bodyEl.on('touchstart', this.onTouchStart, this);
27739             this.bodyEl.on('touchmove', this.onTouchMove, this);
27740             this.bodyEl.on('touchend', this.onTouchEnd, this);
27741         }
27742         
27743         if(!Roo.isTouch){
27744             this.bodyEl.on('mousedown', this.onMouseDown, this);
27745             this.bodyEl.on('mousemove', this.onMouseMove, this);
27746             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27747             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27748             Roo.get(document).on('mouseup', this.onMouseUp, this);
27749         }
27750         
27751         this.selectorEl.on('change', this.onFileSelected, this);
27752     },
27753     
27754     reset : function()
27755     {    
27756         this.scale = 0;
27757         this.baseScale = 1;
27758         this.rotate = 0;
27759         this.baseRotate = 1;
27760         this.dragable = false;
27761         this.pinching = false;
27762         this.mouseX = 0;
27763         this.mouseY = 0;
27764         this.cropData = false;
27765         this.notifyEl.dom.innerHTML = this.emptyText;
27766         
27767         this.selectorEl.dom.value = '';
27768         
27769     },
27770     
27771     resize : function()
27772     {
27773         if(this.fireEvent('resize', this) != false){
27774             this.setThumbBoxPosition();
27775             this.setCanvasPosition();
27776         }
27777     },
27778     
27779     onFooterButtonClick : function(e, el, o, type)
27780     {
27781         switch (type) {
27782             case 'rotate-left' :
27783                 this.onRotateLeft(e);
27784                 break;
27785             case 'rotate-right' :
27786                 this.onRotateRight(e);
27787                 break;
27788             case 'picture' :
27789                 this.beforeSelectFile(e);
27790                 break;
27791             case 'trash' :
27792                 this.trash(e);
27793                 break;
27794             case 'crop' :
27795                 this.crop(e);
27796                 break;
27797             case 'download' :
27798                 this.download(e);
27799                 break;
27800             default :
27801                 break;
27802         }
27803         
27804         this.fireEvent('footerbuttonclick', this, type);
27805     },
27806     
27807     beforeSelectFile : function(e)
27808     {
27809         e.preventDefault();
27810         
27811         if(this.fireEvent('beforeselectfile', this) != false){
27812             this.selectorEl.dom.click();
27813         }
27814     },
27815     
27816     onFileSelected : function(e)
27817     {
27818         e.preventDefault();
27819         
27820         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27821             return;
27822         }
27823         
27824         var file = this.selectorEl.dom.files[0];
27825         
27826         if(this.fireEvent('inspect', this, file) != false){
27827             this.prepare(file);
27828         }
27829         
27830     },
27831     
27832     trash : function(e)
27833     {
27834         this.fireEvent('trash', this);
27835     },
27836     
27837     download : function(e)
27838     {
27839         this.fireEvent('download', this);
27840     },
27841     
27842     loadCanvas : function(src)
27843     {   
27844         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27845             
27846             this.reset();
27847             
27848             this.imageEl = document.createElement('img');
27849             
27850             var _this = this;
27851             
27852             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27853             
27854             this.imageEl.src = src;
27855         }
27856     },
27857     
27858     onLoadCanvas : function()
27859     {   
27860         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27861         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27862         
27863         this.bodyEl.un('click', this.beforeSelectFile, this);
27864         
27865         this.notifyEl.hide();
27866         this.thumbEl.show();
27867         this.footerEl.show();
27868         
27869         this.baseRotateLevel();
27870         
27871         if(this.isDocument){
27872             this.setThumbBoxSize();
27873         }
27874         
27875         this.setThumbBoxPosition();
27876         
27877         this.baseScaleLevel();
27878         
27879         this.draw();
27880         
27881         this.resize();
27882         
27883         this.canvasLoaded = true;
27884         
27885         if(this.loadMask){
27886             this.maskEl.unmask();
27887         }
27888         
27889     },
27890     
27891     setCanvasPosition : function()
27892     {   
27893         if(!this.canvasEl){
27894             return;
27895         }
27896         
27897         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27898         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27899         
27900         this.previewEl.setLeft(pw);
27901         this.previewEl.setTop(ph);
27902         
27903     },
27904     
27905     onMouseDown : function(e)
27906     {   
27907         e.stopEvent();
27908         
27909         this.dragable = true;
27910         this.pinching = false;
27911         
27912         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27913             this.dragable = false;
27914             return;
27915         }
27916         
27917         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27918         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27919         
27920     },
27921     
27922     onMouseMove : function(e)
27923     {   
27924         e.stopEvent();
27925         
27926         if(!this.canvasLoaded){
27927             return;
27928         }
27929         
27930         if (!this.dragable){
27931             return;
27932         }
27933         
27934         var minX = Math.ceil(this.thumbEl.getLeft(true));
27935         var minY = Math.ceil(this.thumbEl.getTop(true));
27936         
27937         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27938         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27939         
27940         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27941         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27942         
27943         x = x - this.mouseX;
27944         y = y - this.mouseY;
27945         
27946         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27947         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27948         
27949         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27950         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27951         
27952         this.previewEl.setLeft(bgX);
27953         this.previewEl.setTop(bgY);
27954         
27955         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27956         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27957     },
27958     
27959     onMouseUp : function(e)
27960     {   
27961         e.stopEvent();
27962         
27963         this.dragable = false;
27964     },
27965     
27966     onMouseWheel : function(e)
27967     {   
27968         e.stopEvent();
27969         
27970         this.startScale = this.scale;
27971         
27972         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27973         
27974         if(!this.zoomable()){
27975             this.scale = this.startScale;
27976             return;
27977         }
27978         
27979         this.draw();
27980         
27981         return;
27982     },
27983     
27984     zoomable : function()
27985     {
27986         var minScale = this.thumbEl.getWidth() / this.minWidth;
27987         
27988         if(this.minWidth < this.minHeight){
27989             minScale = this.thumbEl.getHeight() / this.minHeight;
27990         }
27991         
27992         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27993         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27994         
27995         if(
27996                 this.isDocument &&
27997                 (this.rotate == 0 || this.rotate == 180) && 
27998                 (
27999                     width > this.imageEl.OriginWidth || 
28000                     height > this.imageEl.OriginHeight ||
28001                     (width < this.minWidth && height < this.minHeight)
28002                 )
28003         ){
28004             return false;
28005         }
28006         
28007         if(
28008                 this.isDocument &&
28009                 (this.rotate == 90 || this.rotate == 270) && 
28010                 (
28011                     width > this.imageEl.OriginWidth || 
28012                     height > this.imageEl.OriginHeight ||
28013                     (width < this.minHeight && height < this.minWidth)
28014                 )
28015         ){
28016             return false;
28017         }
28018         
28019         if(
28020                 !this.isDocument &&
28021                 (this.rotate == 0 || this.rotate == 180) && 
28022                 (
28023                     width < this.minWidth || 
28024                     width > this.imageEl.OriginWidth || 
28025                     height < this.minHeight || 
28026                     height > this.imageEl.OriginHeight
28027                 )
28028         ){
28029             return false;
28030         }
28031         
28032         if(
28033                 !this.isDocument &&
28034                 (this.rotate == 90 || this.rotate == 270) && 
28035                 (
28036                     width < this.minHeight || 
28037                     width > this.imageEl.OriginWidth || 
28038                     height < this.minWidth || 
28039                     height > this.imageEl.OriginHeight
28040                 )
28041         ){
28042             return false;
28043         }
28044         
28045         return true;
28046         
28047     },
28048     
28049     onRotateLeft : function(e)
28050     {   
28051         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28052             
28053             var minScale = this.thumbEl.getWidth() / this.minWidth;
28054             
28055             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28056             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28057             
28058             this.startScale = this.scale;
28059             
28060             while (this.getScaleLevel() < minScale){
28061             
28062                 this.scale = this.scale + 1;
28063                 
28064                 if(!this.zoomable()){
28065                     break;
28066                 }
28067                 
28068                 if(
28069                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28070                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28071                 ){
28072                     continue;
28073                 }
28074                 
28075                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28076
28077                 this.draw();
28078                 
28079                 return;
28080             }
28081             
28082             this.scale = this.startScale;
28083             
28084             this.onRotateFail();
28085             
28086             return false;
28087         }
28088         
28089         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28090
28091         if(this.isDocument){
28092             this.setThumbBoxSize();
28093             this.setThumbBoxPosition();
28094             this.setCanvasPosition();
28095         }
28096         
28097         this.draw();
28098         
28099         this.fireEvent('rotate', this, 'left');
28100         
28101     },
28102     
28103     onRotateRight : function(e)
28104     {
28105         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28106             
28107             var minScale = this.thumbEl.getWidth() / this.minWidth;
28108         
28109             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28110             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28111             
28112             this.startScale = this.scale;
28113             
28114             while (this.getScaleLevel() < minScale){
28115             
28116                 this.scale = this.scale + 1;
28117                 
28118                 if(!this.zoomable()){
28119                     break;
28120                 }
28121                 
28122                 if(
28123                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28124                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28125                 ){
28126                     continue;
28127                 }
28128                 
28129                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28130
28131                 this.draw();
28132                 
28133                 return;
28134             }
28135             
28136             this.scale = this.startScale;
28137             
28138             this.onRotateFail();
28139             
28140             return false;
28141         }
28142         
28143         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28144
28145         if(this.isDocument){
28146             this.setThumbBoxSize();
28147             this.setThumbBoxPosition();
28148             this.setCanvasPosition();
28149         }
28150         
28151         this.draw();
28152         
28153         this.fireEvent('rotate', this, 'right');
28154     },
28155     
28156     onRotateFail : function()
28157     {
28158         this.errorEl.show(true);
28159         
28160         var _this = this;
28161         
28162         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28163     },
28164     
28165     draw : function()
28166     {
28167         this.previewEl.dom.innerHTML = '';
28168         
28169         var canvasEl = document.createElement("canvas");
28170         
28171         var contextEl = canvasEl.getContext("2d");
28172         
28173         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28174         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28175         var center = this.imageEl.OriginWidth / 2;
28176         
28177         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28178             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28179             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28180             center = this.imageEl.OriginHeight / 2;
28181         }
28182         
28183         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28184         
28185         contextEl.translate(center, center);
28186         contextEl.rotate(this.rotate * Math.PI / 180);
28187
28188         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28189         
28190         this.canvasEl = document.createElement("canvas");
28191         
28192         this.contextEl = this.canvasEl.getContext("2d");
28193         
28194         switch (this.rotate) {
28195             case 0 :
28196                 
28197                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28198                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28199                 
28200                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28201                 
28202                 break;
28203             case 90 : 
28204                 
28205                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28206                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28207                 
28208                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28209                     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);
28210                     break;
28211                 }
28212                 
28213                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28214                 
28215                 break;
28216             case 180 :
28217                 
28218                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28219                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28220                 
28221                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28222                     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);
28223                     break;
28224                 }
28225                 
28226                 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);
28227                 
28228                 break;
28229             case 270 :
28230                 
28231                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28232                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28233         
28234                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28235                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28236                     break;
28237                 }
28238                 
28239                 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);
28240                 
28241                 break;
28242             default : 
28243                 break;
28244         }
28245         
28246         this.previewEl.appendChild(this.canvasEl);
28247         
28248         this.setCanvasPosition();
28249     },
28250     
28251     crop : function()
28252     {
28253         if(!this.canvasLoaded){
28254             return;
28255         }
28256         
28257         var imageCanvas = document.createElement("canvas");
28258         
28259         var imageContext = imageCanvas.getContext("2d");
28260         
28261         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28262         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28263         
28264         var center = imageCanvas.width / 2;
28265         
28266         imageContext.translate(center, center);
28267         
28268         imageContext.rotate(this.rotate * Math.PI / 180);
28269         
28270         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28271         
28272         var canvas = document.createElement("canvas");
28273         
28274         var context = canvas.getContext("2d");
28275                 
28276         canvas.width = this.minWidth;
28277         canvas.height = this.minHeight;
28278
28279         switch (this.rotate) {
28280             case 0 :
28281                 
28282                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28283                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28284                 
28285                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28286                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28287                 
28288                 var targetWidth = this.minWidth - 2 * x;
28289                 var targetHeight = this.minHeight - 2 * y;
28290                 
28291                 var scale = 1;
28292                 
28293                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28294                     scale = targetWidth / width;
28295                 }
28296                 
28297                 if(x > 0 && y == 0){
28298                     scale = targetHeight / height;
28299                 }
28300                 
28301                 if(x > 0 && y > 0){
28302                     scale = targetWidth / width;
28303                     
28304                     if(width < height){
28305                         scale = targetHeight / height;
28306                     }
28307                 }
28308                 
28309                 context.scale(scale, scale);
28310                 
28311                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28312                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28313
28314                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28315                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28316
28317                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28318                 
28319                 break;
28320             case 90 : 
28321                 
28322                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28323                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28324                 
28325                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28326                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28327                 
28328                 var targetWidth = this.minWidth - 2 * x;
28329                 var targetHeight = this.minHeight - 2 * y;
28330                 
28331                 var scale = 1;
28332                 
28333                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28334                     scale = targetWidth / width;
28335                 }
28336                 
28337                 if(x > 0 && y == 0){
28338                     scale = targetHeight / height;
28339                 }
28340                 
28341                 if(x > 0 && y > 0){
28342                     scale = targetWidth / width;
28343                     
28344                     if(width < height){
28345                         scale = targetHeight / height;
28346                     }
28347                 }
28348                 
28349                 context.scale(scale, scale);
28350                 
28351                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28352                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28353
28354                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28355                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28356                 
28357                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28358                 
28359                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28360                 
28361                 break;
28362             case 180 :
28363                 
28364                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28365                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28366                 
28367                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28368                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28369                 
28370                 var targetWidth = this.minWidth - 2 * x;
28371                 var targetHeight = this.minHeight - 2 * y;
28372                 
28373                 var scale = 1;
28374                 
28375                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28376                     scale = targetWidth / width;
28377                 }
28378                 
28379                 if(x > 0 && y == 0){
28380                     scale = targetHeight / height;
28381                 }
28382                 
28383                 if(x > 0 && y > 0){
28384                     scale = targetWidth / width;
28385                     
28386                     if(width < height){
28387                         scale = targetHeight / height;
28388                     }
28389                 }
28390                 
28391                 context.scale(scale, scale);
28392                 
28393                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28394                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28395
28396                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28397                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28398
28399                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28400                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28401                 
28402                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28403                 
28404                 break;
28405             case 270 :
28406                 
28407                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28408                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28409                 
28410                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28411                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28412                 
28413                 var targetWidth = this.minWidth - 2 * x;
28414                 var targetHeight = this.minHeight - 2 * y;
28415                 
28416                 var scale = 1;
28417                 
28418                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28419                     scale = targetWidth / width;
28420                 }
28421                 
28422                 if(x > 0 && y == 0){
28423                     scale = targetHeight / height;
28424                 }
28425                 
28426                 if(x > 0 && y > 0){
28427                     scale = targetWidth / width;
28428                     
28429                     if(width < height){
28430                         scale = targetHeight / height;
28431                     }
28432                 }
28433                 
28434                 context.scale(scale, scale);
28435                 
28436                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28437                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28438
28439                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28440                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28441                 
28442                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28443                 
28444                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28445                 
28446                 break;
28447             default : 
28448                 break;
28449         }
28450         
28451         this.cropData = canvas.toDataURL(this.cropType);
28452         
28453         if(this.fireEvent('crop', this, this.cropData) !== false){
28454             this.process(this.file, this.cropData);
28455         }
28456         
28457         return;
28458         
28459     },
28460     
28461     setThumbBoxSize : function()
28462     {
28463         var width, height;
28464         
28465         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28466             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28467             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28468             
28469             this.minWidth = width;
28470             this.minHeight = height;
28471             
28472             if(this.rotate == 90 || this.rotate == 270){
28473                 this.minWidth = height;
28474                 this.minHeight = width;
28475             }
28476         }
28477         
28478         height = 300;
28479         width = Math.ceil(this.minWidth * height / this.minHeight);
28480         
28481         if(this.minWidth > this.minHeight){
28482             width = 300;
28483             height = Math.ceil(this.minHeight * width / this.minWidth);
28484         }
28485         
28486         this.thumbEl.setStyle({
28487             width : width + 'px',
28488             height : height + 'px'
28489         });
28490
28491         return;
28492             
28493     },
28494     
28495     setThumbBoxPosition : function()
28496     {
28497         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28498         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28499         
28500         this.thumbEl.setLeft(x);
28501         this.thumbEl.setTop(y);
28502         
28503     },
28504     
28505     baseRotateLevel : function()
28506     {
28507         this.baseRotate = 1;
28508         
28509         if(
28510                 typeof(this.exif) != 'undefined' &&
28511                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28512                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28513         ){
28514             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28515         }
28516         
28517         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28518         
28519     },
28520     
28521     baseScaleLevel : function()
28522     {
28523         var width, height;
28524         
28525         if(this.isDocument){
28526             
28527             if(this.baseRotate == 6 || this.baseRotate == 8){
28528             
28529                 height = this.thumbEl.getHeight();
28530                 this.baseScale = height / this.imageEl.OriginWidth;
28531
28532                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28533                     width = this.thumbEl.getWidth();
28534                     this.baseScale = width / this.imageEl.OriginHeight;
28535                 }
28536
28537                 return;
28538             }
28539
28540             height = this.thumbEl.getHeight();
28541             this.baseScale = height / this.imageEl.OriginHeight;
28542
28543             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28544                 width = this.thumbEl.getWidth();
28545                 this.baseScale = width / this.imageEl.OriginWidth;
28546             }
28547
28548             return;
28549         }
28550         
28551         if(this.baseRotate == 6 || this.baseRotate == 8){
28552             
28553             width = this.thumbEl.getHeight();
28554             this.baseScale = width / this.imageEl.OriginHeight;
28555             
28556             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28557                 height = this.thumbEl.getWidth();
28558                 this.baseScale = height / this.imageEl.OriginHeight;
28559             }
28560             
28561             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28562                 height = this.thumbEl.getWidth();
28563                 this.baseScale = height / this.imageEl.OriginHeight;
28564                 
28565                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28566                     width = this.thumbEl.getHeight();
28567                     this.baseScale = width / this.imageEl.OriginWidth;
28568                 }
28569             }
28570             
28571             return;
28572         }
28573         
28574         width = this.thumbEl.getWidth();
28575         this.baseScale = width / this.imageEl.OriginWidth;
28576         
28577         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28578             height = this.thumbEl.getHeight();
28579             this.baseScale = height / this.imageEl.OriginHeight;
28580         }
28581         
28582         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28583             
28584             height = this.thumbEl.getHeight();
28585             this.baseScale = height / this.imageEl.OriginHeight;
28586             
28587             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28588                 width = this.thumbEl.getWidth();
28589                 this.baseScale = width / this.imageEl.OriginWidth;
28590             }
28591             
28592         }
28593         
28594         return;
28595     },
28596     
28597     getScaleLevel : function()
28598     {
28599         return this.baseScale * Math.pow(1.1, this.scale);
28600     },
28601     
28602     onTouchStart : function(e)
28603     {
28604         if(!this.canvasLoaded){
28605             this.beforeSelectFile(e);
28606             return;
28607         }
28608         
28609         var touches = e.browserEvent.touches;
28610         
28611         if(!touches){
28612             return;
28613         }
28614         
28615         if(touches.length == 1){
28616             this.onMouseDown(e);
28617             return;
28618         }
28619         
28620         if(touches.length != 2){
28621             return;
28622         }
28623         
28624         var coords = [];
28625         
28626         for(var i = 0, finger; finger = touches[i]; i++){
28627             coords.push(finger.pageX, finger.pageY);
28628         }
28629         
28630         var x = Math.pow(coords[0] - coords[2], 2);
28631         var y = Math.pow(coords[1] - coords[3], 2);
28632         
28633         this.startDistance = Math.sqrt(x + y);
28634         
28635         this.startScale = this.scale;
28636         
28637         this.pinching = true;
28638         this.dragable = false;
28639         
28640     },
28641     
28642     onTouchMove : function(e)
28643     {
28644         if(!this.pinching && !this.dragable){
28645             return;
28646         }
28647         
28648         var touches = e.browserEvent.touches;
28649         
28650         if(!touches){
28651             return;
28652         }
28653         
28654         if(this.dragable){
28655             this.onMouseMove(e);
28656             return;
28657         }
28658         
28659         var coords = [];
28660         
28661         for(var i = 0, finger; finger = touches[i]; i++){
28662             coords.push(finger.pageX, finger.pageY);
28663         }
28664         
28665         var x = Math.pow(coords[0] - coords[2], 2);
28666         var y = Math.pow(coords[1] - coords[3], 2);
28667         
28668         this.endDistance = Math.sqrt(x + y);
28669         
28670         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28671         
28672         if(!this.zoomable()){
28673             this.scale = this.startScale;
28674             return;
28675         }
28676         
28677         this.draw();
28678         
28679     },
28680     
28681     onTouchEnd : function(e)
28682     {
28683         this.pinching = false;
28684         this.dragable = false;
28685         
28686     },
28687     
28688     process : function(file, crop)
28689     {
28690         if(this.loadMask){
28691             this.maskEl.mask(this.loadingText);
28692         }
28693         
28694         this.xhr = new XMLHttpRequest();
28695         
28696         file.xhr = this.xhr;
28697
28698         this.xhr.open(this.method, this.url, true);
28699         
28700         var headers = {
28701             "Accept": "application/json",
28702             "Cache-Control": "no-cache",
28703             "X-Requested-With": "XMLHttpRequest"
28704         };
28705         
28706         for (var headerName in headers) {
28707             var headerValue = headers[headerName];
28708             if (headerValue) {
28709                 this.xhr.setRequestHeader(headerName, headerValue);
28710             }
28711         }
28712         
28713         var _this = this;
28714         
28715         this.xhr.onload = function()
28716         {
28717             _this.xhrOnLoad(_this.xhr);
28718         }
28719         
28720         this.xhr.onerror = function()
28721         {
28722             _this.xhrOnError(_this.xhr);
28723         }
28724         
28725         var formData = new FormData();
28726
28727         formData.append('returnHTML', 'NO');
28728         
28729         if(crop){
28730             formData.append('crop', crop);
28731         }
28732         
28733         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28734             formData.append(this.paramName, file, file.name);
28735         }
28736         
28737         if(typeof(file.filename) != 'undefined'){
28738             formData.append('filename', file.filename);
28739         }
28740         
28741         if(typeof(file.mimetype) != 'undefined'){
28742             formData.append('mimetype', file.mimetype);
28743         }
28744         
28745         if(this.fireEvent('arrange', this, formData) != false){
28746             this.xhr.send(formData);
28747         };
28748     },
28749     
28750     xhrOnLoad : function(xhr)
28751     {
28752         if(this.loadMask){
28753             this.maskEl.unmask();
28754         }
28755         
28756         if (xhr.readyState !== 4) {
28757             this.fireEvent('exception', this, xhr);
28758             return;
28759         }
28760
28761         var response = Roo.decode(xhr.responseText);
28762         
28763         if(!response.success){
28764             this.fireEvent('exception', this, xhr);
28765             return;
28766         }
28767         
28768         var response = Roo.decode(xhr.responseText);
28769         
28770         this.fireEvent('upload', this, response);
28771         
28772     },
28773     
28774     xhrOnError : function()
28775     {
28776         if(this.loadMask){
28777             this.maskEl.unmask();
28778         }
28779         
28780         Roo.log('xhr on error');
28781         
28782         var response = Roo.decode(xhr.responseText);
28783           
28784         Roo.log(response);
28785         
28786     },
28787     
28788     prepare : function(file)
28789     {   
28790         if(this.loadMask){
28791             this.maskEl.mask(this.loadingText);
28792         }
28793         
28794         this.file = false;
28795         this.exif = {};
28796         
28797         if(typeof(file) === 'string'){
28798             this.loadCanvas(file);
28799             return;
28800         }
28801         
28802         if(!file || !this.urlAPI){
28803             return;
28804         }
28805         
28806         this.file = file;
28807         this.cropType = file.type;
28808         
28809         var _this = this;
28810         
28811         if(this.fireEvent('prepare', this, this.file) != false){
28812             
28813             var reader = new FileReader();
28814             
28815             reader.onload = function (e) {
28816                 if (e.target.error) {
28817                     Roo.log(e.target.error);
28818                     return;
28819                 }
28820                 
28821                 var buffer = e.target.result,
28822                     dataView = new DataView(buffer),
28823                     offset = 2,
28824                     maxOffset = dataView.byteLength - 4,
28825                     markerBytes,
28826                     markerLength;
28827                 
28828                 if (dataView.getUint16(0) === 0xffd8) {
28829                     while (offset < maxOffset) {
28830                         markerBytes = dataView.getUint16(offset);
28831                         
28832                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28833                             markerLength = dataView.getUint16(offset + 2) + 2;
28834                             if (offset + markerLength > dataView.byteLength) {
28835                                 Roo.log('Invalid meta data: Invalid segment size.');
28836                                 break;
28837                             }
28838                             
28839                             if(markerBytes == 0xffe1){
28840                                 _this.parseExifData(
28841                                     dataView,
28842                                     offset,
28843                                     markerLength
28844                                 );
28845                             }
28846                             
28847                             offset += markerLength;
28848                             
28849                             continue;
28850                         }
28851                         
28852                         break;
28853                     }
28854                     
28855                 }
28856                 
28857                 var url = _this.urlAPI.createObjectURL(_this.file);
28858                 
28859                 _this.loadCanvas(url);
28860                 
28861                 return;
28862             }
28863             
28864             reader.readAsArrayBuffer(this.file);
28865             
28866         }
28867         
28868     },
28869     
28870     parseExifData : function(dataView, offset, length)
28871     {
28872         var tiffOffset = offset + 10,
28873             littleEndian,
28874             dirOffset;
28875     
28876         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28877             // No Exif data, might be XMP data instead
28878             return;
28879         }
28880         
28881         // Check for the ASCII code for "Exif" (0x45786966):
28882         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28883             // No Exif data, might be XMP data instead
28884             return;
28885         }
28886         if (tiffOffset + 8 > dataView.byteLength) {
28887             Roo.log('Invalid Exif data: Invalid segment size.');
28888             return;
28889         }
28890         // Check for the two null bytes:
28891         if (dataView.getUint16(offset + 8) !== 0x0000) {
28892             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28893             return;
28894         }
28895         // Check the byte alignment:
28896         switch (dataView.getUint16(tiffOffset)) {
28897         case 0x4949:
28898             littleEndian = true;
28899             break;
28900         case 0x4D4D:
28901             littleEndian = false;
28902             break;
28903         default:
28904             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28905             return;
28906         }
28907         // Check for the TIFF tag marker (0x002A):
28908         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28909             Roo.log('Invalid Exif data: Missing TIFF marker.');
28910             return;
28911         }
28912         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28913         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28914         
28915         this.parseExifTags(
28916             dataView,
28917             tiffOffset,
28918             tiffOffset + dirOffset,
28919             littleEndian
28920         );
28921     },
28922     
28923     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28924     {
28925         var tagsNumber,
28926             dirEndOffset,
28927             i;
28928         if (dirOffset + 6 > dataView.byteLength) {
28929             Roo.log('Invalid Exif data: Invalid directory offset.');
28930             return;
28931         }
28932         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28933         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28934         if (dirEndOffset + 4 > dataView.byteLength) {
28935             Roo.log('Invalid Exif data: Invalid directory size.');
28936             return;
28937         }
28938         for (i = 0; i < tagsNumber; i += 1) {
28939             this.parseExifTag(
28940                 dataView,
28941                 tiffOffset,
28942                 dirOffset + 2 + 12 * i, // tag offset
28943                 littleEndian
28944             );
28945         }
28946         // Return the offset to the next directory:
28947         return dataView.getUint32(dirEndOffset, littleEndian);
28948     },
28949     
28950     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28951     {
28952         var tag = dataView.getUint16(offset, littleEndian);
28953         
28954         this.exif[tag] = this.getExifValue(
28955             dataView,
28956             tiffOffset,
28957             offset,
28958             dataView.getUint16(offset + 2, littleEndian), // tag type
28959             dataView.getUint32(offset + 4, littleEndian), // tag length
28960             littleEndian
28961         );
28962     },
28963     
28964     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28965     {
28966         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28967             tagSize,
28968             dataOffset,
28969             values,
28970             i,
28971             str,
28972             c;
28973     
28974         if (!tagType) {
28975             Roo.log('Invalid Exif data: Invalid tag type.');
28976             return;
28977         }
28978         
28979         tagSize = tagType.size * length;
28980         // Determine if the value is contained in the dataOffset bytes,
28981         // or if the value at the dataOffset is a pointer to the actual data:
28982         dataOffset = tagSize > 4 ?
28983                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28984         if (dataOffset + tagSize > dataView.byteLength) {
28985             Roo.log('Invalid Exif data: Invalid data offset.');
28986             return;
28987         }
28988         if (length === 1) {
28989             return tagType.getValue(dataView, dataOffset, littleEndian);
28990         }
28991         values = [];
28992         for (i = 0; i < length; i += 1) {
28993             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28994         }
28995         
28996         if (tagType.ascii) {
28997             str = '';
28998             // Concatenate the chars:
28999             for (i = 0; i < values.length; i += 1) {
29000                 c = values[i];
29001                 // Ignore the terminating NULL byte(s):
29002                 if (c === '\u0000') {
29003                     break;
29004                 }
29005                 str += c;
29006             }
29007             return str;
29008         }
29009         return values;
29010     }
29011     
29012 });
29013
29014 Roo.apply(Roo.bootstrap.UploadCropbox, {
29015     tags : {
29016         'Orientation': 0x0112
29017     },
29018     
29019     Orientation: {
29020             1: 0, //'top-left',
29021 //            2: 'top-right',
29022             3: 180, //'bottom-right',
29023 //            4: 'bottom-left',
29024 //            5: 'left-top',
29025             6: 90, //'right-top',
29026 //            7: 'right-bottom',
29027             8: 270 //'left-bottom'
29028     },
29029     
29030     exifTagTypes : {
29031         // byte, 8-bit unsigned int:
29032         1: {
29033             getValue: function (dataView, dataOffset) {
29034                 return dataView.getUint8(dataOffset);
29035             },
29036             size: 1
29037         },
29038         // ascii, 8-bit byte:
29039         2: {
29040             getValue: function (dataView, dataOffset) {
29041                 return String.fromCharCode(dataView.getUint8(dataOffset));
29042             },
29043             size: 1,
29044             ascii: true
29045         },
29046         // short, 16 bit int:
29047         3: {
29048             getValue: function (dataView, dataOffset, littleEndian) {
29049                 return dataView.getUint16(dataOffset, littleEndian);
29050             },
29051             size: 2
29052         },
29053         // long, 32 bit int:
29054         4: {
29055             getValue: function (dataView, dataOffset, littleEndian) {
29056                 return dataView.getUint32(dataOffset, littleEndian);
29057             },
29058             size: 4
29059         },
29060         // rational = two long values, first is numerator, second is denominator:
29061         5: {
29062             getValue: function (dataView, dataOffset, littleEndian) {
29063                 return dataView.getUint32(dataOffset, littleEndian) /
29064                     dataView.getUint32(dataOffset + 4, littleEndian);
29065             },
29066             size: 8
29067         },
29068         // slong, 32 bit signed int:
29069         9: {
29070             getValue: function (dataView, dataOffset, littleEndian) {
29071                 return dataView.getInt32(dataOffset, littleEndian);
29072             },
29073             size: 4
29074         },
29075         // srational, two slongs, first is numerator, second is denominator:
29076         10: {
29077             getValue: function (dataView, dataOffset, littleEndian) {
29078                 return dataView.getInt32(dataOffset, littleEndian) /
29079                     dataView.getInt32(dataOffset + 4, littleEndian);
29080             },
29081             size: 8
29082         }
29083     },
29084     
29085     footer : {
29086         STANDARD : [
29087             {
29088                 tag : 'div',
29089                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29090                 action : 'rotate-left',
29091                 cn : [
29092                     {
29093                         tag : 'button',
29094                         cls : 'btn btn-default',
29095                         html : '<i class="fa fa-undo"></i>'
29096                     }
29097                 ]
29098             },
29099             {
29100                 tag : 'div',
29101                 cls : 'btn-group roo-upload-cropbox-picture',
29102                 action : 'picture',
29103                 cn : [
29104                     {
29105                         tag : 'button',
29106                         cls : 'btn btn-default',
29107                         html : '<i class="fa fa-picture-o"></i>'
29108                     }
29109                 ]
29110             },
29111             {
29112                 tag : 'div',
29113                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29114                 action : 'rotate-right',
29115                 cn : [
29116                     {
29117                         tag : 'button',
29118                         cls : 'btn btn-default',
29119                         html : '<i class="fa fa-repeat"></i>'
29120                     }
29121                 ]
29122             }
29123         ],
29124         DOCUMENT : [
29125             {
29126                 tag : 'div',
29127                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29128                 action : 'rotate-left',
29129                 cn : [
29130                     {
29131                         tag : 'button',
29132                         cls : 'btn btn-default',
29133                         html : '<i class="fa fa-undo"></i>'
29134                     }
29135                 ]
29136             },
29137             {
29138                 tag : 'div',
29139                 cls : 'btn-group roo-upload-cropbox-download',
29140                 action : 'download',
29141                 cn : [
29142                     {
29143                         tag : 'button',
29144                         cls : 'btn btn-default',
29145                         html : '<i class="fa fa-download"></i>'
29146                     }
29147                 ]
29148             },
29149             {
29150                 tag : 'div',
29151                 cls : 'btn-group roo-upload-cropbox-crop',
29152                 action : 'crop',
29153                 cn : [
29154                     {
29155                         tag : 'button',
29156                         cls : 'btn btn-default',
29157                         html : '<i class="fa fa-crop"></i>'
29158                     }
29159                 ]
29160             },
29161             {
29162                 tag : 'div',
29163                 cls : 'btn-group roo-upload-cropbox-trash',
29164                 action : 'trash',
29165                 cn : [
29166                     {
29167                         tag : 'button',
29168                         cls : 'btn btn-default',
29169                         html : '<i class="fa fa-trash"></i>'
29170                     }
29171                 ]
29172             },
29173             {
29174                 tag : 'div',
29175                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29176                 action : 'rotate-right',
29177                 cn : [
29178                     {
29179                         tag : 'button',
29180                         cls : 'btn btn-default',
29181                         html : '<i class="fa fa-repeat"></i>'
29182                     }
29183                 ]
29184             }
29185         ],
29186         ROTATOR : [
29187             {
29188                 tag : 'div',
29189                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29190                 action : 'rotate-left',
29191                 cn : [
29192                     {
29193                         tag : 'button',
29194                         cls : 'btn btn-default',
29195                         html : '<i class="fa fa-undo"></i>'
29196                     }
29197                 ]
29198             },
29199             {
29200                 tag : 'div',
29201                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29202                 action : 'rotate-right',
29203                 cn : [
29204                     {
29205                         tag : 'button',
29206                         cls : 'btn btn-default',
29207                         html : '<i class="fa fa-repeat"></i>'
29208                     }
29209                 ]
29210             }
29211         ]
29212     }
29213 });
29214
29215 /*
29216 * Licence: LGPL
29217 */
29218
29219 /**
29220  * @class Roo.bootstrap.DocumentManager
29221  * @extends Roo.bootstrap.Component
29222  * Bootstrap DocumentManager class
29223  * @cfg {String} paramName default 'imageUpload'
29224  * @cfg {String} toolTipName default 'filename'
29225  * @cfg {String} method default POST
29226  * @cfg {String} url action url
29227  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29228  * @cfg {Boolean} multiple multiple upload default true
29229  * @cfg {Number} thumbSize default 300
29230  * @cfg {String} fieldLabel
29231  * @cfg {Number} labelWidth default 4
29232  * @cfg {String} labelAlign (left|top) default left
29233  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29234 * @cfg {Number} labellg set the width of label (1-12)
29235  * @cfg {Number} labelmd set the width of label (1-12)
29236  * @cfg {Number} labelsm set the width of label (1-12)
29237  * @cfg {Number} labelxs set the width of label (1-12)
29238  * 
29239  * @constructor
29240  * Create a new DocumentManager
29241  * @param {Object} config The config object
29242  */
29243
29244 Roo.bootstrap.DocumentManager = function(config){
29245     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29246     
29247     this.files = [];
29248     this.delegates = [];
29249     
29250     this.addEvents({
29251         /**
29252          * @event initial
29253          * Fire when initial the DocumentManager
29254          * @param {Roo.bootstrap.DocumentManager} this
29255          */
29256         "initial" : true,
29257         /**
29258          * @event inspect
29259          * inspect selected file
29260          * @param {Roo.bootstrap.DocumentManager} this
29261          * @param {File} file
29262          */
29263         "inspect" : true,
29264         /**
29265          * @event exception
29266          * Fire when xhr load exception
29267          * @param {Roo.bootstrap.DocumentManager} this
29268          * @param {XMLHttpRequest} xhr
29269          */
29270         "exception" : true,
29271         /**
29272          * @event afterupload
29273          * Fire when xhr load exception
29274          * @param {Roo.bootstrap.DocumentManager} this
29275          * @param {XMLHttpRequest} xhr
29276          */
29277         "afterupload" : true,
29278         /**
29279          * @event prepare
29280          * prepare the form data
29281          * @param {Roo.bootstrap.DocumentManager} this
29282          * @param {Object} formData
29283          */
29284         "prepare" : true,
29285         /**
29286          * @event remove
29287          * Fire when remove the file
29288          * @param {Roo.bootstrap.DocumentManager} this
29289          * @param {Object} file
29290          */
29291         "remove" : true,
29292         /**
29293          * @event refresh
29294          * Fire after refresh the file
29295          * @param {Roo.bootstrap.DocumentManager} this
29296          */
29297         "refresh" : true,
29298         /**
29299          * @event click
29300          * Fire after click the image
29301          * @param {Roo.bootstrap.DocumentManager} this
29302          * @param {Object} file
29303          */
29304         "click" : true,
29305         /**
29306          * @event edit
29307          * Fire when upload a image and editable set to true
29308          * @param {Roo.bootstrap.DocumentManager} this
29309          * @param {Object} file
29310          */
29311         "edit" : true,
29312         /**
29313          * @event beforeselectfile
29314          * Fire before select file
29315          * @param {Roo.bootstrap.DocumentManager} this
29316          */
29317         "beforeselectfile" : true,
29318         /**
29319          * @event process
29320          * Fire before process file
29321          * @param {Roo.bootstrap.DocumentManager} this
29322          * @param {Object} file
29323          */
29324         "process" : true,
29325         /**
29326          * @event previewrendered
29327          * Fire when preview rendered
29328          * @param {Roo.bootstrap.DocumentManager} this
29329          * @param {Object} file
29330          */
29331         "previewrendered" : true,
29332         /**
29333          */
29334         "previewResize" : true
29335         
29336     });
29337 };
29338
29339 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29340     
29341     boxes : 0,
29342     inputName : '',
29343     thumbSize : 300,
29344     multiple : true,
29345     files : false,
29346     method : 'POST',
29347     url : '',
29348     paramName : 'imageUpload',
29349     toolTipName : 'filename',
29350     fieldLabel : '',
29351     labelWidth : 4,
29352     labelAlign : 'left',
29353     editable : true,
29354     delegates : false,
29355     xhr : false, 
29356     
29357     labellg : 0,
29358     labelmd : 0,
29359     labelsm : 0,
29360     labelxs : 0,
29361     
29362     getAutoCreate : function()
29363     {   
29364         var managerWidget = {
29365             tag : 'div',
29366             cls : 'roo-document-manager',
29367             cn : [
29368                 {
29369                     tag : 'input',
29370                     cls : 'roo-document-manager-selector',
29371                     type : 'file'
29372                 },
29373                 {
29374                     tag : 'div',
29375                     cls : 'roo-document-manager-uploader',
29376                     cn : [
29377                         {
29378                             tag : 'div',
29379                             cls : 'roo-document-manager-upload-btn',
29380                             html : '<i class="fa fa-plus"></i>'
29381                         }
29382                     ]
29383                     
29384                 }
29385             ]
29386         };
29387         
29388         var content = [
29389             {
29390                 tag : 'div',
29391                 cls : 'column col-md-12',
29392                 cn : managerWidget
29393             }
29394         ];
29395         
29396         if(this.fieldLabel.length){
29397             
29398             content = [
29399                 {
29400                     tag : 'div',
29401                     cls : 'column col-md-12',
29402                     html : this.fieldLabel
29403                 },
29404                 {
29405                     tag : 'div',
29406                     cls : 'column col-md-12',
29407                     cn : managerWidget
29408                 }
29409             ];
29410
29411             if(this.labelAlign == 'left'){
29412                 content = [
29413                     {
29414                         tag : 'div',
29415                         cls : 'column',
29416                         html : this.fieldLabel
29417                     },
29418                     {
29419                         tag : 'div',
29420                         cls : 'column',
29421                         cn : managerWidget
29422                     }
29423                 ];
29424                 
29425                 if(this.labelWidth > 12){
29426                     content[0].style = "width: " + this.labelWidth + 'px';
29427                 }
29428
29429                 if(this.labelWidth < 13 && this.labelmd == 0){
29430                     this.labelmd = this.labelWidth;
29431                 }
29432
29433                 if(this.labellg > 0){
29434                     content[0].cls += ' col-lg-' + this.labellg;
29435                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29436                 }
29437
29438                 if(this.labelmd > 0){
29439                     content[0].cls += ' col-md-' + this.labelmd;
29440                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29441                 }
29442
29443                 if(this.labelsm > 0){
29444                     content[0].cls += ' col-sm-' + this.labelsm;
29445                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29446                 }
29447
29448                 if(this.labelxs > 0){
29449                     content[0].cls += ' col-xs-' + this.labelxs;
29450                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29451                 }
29452                 
29453             }
29454         }
29455         
29456         var cfg = {
29457             tag : 'div',
29458             cls : 'row clearfix',
29459             cn : content
29460         };
29461         
29462         return cfg;
29463         
29464     },
29465     
29466     initEvents : function()
29467     {
29468         this.managerEl = this.el.select('.roo-document-manager', true).first();
29469         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29470         
29471         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29472         this.selectorEl.hide();
29473         
29474         if(this.multiple){
29475             this.selectorEl.attr('multiple', 'multiple');
29476         }
29477         
29478         this.selectorEl.on('change', this.onFileSelected, this);
29479         
29480         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29481         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29482         
29483         this.uploader.on('click', this.onUploaderClick, this);
29484         
29485         this.renderProgressDialog();
29486         
29487         var _this = this;
29488         
29489         window.addEventListener("resize", function() { _this.refresh(); } );
29490         
29491         this.fireEvent('initial', this);
29492     },
29493     
29494     renderProgressDialog : function()
29495     {
29496         var _this = this;
29497         
29498         this.progressDialog = new Roo.bootstrap.Modal({
29499             cls : 'roo-document-manager-progress-dialog',
29500             allow_close : false,
29501             animate : false,
29502             title : '',
29503             buttons : [
29504                 {
29505                     name  :'cancel',
29506                     weight : 'danger',
29507                     html : 'Cancel'
29508                 }
29509             ], 
29510             listeners : { 
29511                 btnclick : function() {
29512                     _this.uploadCancel();
29513                     this.hide();
29514                 }
29515             }
29516         });
29517          
29518         this.progressDialog.render(Roo.get(document.body));
29519          
29520         this.progress = new Roo.bootstrap.Progress({
29521             cls : 'roo-document-manager-progress',
29522             active : true,
29523             striped : true
29524         });
29525         
29526         this.progress.render(this.progressDialog.getChildContainer());
29527         
29528         this.progressBar = new Roo.bootstrap.ProgressBar({
29529             cls : 'roo-document-manager-progress-bar',
29530             aria_valuenow : 0,
29531             aria_valuemin : 0,
29532             aria_valuemax : 12,
29533             panel : 'success'
29534         });
29535         
29536         this.progressBar.render(this.progress.getChildContainer());
29537     },
29538     
29539     onUploaderClick : function(e)
29540     {
29541         e.preventDefault();
29542      
29543         if(this.fireEvent('beforeselectfile', this) != false){
29544             this.selectorEl.dom.click();
29545         }
29546         
29547     },
29548     
29549     onFileSelected : function(e)
29550     {
29551         e.preventDefault();
29552         
29553         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29554             return;
29555         }
29556         
29557         Roo.each(this.selectorEl.dom.files, function(file){
29558             if(this.fireEvent('inspect', this, file) != false){
29559                 this.files.push(file);
29560             }
29561         }, this);
29562         
29563         this.queue();
29564         
29565     },
29566     
29567     queue : function()
29568     {
29569         this.selectorEl.dom.value = '';
29570         
29571         if(!this.files || !this.files.length){
29572             return;
29573         }
29574         
29575         if(this.boxes > 0 && this.files.length > this.boxes){
29576             this.files = this.files.slice(0, this.boxes);
29577         }
29578         
29579         this.uploader.show();
29580         
29581         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29582             this.uploader.hide();
29583         }
29584         
29585         var _this = this;
29586         
29587         var files = [];
29588         
29589         var docs = [];
29590         
29591         Roo.each(this.files, function(file){
29592             
29593             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29594                 var f = this.renderPreview(file);
29595                 files.push(f);
29596                 return;
29597             }
29598             
29599             if(file.type.indexOf('image') != -1){
29600                 this.delegates.push(
29601                     (function(){
29602                         _this.process(file);
29603                     }).createDelegate(this)
29604                 );
29605         
29606                 return;
29607             }
29608             
29609             docs.push(
29610                 (function(){
29611                     _this.process(file);
29612                 }).createDelegate(this)
29613             );
29614             
29615         }, this);
29616         
29617         this.files = files;
29618         
29619         this.delegates = this.delegates.concat(docs);
29620         
29621         if(!this.delegates.length){
29622             this.refresh();
29623             return;
29624         }
29625         
29626         this.progressBar.aria_valuemax = this.delegates.length;
29627         
29628         this.arrange();
29629         
29630         return;
29631     },
29632     
29633     arrange : function()
29634     {
29635         if(!this.delegates.length){
29636             this.progressDialog.hide();
29637             this.refresh();
29638             return;
29639         }
29640         
29641         var delegate = this.delegates.shift();
29642         
29643         this.progressDialog.show();
29644         
29645         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29646         
29647         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29648         
29649         delegate();
29650     },
29651     
29652     refresh : function()
29653     {
29654         this.uploader.show();
29655         
29656         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29657             this.uploader.hide();
29658         }
29659         
29660         Roo.isTouch ? this.closable(false) : this.closable(true);
29661         
29662         this.fireEvent('refresh', this);
29663     },
29664     
29665     onRemove : function(e, el, o)
29666     {
29667         e.preventDefault();
29668         
29669         this.fireEvent('remove', this, o);
29670         
29671     },
29672     
29673     remove : function(o)
29674     {
29675         var files = [];
29676         
29677         Roo.each(this.files, function(file){
29678             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29679                 files.push(file);
29680                 return;
29681             }
29682
29683             o.target.remove();
29684
29685         }, this);
29686         
29687         this.files = files;
29688         
29689         this.refresh();
29690     },
29691     
29692     clear : function()
29693     {
29694         Roo.each(this.files, function(file){
29695             if(!file.target){
29696                 return;
29697             }
29698             
29699             file.target.remove();
29700
29701         }, this);
29702         
29703         this.files = [];
29704         
29705         this.refresh();
29706     },
29707     
29708     onClick : function(e, el, o)
29709     {
29710         e.preventDefault();
29711         
29712         this.fireEvent('click', this, o);
29713         
29714     },
29715     
29716     closable : function(closable)
29717     {
29718         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29719             
29720             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29721             
29722             if(closable){
29723                 el.show();
29724                 return;
29725             }
29726             
29727             el.hide();
29728             
29729         }, this);
29730     },
29731     
29732     xhrOnLoad : function(xhr)
29733     {
29734         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29735             el.remove();
29736         }, this);
29737         
29738         if (xhr.readyState !== 4) {
29739             this.arrange();
29740             this.fireEvent('exception', this, xhr);
29741             return;
29742         }
29743
29744         var response = Roo.decode(xhr.responseText);
29745         
29746         if(!response.success){
29747             this.arrange();
29748             this.fireEvent('exception', this, xhr);
29749             return;
29750         }
29751         
29752         var file = this.renderPreview(response.data);
29753         
29754         this.files.push(file);
29755         
29756         this.arrange();
29757         
29758         this.fireEvent('afterupload', this, xhr);
29759         
29760     },
29761     
29762     xhrOnError : function(xhr)
29763     {
29764         Roo.log('xhr on error');
29765         
29766         var response = Roo.decode(xhr.responseText);
29767           
29768         Roo.log(response);
29769         
29770         this.arrange();
29771     },
29772     
29773     process : function(file)
29774     {
29775         if(this.fireEvent('process', this, file) !== false){
29776             if(this.editable && file.type.indexOf('image') != -1){
29777                 this.fireEvent('edit', this, file);
29778                 return;
29779             }
29780
29781             this.uploadStart(file, false);
29782
29783             return;
29784         }
29785         
29786     },
29787     
29788     uploadStart : function(file, crop)
29789     {
29790         this.xhr = new XMLHttpRequest();
29791         
29792         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29793             this.arrange();
29794             return;
29795         }
29796         
29797         file.xhr = this.xhr;
29798             
29799         this.managerEl.createChild({
29800             tag : 'div',
29801             cls : 'roo-document-manager-loading',
29802             cn : [
29803                 {
29804                     tag : 'div',
29805                     tooltip : file.name,
29806                     cls : 'roo-document-manager-thumb',
29807                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29808                 }
29809             ]
29810
29811         });
29812
29813         this.xhr.open(this.method, this.url, true);
29814         
29815         var headers = {
29816             "Accept": "application/json",
29817             "Cache-Control": "no-cache",
29818             "X-Requested-With": "XMLHttpRequest"
29819         };
29820         
29821         for (var headerName in headers) {
29822             var headerValue = headers[headerName];
29823             if (headerValue) {
29824                 this.xhr.setRequestHeader(headerName, headerValue);
29825             }
29826         }
29827         
29828         var _this = this;
29829         
29830         this.xhr.onload = function()
29831         {
29832             _this.xhrOnLoad(_this.xhr);
29833         }
29834         
29835         this.xhr.onerror = function()
29836         {
29837             _this.xhrOnError(_this.xhr);
29838         }
29839         
29840         var formData = new FormData();
29841
29842         formData.append('returnHTML', 'NO');
29843         
29844         if(crop){
29845             formData.append('crop', crop);
29846         }
29847         
29848         formData.append(this.paramName, file, file.name);
29849         
29850         var options = {
29851             file : file, 
29852             manually : false
29853         };
29854         
29855         if(this.fireEvent('prepare', this, formData, options) != false){
29856             
29857             if(options.manually){
29858                 return;
29859             }
29860             
29861             this.xhr.send(formData);
29862             return;
29863         };
29864         
29865         this.uploadCancel();
29866     },
29867     
29868     uploadCancel : function()
29869     {
29870         if (this.xhr) {
29871             this.xhr.abort();
29872         }
29873         
29874         this.delegates = [];
29875         
29876         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29877             el.remove();
29878         }, this);
29879         
29880         this.arrange();
29881     },
29882     
29883     renderPreview : function(file)
29884     {
29885         if(typeof(file.target) != 'undefined' && file.target){
29886             return file;
29887         }
29888         
29889         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29890         
29891         var previewEl = this.managerEl.createChild({
29892             tag : 'div',
29893             cls : 'roo-document-manager-preview',
29894             cn : [
29895                 {
29896                     tag : 'div',
29897                     tooltip : file[this.toolTipName],
29898                     cls : 'roo-document-manager-thumb',
29899                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29900                 },
29901                 {
29902                     tag : 'button',
29903                     cls : 'close',
29904                     html : '<i class="fa fa-times-circle"></i>'
29905                 }
29906             ]
29907         });
29908
29909         var close = previewEl.select('button.close', true).first();
29910
29911         close.on('click', this.onRemove, this, file);
29912
29913         file.target = previewEl;
29914
29915         var image = previewEl.select('img', true).first();
29916         
29917         var _this = this;
29918         
29919         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29920         
29921         image.on('click', this.onClick, this, file);
29922         
29923         this.fireEvent('previewrendered', this, file);
29924         
29925         return file;
29926         
29927     },
29928     
29929     onPreviewLoad : function(file, image)
29930     {
29931         if(typeof(file.target) == 'undefined' || !file.target){
29932             return;
29933         }
29934         
29935         var width = image.dom.naturalWidth || image.dom.width;
29936         var height = image.dom.naturalHeight || image.dom.height;
29937         
29938         if(!this.previewResize) {
29939             return;
29940         }
29941         
29942         if(width > height){
29943             file.target.addClass('wide');
29944             return;
29945         }
29946         
29947         file.target.addClass('tall');
29948         return;
29949         
29950     },
29951     
29952     uploadFromSource : function(file, crop)
29953     {
29954         this.xhr = new XMLHttpRequest();
29955         
29956         this.managerEl.createChild({
29957             tag : 'div',
29958             cls : 'roo-document-manager-loading',
29959             cn : [
29960                 {
29961                     tag : 'div',
29962                     tooltip : file.name,
29963                     cls : 'roo-document-manager-thumb',
29964                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29965                 }
29966             ]
29967
29968         });
29969
29970         this.xhr.open(this.method, this.url, true);
29971         
29972         var headers = {
29973             "Accept": "application/json",
29974             "Cache-Control": "no-cache",
29975             "X-Requested-With": "XMLHttpRequest"
29976         };
29977         
29978         for (var headerName in headers) {
29979             var headerValue = headers[headerName];
29980             if (headerValue) {
29981                 this.xhr.setRequestHeader(headerName, headerValue);
29982             }
29983         }
29984         
29985         var _this = this;
29986         
29987         this.xhr.onload = function()
29988         {
29989             _this.xhrOnLoad(_this.xhr);
29990         }
29991         
29992         this.xhr.onerror = function()
29993         {
29994             _this.xhrOnError(_this.xhr);
29995         }
29996         
29997         var formData = new FormData();
29998
29999         formData.append('returnHTML', 'NO');
30000         
30001         formData.append('crop', crop);
30002         
30003         if(typeof(file.filename) != 'undefined'){
30004             formData.append('filename', file.filename);
30005         }
30006         
30007         if(typeof(file.mimetype) != 'undefined'){
30008             formData.append('mimetype', file.mimetype);
30009         }
30010         
30011         Roo.log(formData);
30012         
30013         if(this.fireEvent('prepare', this, formData) != false){
30014             this.xhr.send(formData);
30015         };
30016     }
30017 });
30018
30019 /*
30020 * Licence: LGPL
30021 */
30022
30023 /**
30024  * @class Roo.bootstrap.DocumentViewer
30025  * @extends Roo.bootstrap.Component
30026  * Bootstrap DocumentViewer class
30027  * @cfg {Boolean} showDownload (true|false) show download button (default true)
30028  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30029  * 
30030  * @constructor
30031  * Create a new DocumentViewer
30032  * @param {Object} config The config object
30033  */
30034
30035 Roo.bootstrap.DocumentViewer = function(config){
30036     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30037     
30038     this.addEvents({
30039         /**
30040          * @event initial
30041          * Fire after initEvent
30042          * @param {Roo.bootstrap.DocumentViewer} this
30043          */
30044         "initial" : true,
30045         /**
30046          * @event click
30047          * Fire after click
30048          * @param {Roo.bootstrap.DocumentViewer} this
30049          */
30050         "click" : true,
30051         /**
30052          * @event download
30053          * Fire after download button
30054          * @param {Roo.bootstrap.DocumentViewer} this
30055          */
30056         "download" : true,
30057         /**
30058          * @event trash
30059          * Fire after trash button
30060          * @param {Roo.bootstrap.DocumentViewer} this
30061          */
30062         "trash" : true
30063         
30064     });
30065 };
30066
30067 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
30068     
30069     showDownload : true,
30070     
30071     showTrash : true,
30072     
30073     getAutoCreate : function()
30074     {
30075         var cfg = {
30076             tag : 'div',
30077             cls : 'roo-document-viewer',
30078             cn : [
30079                 {
30080                     tag : 'div',
30081                     cls : 'roo-document-viewer-body',
30082                     cn : [
30083                         {
30084                             tag : 'div',
30085                             cls : 'roo-document-viewer-thumb',
30086                             cn : [
30087                                 {
30088                                     tag : 'img',
30089                                     cls : 'roo-document-viewer-image'
30090                                 }
30091                             ]
30092                         }
30093                     ]
30094                 },
30095                 {
30096                     tag : 'div',
30097                     cls : 'roo-document-viewer-footer',
30098                     cn : {
30099                         tag : 'div',
30100                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30101                         cn : [
30102                             {
30103                                 tag : 'div',
30104                                 cls : 'btn-group roo-document-viewer-download',
30105                                 cn : [
30106                                     {
30107                                         tag : 'button',
30108                                         cls : 'btn btn-default',
30109                                         html : '<i class="fa fa-download"></i>'
30110                                     }
30111                                 ]
30112                             },
30113                             {
30114                                 tag : 'div',
30115                                 cls : 'btn-group roo-document-viewer-trash',
30116                                 cn : [
30117                                     {
30118                                         tag : 'button',
30119                                         cls : 'btn btn-default',
30120                                         html : '<i class="fa fa-trash"></i>'
30121                                     }
30122                                 ]
30123                             }
30124                         ]
30125                     }
30126                 }
30127             ]
30128         };
30129         
30130         return cfg;
30131     },
30132     
30133     initEvents : function()
30134     {
30135         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30136         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30137         
30138         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30139         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30140         
30141         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30142         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30143         
30144         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30145         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30146         
30147         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30148         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30149         
30150         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30151         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30152         
30153         this.bodyEl.on('click', this.onClick, this);
30154         this.downloadBtn.on('click', this.onDownload, this);
30155         this.trashBtn.on('click', this.onTrash, this);
30156         
30157         this.downloadBtn.hide();
30158         this.trashBtn.hide();
30159         
30160         if(this.showDownload){
30161             this.downloadBtn.show();
30162         }
30163         
30164         if(this.showTrash){
30165             this.trashBtn.show();
30166         }
30167         
30168         if(!this.showDownload && !this.showTrash) {
30169             this.footerEl.hide();
30170         }
30171         
30172     },
30173     
30174     initial : function()
30175     {
30176         this.fireEvent('initial', this);
30177         
30178     },
30179     
30180     onClick : function(e)
30181     {
30182         e.preventDefault();
30183         
30184         this.fireEvent('click', this);
30185     },
30186     
30187     onDownload : function(e)
30188     {
30189         e.preventDefault();
30190         
30191         this.fireEvent('download', this);
30192     },
30193     
30194     onTrash : function(e)
30195     {
30196         e.preventDefault();
30197         
30198         this.fireEvent('trash', this);
30199     }
30200     
30201 });
30202 /*
30203  * - LGPL
30204  *
30205  * nav progress bar
30206  * 
30207  */
30208
30209 /**
30210  * @class Roo.bootstrap.NavProgressBar
30211  * @extends Roo.bootstrap.Component
30212  * Bootstrap NavProgressBar class
30213  * 
30214  * @constructor
30215  * Create a new nav progress bar
30216  * @param {Object} config The config object
30217  */
30218
30219 Roo.bootstrap.NavProgressBar = function(config){
30220     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30221
30222     this.bullets = this.bullets || [];
30223    
30224 //    Roo.bootstrap.NavProgressBar.register(this);
30225      this.addEvents({
30226         /**
30227              * @event changed
30228              * Fires when the active item changes
30229              * @param {Roo.bootstrap.NavProgressBar} this
30230              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30231              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30232          */
30233         'changed': true
30234      });
30235     
30236 };
30237
30238 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30239     
30240     bullets : [],
30241     barItems : [],
30242     
30243     getAutoCreate : function()
30244     {
30245         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30246         
30247         cfg = {
30248             tag : 'div',
30249             cls : 'roo-navigation-bar-group',
30250             cn : [
30251                 {
30252                     tag : 'div',
30253                     cls : 'roo-navigation-top-bar'
30254                 },
30255                 {
30256                     tag : 'div',
30257                     cls : 'roo-navigation-bullets-bar',
30258                     cn : [
30259                         {
30260                             tag : 'ul',
30261                             cls : 'roo-navigation-bar'
30262                         }
30263                     ]
30264                 },
30265                 
30266                 {
30267                     tag : 'div',
30268                     cls : 'roo-navigation-bottom-bar'
30269                 }
30270             ]
30271             
30272         };
30273         
30274         return cfg;
30275         
30276     },
30277     
30278     initEvents: function() 
30279     {
30280         
30281     },
30282     
30283     onRender : function(ct, position) 
30284     {
30285         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30286         
30287         if(this.bullets.length){
30288             Roo.each(this.bullets, function(b){
30289                this.addItem(b);
30290             }, this);
30291         }
30292         
30293         this.format();
30294         
30295     },
30296     
30297     addItem : function(cfg)
30298     {
30299         var item = new Roo.bootstrap.NavProgressItem(cfg);
30300         
30301         item.parentId = this.id;
30302         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30303         
30304         if(cfg.html){
30305             var top = new Roo.bootstrap.Element({
30306                 tag : 'div',
30307                 cls : 'roo-navigation-bar-text'
30308             });
30309             
30310             var bottom = new Roo.bootstrap.Element({
30311                 tag : 'div',
30312                 cls : 'roo-navigation-bar-text'
30313             });
30314             
30315             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30316             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30317             
30318             var topText = new Roo.bootstrap.Element({
30319                 tag : 'span',
30320                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30321             });
30322             
30323             var bottomText = new Roo.bootstrap.Element({
30324                 tag : 'span',
30325                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30326             });
30327             
30328             topText.onRender(top.el, null);
30329             bottomText.onRender(bottom.el, null);
30330             
30331             item.topEl = top;
30332             item.bottomEl = bottom;
30333         }
30334         
30335         this.barItems.push(item);
30336         
30337         return item;
30338     },
30339     
30340     getActive : function()
30341     {
30342         var active = false;
30343         
30344         Roo.each(this.barItems, function(v){
30345             
30346             if (!v.isActive()) {
30347                 return;
30348             }
30349             
30350             active = v;
30351             return false;
30352             
30353         });
30354         
30355         return active;
30356     },
30357     
30358     setActiveItem : function(item)
30359     {
30360         var prev = false;
30361         
30362         Roo.each(this.barItems, function(v){
30363             if (v.rid == item.rid) {
30364                 return ;
30365             }
30366             
30367             if (v.isActive()) {
30368                 v.setActive(false);
30369                 prev = v;
30370             }
30371         });
30372
30373         item.setActive(true);
30374         
30375         this.fireEvent('changed', this, item, prev);
30376     },
30377     
30378     getBarItem: function(rid)
30379     {
30380         var ret = false;
30381         
30382         Roo.each(this.barItems, function(e) {
30383             if (e.rid != rid) {
30384                 return;
30385             }
30386             
30387             ret =  e;
30388             return false;
30389         });
30390         
30391         return ret;
30392     },
30393     
30394     indexOfItem : function(item)
30395     {
30396         var index = false;
30397         
30398         Roo.each(this.barItems, function(v, i){
30399             
30400             if (v.rid != item.rid) {
30401                 return;
30402             }
30403             
30404             index = i;
30405             return false
30406         });
30407         
30408         return index;
30409     },
30410     
30411     setActiveNext : function()
30412     {
30413         var i = this.indexOfItem(this.getActive());
30414         
30415         if (i > this.barItems.length) {
30416             return;
30417         }
30418         
30419         this.setActiveItem(this.barItems[i+1]);
30420     },
30421     
30422     setActivePrev : function()
30423     {
30424         var i = this.indexOfItem(this.getActive());
30425         
30426         if (i  < 1) {
30427             return;
30428         }
30429         
30430         this.setActiveItem(this.barItems[i-1]);
30431     },
30432     
30433     format : function()
30434     {
30435         if(!this.barItems.length){
30436             return;
30437         }
30438      
30439         var width = 100 / this.barItems.length;
30440         
30441         Roo.each(this.barItems, function(i){
30442             i.el.setStyle('width', width + '%');
30443             i.topEl.el.setStyle('width', width + '%');
30444             i.bottomEl.el.setStyle('width', width + '%');
30445         }, this);
30446         
30447     }
30448     
30449 });
30450 /*
30451  * - LGPL
30452  *
30453  * Nav Progress Item
30454  * 
30455  */
30456
30457 /**
30458  * @class Roo.bootstrap.NavProgressItem
30459  * @extends Roo.bootstrap.Component
30460  * Bootstrap NavProgressItem class
30461  * @cfg {String} rid the reference id
30462  * @cfg {Boolean} active (true|false) Is item active default false
30463  * @cfg {Boolean} disabled (true|false) Is item active default false
30464  * @cfg {String} html
30465  * @cfg {String} position (top|bottom) text position default bottom
30466  * @cfg {String} icon show icon instead of number
30467  * 
30468  * @constructor
30469  * Create a new NavProgressItem
30470  * @param {Object} config The config object
30471  */
30472 Roo.bootstrap.NavProgressItem = function(config){
30473     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30474     this.addEvents({
30475         // raw events
30476         /**
30477          * @event click
30478          * The raw click event for the entire grid.
30479          * @param {Roo.bootstrap.NavProgressItem} this
30480          * @param {Roo.EventObject} e
30481          */
30482         "click" : true
30483     });
30484    
30485 };
30486
30487 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30488     
30489     rid : '',
30490     active : false,
30491     disabled : false,
30492     html : '',
30493     position : 'bottom',
30494     icon : false,
30495     
30496     getAutoCreate : function()
30497     {
30498         var iconCls = 'roo-navigation-bar-item-icon';
30499         
30500         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30501         
30502         var cfg = {
30503             tag: 'li',
30504             cls: 'roo-navigation-bar-item',
30505             cn : [
30506                 {
30507                     tag : 'i',
30508                     cls : iconCls
30509                 }
30510             ]
30511         };
30512         
30513         if(this.active){
30514             cfg.cls += ' active';
30515         }
30516         if(this.disabled){
30517             cfg.cls += ' disabled';
30518         }
30519         
30520         return cfg;
30521     },
30522     
30523     disable : function()
30524     {
30525         this.setDisabled(true);
30526     },
30527     
30528     enable : function()
30529     {
30530         this.setDisabled(false);
30531     },
30532     
30533     initEvents: function() 
30534     {
30535         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30536         
30537         this.iconEl.on('click', this.onClick, this);
30538     },
30539     
30540     onClick : function(e)
30541     {
30542         e.preventDefault();
30543         
30544         if(this.disabled){
30545             return;
30546         }
30547         
30548         if(this.fireEvent('click', this, e) === false){
30549             return;
30550         };
30551         
30552         this.parent().setActiveItem(this);
30553     },
30554     
30555     isActive: function () 
30556     {
30557         return this.active;
30558     },
30559     
30560     setActive : function(state)
30561     {
30562         if(this.active == state){
30563             return;
30564         }
30565         
30566         this.active = state;
30567         
30568         if (state) {
30569             this.el.addClass('active');
30570             return;
30571         }
30572         
30573         this.el.removeClass('active');
30574         
30575         return;
30576     },
30577     
30578     setDisabled : function(state)
30579     {
30580         if(this.disabled == state){
30581             return;
30582         }
30583         
30584         this.disabled = state;
30585         
30586         if (state) {
30587             this.el.addClass('disabled');
30588             return;
30589         }
30590         
30591         this.el.removeClass('disabled');
30592     },
30593     
30594     tooltipEl : function()
30595     {
30596         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30597     }
30598 });
30599  
30600
30601  /*
30602  * - LGPL
30603  *
30604  * FieldLabel
30605  * 
30606  */
30607
30608 /**
30609  * @class Roo.bootstrap.FieldLabel
30610  * @extends Roo.bootstrap.Component
30611  * Bootstrap FieldLabel class
30612  * @cfg {String} html contents of the element
30613  * @cfg {String} tag tag of the element default label
30614  * @cfg {String} cls class of the element
30615  * @cfg {String} target label target 
30616  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30617  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30618  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30619  * @cfg {String} iconTooltip default "This field is required"
30620  * @cfg {String} indicatorpos (left|right) default left
30621  * 
30622  * @constructor
30623  * Create a new FieldLabel
30624  * @param {Object} config The config object
30625  */
30626
30627 Roo.bootstrap.FieldLabel = function(config){
30628     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30629     
30630     this.addEvents({
30631             /**
30632              * @event invalid
30633              * Fires after the field has been marked as invalid.
30634              * @param {Roo.form.FieldLabel} this
30635              * @param {String} msg The validation message
30636              */
30637             invalid : true,
30638             /**
30639              * @event valid
30640              * Fires after the field has been validated with no errors.
30641              * @param {Roo.form.FieldLabel} this
30642              */
30643             valid : true
30644         });
30645 };
30646
30647 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30648     
30649     tag: 'label',
30650     cls: '',
30651     html: '',
30652     target: '',
30653     allowBlank : true,
30654     invalidClass : 'has-warning',
30655     validClass : 'has-success',
30656     iconTooltip : 'This field is required',
30657     indicatorpos : 'left',
30658     
30659     getAutoCreate : function(){
30660         
30661         var cls = "";
30662         if (!this.allowBlank) {
30663             cls  = "visible";
30664         }
30665         
30666         var cfg = {
30667             tag : this.tag,
30668             cls : 'roo-bootstrap-field-label ' + this.cls,
30669             for : this.target,
30670             cn : [
30671                 {
30672                     tag : 'i',
30673                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30674                     tooltip : this.iconTooltip
30675                 },
30676                 {
30677                     tag : 'span',
30678                     html : this.html
30679                 }
30680             ] 
30681         };
30682         
30683         if(this.indicatorpos == 'right'){
30684             var cfg = {
30685                 tag : this.tag,
30686                 cls : 'roo-bootstrap-field-label ' + this.cls,
30687                 for : this.target,
30688                 cn : [
30689                     {
30690                         tag : 'span',
30691                         html : this.html
30692                     },
30693                     {
30694                         tag : 'i',
30695                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30696                         tooltip : this.iconTooltip
30697                     }
30698                 ] 
30699             };
30700         }
30701         
30702         return cfg;
30703     },
30704     
30705     initEvents: function() 
30706     {
30707         Roo.bootstrap.Element.superclass.initEvents.call(this);
30708         
30709         this.indicator = this.indicatorEl();
30710         
30711         if(this.indicator){
30712             this.indicator.removeClass('visible');
30713             this.indicator.addClass('invisible');
30714         }
30715         
30716         Roo.bootstrap.FieldLabel.register(this);
30717     },
30718     
30719     indicatorEl : function()
30720     {
30721         var indicator = this.el.select('i.roo-required-indicator',true).first();
30722         
30723         if(!indicator){
30724             return false;
30725         }
30726         
30727         return indicator;
30728         
30729     },
30730     
30731     /**
30732      * Mark this field as valid
30733      */
30734     markValid : function()
30735     {
30736         if(this.indicator){
30737             this.indicator.removeClass('visible');
30738             this.indicator.addClass('invisible');
30739         }
30740         if (Roo.bootstrap.version == 3) {
30741             this.el.removeClass(this.invalidClass);
30742             this.el.addClass(this.validClass);
30743         } else {
30744             this.el.removeClass('is-invalid');
30745             this.el.addClass('is-valid');
30746         }
30747         
30748         
30749         this.fireEvent('valid', this);
30750     },
30751     
30752     /**
30753      * Mark this field as invalid
30754      * @param {String} msg The validation message
30755      */
30756     markInvalid : function(msg)
30757     {
30758         if(this.indicator){
30759             this.indicator.removeClass('invisible');
30760             this.indicator.addClass('visible');
30761         }
30762           if (Roo.bootstrap.version == 3) {
30763             this.el.removeClass(this.validClass);
30764             this.el.addClass(this.invalidClass);
30765         } else {
30766             this.el.removeClass('is-valid');
30767             this.el.addClass('is-invalid');
30768         }
30769         
30770         
30771         this.fireEvent('invalid', this, msg);
30772     }
30773     
30774    
30775 });
30776
30777 Roo.apply(Roo.bootstrap.FieldLabel, {
30778     
30779     groups: {},
30780     
30781      /**
30782     * register a FieldLabel Group
30783     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30784     */
30785     register : function(label)
30786     {
30787         if(this.groups.hasOwnProperty(label.target)){
30788             return;
30789         }
30790      
30791         this.groups[label.target] = label;
30792         
30793     },
30794     /**
30795     * fetch a FieldLabel Group based on the target
30796     * @param {string} target
30797     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30798     */
30799     get: function(target) {
30800         if (typeof(this.groups[target]) == 'undefined') {
30801             return false;
30802         }
30803         
30804         return this.groups[target] ;
30805     }
30806 });
30807
30808  
30809
30810  /*
30811  * - LGPL
30812  *
30813  * page DateSplitField.
30814  * 
30815  */
30816
30817
30818 /**
30819  * @class Roo.bootstrap.DateSplitField
30820  * @extends Roo.bootstrap.Component
30821  * Bootstrap DateSplitField class
30822  * @cfg {string} fieldLabel - the label associated
30823  * @cfg {Number} labelWidth set the width of label (0-12)
30824  * @cfg {String} labelAlign (top|left)
30825  * @cfg {Boolean} dayAllowBlank (true|false) default false
30826  * @cfg {Boolean} monthAllowBlank (true|false) default false
30827  * @cfg {Boolean} yearAllowBlank (true|false) default false
30828  * @cfg {string} dayPlaceholder 
30829  * @cfg {string} monthPlaceholder
30830  * @cfg {string} yearPlaceholder
30831  * @cfg {string} dayFormat default 'd'
30832  * @cfg {string} monthFormat default 'm'
30833  * @cfg {string} yearFormat default 'Y'
30834  * @cfg {Number} labellg set the width of label (1-12)
30835  * @cfg {Number} labelmd set the width of label (1-12)
30836  * @cfg {Number} labelsm set the width of label (1-12)
30837  * @cfg {Number} labelxs set the width of label (1-12)
30838
30839  *     
30840  * @constructor
30841  * Create a new DateSplitField
30842  * @param {Object} config The config object
30843  */
30844
30845 Roo.bootstrap.DateSplitField = function(config){
30846     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30847     
30848     this.addEvents({
30849         // raw events
30850          /**
30851          * @event years
30852          * getting the data of years
30853          * @param {Roo.bootstrap.DateSplitField} this
30854          * @param {Object} years
30855          */
30856         "years" : true,
30857         /**
30858          * @event days
30859          * getting the data of days
30860          * @param {Roo.bootstrap.DateSplitField} this
30861          * @param {Object} days
30862          */
30863         "days" : true,
30864         /**
30865          * @event invalid
30866          * Fires after the field has been marked as invalid.
30867          * @param {Roo.form.Field} this
30868          * @param {String} msg The validation message
30869          */
30870         invalid : true,
30871        /**
30872          * @event valid
30873          * Fires after the field has been validated with no errors.
30874          * @param {Roo.form.Field} this
30875          */
30876         valid : true
30877     });
30878 };
30879
30880 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30881     
30882     fieldLabel : '',
30883     labelAlign : 'top',
30884     labelWidth : 3,
30885     dayAllowBlank : false,
30886     monthAllowBlank : false,
30887     yearAllowBlank : false,
30888     dayPlaceholder : '',
30889     monthPlaceholder : '',
30890     yearPlaceholder : '',
30891     dayFormat : 'd',
30892     monthFormat : 'm',
30893     yearFormat : 'Y',
30894     isFormField : true,
30895     labellg : 0,
30896     labelmd : 0,
30897     labelsm : 0,
30898     labelxs : 0,
30899     
30900     getAutoCreate : function()
30901     {
30902         var cfg = {
30903             tag : 'div',
30904             cls : 'row roo-date-split-field-group',
30905             cn : [
30906                 {
30907                     tag : 'input',
30908                     type : 'hidden',
30909                     cls : 'form-hidden-field roo-date-split-field-group-value',
30910                     name : this.name
30911                 }
30912             ]
30913         };
30914         
30915         var labelCls = 'col-md-12';
30916         var contentCls = 'col-md-4';
30917         
30918         if(this.fieldLabel){
30919             
30920             var label = {
30921                 tag : 'div',
30922                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30923                 cn : [
30924                     {
30925                         tag : 'label',
30926                         html : this.fieldLabel
30927                     }
30928                 ]
30929             };
30930             
30931             if(this.labelAlign == 'left'){
30932             
30933                 if(this.labelWidth > 12){
30934                     label.style = "width: " + this.labelWidth + 'px';
30935                 }
30936
30937                 if(this.labelWidth < 13 && this.labelmd == 0){
30938                     this.labelmd = this.labelWidth;
30939                 }
30940
30941                 if(this.labellg > 0){
30942                     labelCls = ' col-lg-' + this.labellg;
30943                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30944                 }
30945
30946                 if(this.labelmd > 0){
30947                     labelCls = ' col-md-' + this.labelmd;
30948                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30949                 }
30950
30951                 if(this.labelsm > 0){
30952                     labelCls = ' col-sm-' + this.labelsm;
30953                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30954                 }
30955
30956                 if(this.labelxs > 0){
30957                     labelCls = ' col-xs-' + this.labelxs;
30958                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30959                 }
30960             }
30961             
30962             label.cls += ' ' + labelCls;
30963             
30964             cfg.cn.push(label);
30965         }
30966         
30967         Roo.each(['day', 'month', 'year'], function(t){
30968             cfg.cn.push({
30969                 tag : 'div',
30970                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30971             });
30972         }, this);
30973         
30974         return cfg;
30975     },
30976     
30977     inputEl: function ()
30978     {
30979         return this.el.select('.roo-date-split-field-group-value', true).first();
30980     },
30981     
30982     onRender : function(ct, position) 
30983     {
30984         var _this = this;
30985         
30986         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30987         
30988         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30989         
30990         this.dayField = new Roo.bootstrap.ComboBox({
30991             allowBlank : this.dayAllowBlank,
30992             alwaysQuery : true,
30993             displayField : 'value',
30994             editable : false,
30995             fieldLabel : '',
30996             forceSelection : true,
30997             mode : 'local',
30998             placeholder : this.dayPlaceholder,
30999             selectOnFocus : true,
31000             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31001             triggerAction : 'all',
31002             typeAhead : true,
31003             valueField : 'value',
31004             store : new Roo.data.SimpleStore({
31005                 data : (function() {    
31006                     var days = [];
31007                     _this.fireEvent('days', _this, days);
31008                     return days;
31009                 })(),
31010                 fields : [ 'value' ]
31011             }),
31012             listeners : {
31013                 select : function (_self, record, index)
31014                 {
31015                     _this.setValue(_this.getValue());
31016                 }
31017             }
31018         });
31019
31020         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31021         
31022         this.monthField = new Roo.bootstrap.MonthField({
31023             after : '<i class=\"fa fa-calendar\"></i>',
31024             allowBlank : this.monthAllowBlank,
31025             placeholder : this.monthPlaceholder,
31026             readOnly : true,
31027             listeners : {
31028                 render : function (_self)
31029                 {
31030                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
31031                         e.preventDefault();
31032                         _self.focus();
31033                     });
31034                 },
31035                 select : function (_self, oldvalue, newvalue)
31036                 {
31037                     _this.setValue(_this.getValue());
31038                 }
31039             }
31040         });
31041         
31042         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31043         
31044         this.yearField = new Roo.bootstrap.ComboBox({
31045             allowBlank : this.yearAllowBlank,
31046             alwaysQuery : true,
31047             displayField : 'value',
31048             editable : false,
31049             fieldLabel : '',
31050             forceSelection : true,
31051             mode : 'local',
31052             placeholder : this.yearPlaceholder,
31053             selectOnFocus : true,
31054             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31055             triggerAction : 'all',
31056             typeAhead : true,
31057             valueField : 'value',
31058             store : new Roo.data.SimpleStore({
31059                 data : (function() {
31060                     var years = [];
31061                     _this.fireEvent('years', _this, years);
31062                     return years;
31063                 })(),
31064                 fields : [ 'value' ]
31065             }),
31066             listeners : {
31067                 select : function (_self, record, index)
31068                 {
31069                     _this.setValue(_this.getValue());
31070                 }
31071             }
31072         });
31073
31074         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31075     },
31076     
31077     setValue : function(v, format)
31078     {
31079         this.inputEl.dom.value = v;
31080         
31081         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31082         
31083         var d = Date.parseDate(v, f);
31084         
31085         if(!d){
31086             this.validate();
31087             return;
31088         }
31089         
31090         this.setDay(d.format(this.dayFormat));
31091         this.setMonth(d.format(this.monthFormat));
31092         this.setYear(d.format(this.yearFormat));
31093         
31094         this.validate();
31095         
31096         return;
31097     },
31098     
31099     setDay : function(v)
31100     {
31101         this.dayField.setValue(v);
31102         this.inputEl.dom.value = this.getValue();
31103         this.validate();
31104         return;
31105     },
31106     
31107     setMonth : function(v)
31108     {
31109         this.monthField.setValue(v, true);
31110         this.inputEl.dom.value = this.getValue();
31111         this.validate();
31112         return;
31113     },
31114     
31115     setYear : function(v)
31116     {
31117         this.yearField.setValue(v);
31118         this.inputEl.dom.value = this.getValue();
31119         this.validate();
31120         return;
31121     },
31122     
31123     getDay : function()
31124     {
31125         return this.dayField.getValue();
31126     },
31127     
31128     getMonth : function()
31129     {
31130         return this.monthField.getValue();
31131     },
31132     
31133     getYear : function()
31134     {
31135         return this.yearField.getValue();
31136     },
31137     
31138     getValue : function()
31139     {
31140         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31141         
31142         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31143         
31144         return date;
31145     },
31146     
31147     reset : function()
31148     {
31149         this.setDay('');
31150         this.setMonth('');
31151         this.setYear('');
31152         this.inputEl.dom.value = '';
31153         this.validate();
31154         return;
31155     },
31156     
31157     validate : function()
31158     {
31159         var d = this.dayField.validate();
31160         var m = this.monthField.validate();
31161         var y = this.yearField.validate();
31162         
31163         var valid = true;
31164         
31165         if(
31166                 (!this.dayAllowBlank && !d) ||
31167                 (!this.monthAllowBlank && !m) ||
31168                 (!this.yearAllowBlank && !y)
31169         ){
31170             valid = false;
31171         }
31172         
31173         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31174             return valid;
31175         }
31176         
31177         if(valid){
31178             this.markValid();
31179             return valid;
31180         }
31181         
31182         this.markInvalid();
31183         
31184         return valid;
31185     },
31186     
31187     markValid : function()
31188     {
31189         
31190         var label = this.el.select('label', true).first();
31191         var icon = this.el.select('i.fa-star', true).first();
31192
31193         if(label && icon){
31194             icon.remove();
31195         }
31196         
31197         this.fireEvent('valid', this);
31198     },
31199     
31200      /**
31201      * Mark this field as invalid
31202      * @param {String} msg The validation message
31203      */
31204     markInvalid : function(msg)
31205     {
31206         
31207         var label = this.el.select('label', true).first();
31208         var icon = this.el.select('i.fa-star', true).first();
31209
31210         if(label && !icon){
31211             this.el.select('.roo-date-split-field-label', true).createChild({
31212                 tag : 'i',
31213                 cls : 'text-danger fa fa-lg fa-star',
31214                 tooltip : 'This field is required',
31215                 style : 'margin-right:5px;'
31216             }, label, true);
31217         }
31218         
31219         this.fireEvent('invalid', this, msg);
31220     },
31221     
31222     clearInvalid : function()
31223     {
31224         var label = this.el.select('label', true).first();
31225         var icon = this.el.select('i.fa-star', true).first();
31226
31227         if(label && icon){
31228             icon.remove();
31229         }
31230         
31231         this.fireEvent('valid', this);
31232     },
31233     
31234     getName: function()
31235     {
31236         return this.name;
31237     }
31238     
31239 });
31240
31241  /**
31242  *
31243  * This is based on 
31244  * http://masonry.desandro.com
31245  *
31246  * The idea is to render all the bricks based on vertical width...
31247  *
31248  * The original code extends 'outlayer' - we might need to use that....
31249  * 
31250  */
31251
31252
31253 /**
31254  * @class Roo.bootstrap.LayoutMasonry
31255  * @extends Roo.bootstrap.Component
31256  * Bootstrap Layout Masonry class
31257  * 
31258  * @constructor
31259  * Create a new Element
31260  * @param {Object} config The config object
31261  */
31262
31263 Roo.bootstrap.LayoutMasonry = function(config){
31264     
31265     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31266     
31267     this.bricks = [];
31268     
31269     Roo.bootstrap.LayoutMasonry.register(this);
31270     
31271     this.addEvents({
31272         // raw events
31273         /**
31274          * @event layout
31275          * Fire after layout the items
31276          * @param {Roo.bootstrap.LayoutMasonry} this
31277          * @param {Roo.EventObject} e
31278          */
31279         "layout" : true
31280     });
31281     
31282 };
31283
31284 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31285     
31286     /**
31287      * @cfg {Boolean} isLayoutInstant = no animation?
31288      */   
31289     isLayoutInstant : false, // needed?
31290    
31291     /**
31292      * @cfg {Number} boxWidth  width of the columns
31293      */   
31294     boxWidth : 450,
31295     
31296       /**
31297      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31298      */   
31299     boxHeight : 0,
31300     
31301     /**
31302      * @cfg {Number} padWidth padding below box..
31303      */   
31304     padWidth : 10, 
31305     
31306     /**
31307      * @cfg {Number} gutter gutter width..
31308      */   
31309     gutter : 10,
31310     
31311      /**
31312      * @cfg {Number} maxCols maximum number of columns
31313      */   
31314     
31315     maxCols: 0,
31316     
31317     /**
31318      * @cfg {Boolean} isAutoInitial defalut true
31319      */   
31320     isAutoInitial : true, 
31321     
31322     containerWidth: 0,
31323     
31324     /**
31325      * @cfg {Boolean} isHorizontal defalut false
31326      */   
31327     isHorizontal : false, 
31328
31329     currentSize : null,
31330     
31331     tag: 'div',
31332     
31333     cls: '',
31334     
31335     bricks: null, //CompositeElement
31336     
31337     cols : 1,
31338     
31339     _isLayoutInited : false,
31340     
31341 //    isAlternative : false, // only use for vertical layout...
31342     
31343     /**
31344      * @cfg {Number} alternativePadWidth padding below box..
31345      */   
31346     alternativePadWidth : 50,
31347     
31348     selectedBrick : [],
31349     
31350     getAutoCreate : function(){
31351         
31352         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31353         
31354         var cfg = {
31355             tag: this.tag,
31356             cls: 'blog-masonary-wrapper ' + this.cls,
31357             cn : {
31358                 cls : 'mas-boxes masonary'
31359             }
31360         };
31361         
31362         return cfg;
31363     },
31364     
31365     getChildContainer: function( )
31366     {
31367         if (this.boxesEl) {
31368             return this.boxesEl;
31369         }
31370         
31371         this.boxesEl = this.el.select('.mas-boxes').first();
31372         
31373         return this.boxesEl;
31374     },
31375     
31376     
31377     initEvents : function()
31378     {
31379         var _this = this;
31380         
31381         if(this.isAutoInitial){
31382             Roo.log('hook children rendered');
31383             this.on('childrenrendered', function() {
31384                 Roo.log('children rendered');
31385                 _this.initial();
31386             } ,this);
31387         }
31388     },
31389     
31390     initial : function()
31391     {
31392         this.selectedBrick = [];
31393         
31394         this.currentSize = this.el.getBox(true);
31395         
31396         Roo.EventManager.onWindowResize(this.resize, this); 
31397
31398         if(!this.isAutoInitial){
31399             this.layout();
31400             return;
31401         }
31402         
31403         this.layout();
31404         
31405         return;
31406         //this.layout.defer(500,this);
31407         
31408     },
31409     
31410     resize : function()
31411     {
31412         var cs = this.el.getBox(true);
31413         
31414         if (
31415                 this.currentSize.width == cs.width && 
31416                 this.currentSize.x == cs.x && 
31417                 this.currentSize.height == cs.height && 
31418                 this.currentSize.y == cs.y 
31419         ) {
31420             Roo.log("no change in with or X or Y");
31421             return;
31422         }
31423         
31424         this.currentSize = cs;
31425         
31426         this.layout();
31427         
31428     },
31429     
31430     layout : function()
31431     {   
31432         this._resetLayout();
31433         
31434         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31435         
31436         this.layoutItems( isInstant );
31437       
31438         this._isLayoutInited = true;
31439         
31440         this.fireEvent('layout', this);
31441         
31442     },
31443     
31444     _resetLayout : function()
31445     {
31446         if(this.isHorizontal){
31447             this.horizontalMeasureColumns();
31448             return;
31449         }
31450         
31451         this.verticalMeasureColumns();
31452         
31453     },
31454     
31455     verticalMeasureColumns : function()
31456     {
31457         this.getContainerWidth();
31458         
31459 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31460 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31461 //            return;
31462 //        }
31463         
31464         var boxWidth = this.boxWidth + this.padWidth;
31465         
31466         if(this.containerWidth < this.boxWidth){
31467             boxWidth = this.containerWidth
31468         }
31469         
31470         var containerWidth = this.containerWidth;
31471         
31472         var cols = Math.floor(containerWidth / boxWidth);
31473         
31474         this.cols = Math.max( cols, 1 );
31475         
31476         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31477         
31478         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31479         
31480         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31481         
31482         this.colWidth = boxWidth + avail - this.padWidth;
31483         
31484         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31485         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31486     },
31487     
31488     horizontalMeasureColumns : function()
31489     {
31490         this.getContainerWidth();
31491         
31492         var boxWidth = this.boxWidth;
31493         
31494         if(this.containerWidth < boxWidth){
31495             boxWidth = this.containerWidth;
31496         }
31497         
31498         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31499         
31500         this.el.setHeight(boxWidth);
31501         
31502     },
31503     
31504     getContainerWidth : function()
31505     {
31506         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31507     },
31508     
31509     layoutItems : function( isInstant )
31510     {
31511         Roo.log(this.bricks);
31512         
31513         var items = Roo.apply([], this.bricks);
31514         
31515         if(this.isHorizontal){
31516             this._horizontalLayoutItems( items , isInstant );
31517             return;
31518         }
31519         
31520 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31521 //            this._verticalAlternativeLayoutItems( items , isInstant );
31522 //            return;
31523 //        }
31524         
31525         this._verticalLayoutItems( items , isInstant );
31526         
31527     },
31528     
31529     _verticalLayoutItems : function ( items , isInstant)
31530     {
31531         if ( !items || !items.length ) {
31532             return;
31533         }
31534         
31535         var standard = [
31536             ['xs', 'xs', 'xs', 'tall'],
31537             ['xs', 'xs', 'tall'],
31538             ['xs', 'xs', 'sm'],
31539             ['xs', 'xs', 'xs'],
31540             ['xs', 'tall'],
31541             ['xs', 'sm'],
31542             ['xs', 'xs'],
31543             ['xs'],
31544             
31545             ['sm', 'xs', 'xs'],
31546             ['sm', 'xs'],
31547             ['sm'],
31548             
31549             ['tall', 'xs', 'xs', 'xs'],
31550             ['tall', 'xs', 'xs'],
31551             ['tall', 'xs'],
31552             ['tall']
31553             
31554         ];
31555         
31556         var queue = [];
31557         
31558         var boxes = [];
31559         
31560         var box = [];
31561         
31562         Roo.each(items, function(item, k){
31563             
31564             switch (item.size) {
31565                 // these layouts take up a full box,
31566                 case 'md' :
31567                 case 'md-left' :
31568                 case 'md-right' :
31569                 case 'wide' :
31570                     
31571                     if(box.length){
31572                         boxes.push(box);
31573                         box = [];
31574                     }
31575                     
31576                     boxes.push([item]);
31577                     
31578                     break;
31579                     
31580                 case 'xs' :
31581                 case 'sm' :
31582                 case 'tall' :
31583                     
31584                     box.push(item);
31585                     
31586                     break;
31587                 default :
31588                     break;
31589                     
31590             }
31591             
31592         }, this);
31593         
31594         if(box.length){
31595             boxes.push(box);
31596             box = [];
31597         }
31598         
31599         var filterPattern = function(box, length)
31600         {
31601             if(!box.length){
31602                 return;
31603             }
31604             
31605             var match = false;
31606             
31607             var pattern = box.slice(0, length);
31608             
31609             var format = [];
31610             
31611             Roo.each(pattern, function(i){
31612                 format.push(i.size);
31613             }, this);
31614             
31615             Roo.each(standard, function(s){
31616                 
31617                 if(String(s) != String(format)){
31618                     return;
31619                 }
31620                 
31621                 match = true;
31622                 return false;
31623                 
31624             }, this);
31625             
31626             if(!match && length == 1){
31627                 return;
31628             }
31629             
31630             if(!match){
31631                 filterPattern(box, length - 1);
31632                 return;
31633             }
31634                 
31635             queue.push(pattern);
31636
31637             box = box.slice(length, box.length);
31638
31639             filterPattern(box, 4);
31640
31641             return;
31642             
31643         }
31644         
31645         Roo.each(boxes, function(box, k){
31646             
31647             if(!box.length){
31648                 return;
31649             }
31650             
31651             if(box.length == 1){
31652                 queue.push(box);
31653                 return;
31654             }
31655             
31656             filterPattern(box, 4);
31657             
31658         }, this);
31659         
31660         this._processVerticalLayoutQueue( queue, isInstant );
31661         
31662     },
31663     
31664 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31665 //    {
31666 //        if ( !items || !items.length ) {
31667 //            return;
31668 //        }
31669 //
31670 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31671 //        
31672 //    },
31673     
31674     _horizontalLayoutItems : function ( items , isInstant)
31675     {
31676         if ( !items || !items.length || items.length < 3) {
31677             return;
31678         }
31679         
31680         items.reverse();
31681         
31682         var eItems = items.slice(0, 3);
31683         
31684         items = items.slice(3, items.length);
31685         
31686         var standard = [
31687             ['xs', 'xs', 'xs', 'wide'],
31688             ['xs', 'xs', 'wide'],
31689             ['xs', 'xs', 'sm'],
31690             ['xs', 'xs', 'xs'],
31691             ['xs', 'wide'],
31692             ['xs', 'sm'],
31693             ['xs', 'xs'],
31694             ['xs'],
31695             
31696             ['sm', 'xs', 'xs'],
31697             ['sm', 'xs'],
31698             ['sm'],
31699             
31700             ['wide', 'xs', 'xs', 'xs'],
31701             ['wide', 'xs', 'xs'],
31702             ['wide', 'xs'],
31703             ['wide'],
31704             
31705             ['wide-thin']
31706         ];
31707         
31708         var queue = [];
31709         
31710         var boxes = [];
31711         
31712         var box = [];
31713         
31714         Roo.each(items, function(item, k){
31715             
31716             switch (item.size) {
31717                 case 'md' :
31718                 case 'md-left' :
31719                 case 'md-right' :
31720                 case 'tall' :
31721                     
31722                     if(box.length){
31723                         boxes.push(box);
31724                         box = [];
31725                     }
31726                     
31727                     boxes.push([item]);
31728                     
31729                     break;
31730                     
31731                 case 'xs' :
31732                 case 'sm' :
31733                 case 'wide' :
31734                 case 'wide-thin' :
31735                     
31736                     box.push(item);
31737                     
31738                     break;
31739                 default :
31740                     break;
31741                     
31742             }
31743             
31744         }, this);
31745         
31746         if(box.length){
31747             boxes.push(box);
31748             box = [];
31749         }
31750         
31751         var filterPattern = function(box, length)
31752         {
31753             if(!box.length){
31754                 return;
31755             }
31756             
31757             var match = false;
31758             
31759             var pattern = box.slice(0, length);
31760             
31761             var format = [];
31762             
31763             Roo.each(pattern, function(i){
31764                 format.push(i.size);
31765             }, this);
31766             
31767             Roo.each(standard, function(s){
31768                 
31769                 if(String(s) != String(format)){
31770                     return;
31771                 }
31772                 
31773                 match = true;
31774                 return false;
31775                 
31776             }, this);
31777             
31778             if(!match && length == 1){
31779                 return;
31780             }
31781             
31782             if(!match){
31783                 filterPattern(box, length - 1);
31784                 return;
31785             }
31786                 
31787             queue.push(pattern);
31788
31789             box = box.slice(length, box.length);
31790
31791             filterPattern(box, 4);
31792
31793             return;
31794             
31795         }
31796         
31797         Roo.each(boxes, function(box, k){
31798             
31799             if(!box.length){
31800                 return;
31801             }
31802             
31803             if(box.length == 1){
31804                 queue.push(box);
31805                 return;
31806             }
31807             
31808             filterPattern(box, 4);
31809             
31810         }, this);
31811         
31812         
31813         var prune = [];
31814         
31815         var pos = this.el.getBox(true);
31816         
31817         var minX = pos.x;
31818         
31819         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31820         
31821         var hit_end = false;
31822         
31823         Roo.each(queue, function(box){
31824             
31825             if(hit_end){
31826                 
31827                 Roo.each(box, function(b){
31828                 
31829                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31830                     b.el.hide();
31831
31832                 }, this);
31833
31834                 return;
31835             }
31836             
31837             var mx = 0;
31838             
31839             Roo.each(box, function(b){
31840                 
31841                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31842                 b.el.show();
31843
31844                 mx = Math.max(mx, b.x);
31845                 
31846             }, this);
31847             
31848             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31849             
31850             if(maxX < minX){
31851                 
31852                 Roo.each(box, function(b){
31853                 
31854                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31855                     b.el.hide();
31856                     
31857                 }, this);
31858                 
31859                 hit_end = true;
31860                 
31861                 return;
31862             }
31863             
31864             prune.push(box);
31865             
31866         }, this);
31867         
31868         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31869     },
31870     
31871     /** Sets position of item in DOM
31872     * @param {Element} item
31873     * @param {Number} x - horizontal position
31874     * @param {Number} y - vertical position
31875     * @param {Boolean} isInstant - disables transitions
31876     */
31877     _processVerticalLayoutQueue : function( queue, isInstant )
31878     {
31879         var pos = this.el.getBox(true);
31880         var x = pos.x;
31881         var y = pos.y;
31882         var maxY = [];
31883         
31884         for (var i = 0; i < this.cols; i++){
31885             maxY[i] = pos.y;
31886         }
31887         
31888         Roo.each(queue, function(box, k){
31889             
31890             var col = k % this.cols;
31891             
31892             Roo.each(box, function(b,kk){
31893                 
31894                 b.el.position('absolute');
31895                 
31896                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31897                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31898                 
31899                 if(b.size == 'md-left' || b.size == 'md-right'){
31900                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31901                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31902                 }
31903                 
31904                 b.el.setWidth(width);
31905                 b.el.setHeight(height);
31906                 // iframe?
31907                 b.el.select('iframe',true).setSize(width,height);
31908                 
31909             }, this);
31910             
31911             for (var i = 0; i < this.cols; i++){
31912                 
31913                 if(maxY[i] < maxY[col]){
31914                     col = i;
31915                     continue;
31916                 }
31917                 
31918                 col = Math.min(col, i);
31919                 
31920             }
31921             
31922             x = pos.x + col * (this.colWidth + this.padWidth);
31923             
31924             y = maxY[col];
31925             
31926             var positions = [];
31927             
31928             switch (box.length){
31929                 case 1 :
31930                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31931                     break;
31932                 case 2 :
31933                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31934                     break;
31935                 case 3 :
31936                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31937                     break;
31938                 case 4 :
31939                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31940                     break;
31941                 default :
31942                     break;
31943             }
31944             
31945             Roo.each(box, function(b,kk){
31946                 
31947                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31948                 
31949                 var sz = b.el.getSize();
31950                 
31951                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31952                 
31953             }, this);
31954             
31955         }, this);
31956         
31957         var mY = 0;
31958         
31959         for (var i = 0; i < this.cols; i++){
31960             mY = Math.max(mY, maxY[i]);
31961         }
31962         
31963         this.el.setHeight(mY - pos.y);
31964         
31965     },
31966     
31967 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31968 //    {
31969 //        var pos = this.el.getBox(true);
31970 //        var x = pos.x;
31971 //        var y = pos.y;
31972 //        var maxX = pos.right;
31973 //        
31974 //        var maxHeight = 0;
31975 //        
31976 //        Roo.each(items, function(item, k){
31977 //            
31978 //            var c = k % 2;
31979 //            
31980 //            item.el.position('absolute');
31981 //                
31982 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31983 //
31984 //            item.el.setWidth(width);
31985 //
31986 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31987 //
31988 //            item.el.setHeight(height);
31989 //            
31990 //            if(c == 0){
31991 //                item.el.setXY([x, y], isInstant ? false : true);
31992 //            } else {
31993 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31994 //            }
31995 //            
31996 //            y = y + height + this.alternativePadWidth;
31997 //            
31998 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31999 //            
32000 //        }, this);
32001 //        
32002 //        this.el.setHeight(maxHeight);
32003 //        
32004 //    },
32005     
32006     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32007     {
32008         var pos = this.el.getBox(true);
32009         
32010         var minX = pos.x;
32011         var minY = pos.y;
32012         
32013         var maxX = pos.right;
32014         
32015         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32016         
32017         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32018         
32019         Roo.each(queue, function(box, k){
32020             
32021             Roo.each(box, function(b, kk){
32022                 
32023                 b.el.position('absolute');
32024                 
32025                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32026                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32027                 
32028                 if(b.size == 'md-left' || b.size == 'md-right'){
32029                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32030                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32031                 }
32032                 
32033                 b.el.setWidth(width);
32034                 b.el.setHeight(height);
32035                 
32036             }, this);
32037             
32038             if(!box.length){
32039                 return;
32040             }
32041             
32042             var positions = [];
32043             
32044             switch (box.length){
32045                 case 1 :
32046                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32047                     break;
32048                 case 2 :
32049                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32050                     break;
32051                 case 3 :
32052                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32053                     break;
32054                 case 4 :
32055                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32056                     break;
32057                 default :
32058                     break;
32059             }
32060             
32061             Roo.each(box, function(b,kk){
32062                 
32063                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32064                 
32065                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32066                 
32067             }, this);
32068             
32069         }, this);
32070         
32071     },
32072     
32073     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32074     {
32075         Roo.each(eItems, function(b,k){
32076             
32077             b.size = (k == 0) ? 'sm' : 'xs';
32078             b.x = (k == 0) ? 2 : 1;
32079             b.y = (k == 0) ? 2 : 1;
32080             
32081             b.el.position('absolute');
32082             
32083             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32084                 
32085             b.el.setWidth(width);
32086             
32087             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32088             
32089             b.el.setHeight(height);
32090             
32091         }, this);
32092
32093         var positions = [];
32094         
32095         positions.push({
32096             x : maxX - this.unitWidth * 2 - this.gutter,
32097             y : minY
32098         });
32099         
32100         positions.push({
32101             x : maxX - this.unitWidth,
32102             y : minY + (this.unitWidth + this.gutter) * 2
32103         });
32104         
32105         positions.push({
32106             x : maxX - this.unitWidth * 3 - this.gutter * 2,
32107             y : minY
32108         });
32109         
32110         Roo.each(eItems, function(b,k){
32111             
32112             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32113
32114         }, this);
32115         
32116     },
32117     
32118     getVerticalOneBoxColPositions : function(x, y, box)
32119     {
32120         var pos = [];
32121         
32122         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32123         
32124         if(box[0].size == 'md-left'){
32125             rand = 0;
32126         }
32127         
32128         if(box[0].size == 'md-right'){
32129             rand = 1;
32130         }
32131         
32132         pos.push({
32133             x : x + (this.unitWidth + this.gutter) * rand,
32134             y : y
32135         });
32136         
32137         return pos;
32138     },
32139     
32140     getVerticalTwoBoxColPositions : function(x, y, box)
32141     {
32142         var pos = [];
32143         
32144         if(box[0].size == 'xs'){
32145             
32146             pos.push({
32147                 x : x,
32148                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32149             });
32150
32151             pos.push({
32152                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32153                 y : y
32154             });
32155             
32156             return pos;
32157             
32158         }
32159         
32160         pos.push({
32161             x : x,
32162             y : y
32163         });
32164
32165         pos.push({
32166             x : x + (this.unitWidth + this.gutter) * 2,
32167             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32168         });
32169         
32170         return pos;
32171         
32172     },
32173     
32174     getVerticalThreeBoxColPositions : function(x, y, box)
32175     {
32176         var pos = [];
32177         
32178         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32179             
32180             pos.push({
32181                 x : x,
32182                 y : y
32183             });
32184
32185             pos.push({
32186                 x : x + (this.unitWidth + this.gutter) * 1,
32187                 y : y
32188             });
32189             
32190             pos.push({
32191                 x : x + (this.unitWidth + this.gutter) * 2,
32192                 y : y
32193             });
32194             
32195             return pos;
32196             
32197         }
32198         
32199         if(box[0].size == 'xs' && box[1].size == 'xs'){
32200             
32201             pos.push({
32202                 x : x,
32203                 y : y
32204             });
32205
32206             pos.push({
32207                 x : x,
32208                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32209             });
32210             
32211             pos.push({
32212                 x : x + (this.unitWidth + this.gutter) * 1,
32213                 y : y
32214             });
32215             
32216             return pos;
32217             
32218         }
32219         
32220         pos.push({
32221             x : x,
32222             y : y
32223         });
32224
32225         pos.push({
32226             x : x + (this.unitWidth + this.gutter) * 2,
32227             y : y
32228         });
32229
32230         pos.push({
32231             x : x + (this.unitWidth + this.gutter) * 2,
32232             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32233         });
32234             
32235         return pos;
32236         
32237     },
32238     
32239     getVerticalFourBoxColPositions : function(x, y, box)
32240     {
32241         var pos = [];
32242         
32243         if(box[0].size == 'xs'){
32244             
32245             pos.push({
32246                 x : x,
32247                 y : y
32248             });
32249
32250             pos.push({
32251                 x : x,
32252                 y : y + (this.unitHeight + this.gutter) * 1
32253             });
32254             
32255             pos.push({
32256                 x : x,
32257                 y : y + (this.unitHeight + this.gutter) * 2
32258             });
32259             
32260             pos.push({
32261                 x : x + (this.unitWidth + this.gutter) * 1,
32262                 y : y
32263             });
32264             
32265             return pos;
32266             
32267         }
32268         
32269         pos.push({
32270             x : x,
32271             y : y
32272         });
32273
32274         pos.push({
32275             x : x + (this.unitWidth + this.gutter) * 2,
32276             y : y
32277         });
32278
32279         pos.push({
32280             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32281             y : y + (this.unitHeight + this.gutter) * 1
32282         });
32283
32284         pos.push({
32285             x : x + (this.unitWidth + this.gutter) * 2,
32286             y : y + (this.unitWidth + this.gutter) * 2
32287         });
32288
32289         return pos;
32290         
32291     },
32292     
32293     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32294     {
32295         var pos = [];
32296         
32297         if(box[0].size == 'md-left'){
32298             pos.push({
32299                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32300                 y : minY
32301             });
32302             
32303             return pos;
32304         }
32305         
32306         if(box[0].size == 'md-right'){
32307             pos.push({
32308                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32309                 y : minY + (this.unitWidth + this.gutter) * 1
32310             });
32311             
32312             return pos;
32313         }
32314         
32315         var rand = Math.floor(Math.random() * (4 - box[0].y));
32316         
32317         pos.push({
32318             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32319             y : minY + (this.unitWidth + this.gutter) * rand
32320         });
32321         
32322         return pos;
32323         
32324     },
32325     
32326     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32327     {
32328         var pos = [];
32329         
32330         if(box[0].size == 'xs'){
32331             
32332             pos.push({
32333                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32334                 y : minY
32335             });
32336
32337             pos.push({
32338                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32339                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32340             });
32341             
32342             return pos;
32343             
32344         }
32345         
32346         pos.push({
32347             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32348             y : minY
32349         });
32350
32351         pos.push({
32352             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32353             y : minY + (this.unitWidth + this.gutter) * 2
32354         });
32355         
32356         return pos;
32357         
32358     },
32359     
32360     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32361     {
32362         var pos = [];
32363         
32364         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32365             
32366             pos.push({
32367                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32368                 y : minY
32369             });
32370
32371             pos.push({
32372                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32373                 y : minY + (this.unitWidth + this.gutter) * 1
32374             });
32375             
32376             pos.push({
32377                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32378                 y : minY + (this.unitWidth + this.gutter) * 2
32379             });
32380             
32381             return pos;
32382             
32383         }
32384         
32385         if(box[0].size == 'xs' && box[1].size == 'xs'){
32386             
32387             pos.push({
32388                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32389                 y : minY
32390             });
32391
32392             pos.push({
32393                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32394                 y : minY
32395             });
32396             
32397             pos.push({
32398                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32399                 y : minY + (this.unitWidth + this.gutter) * 1
32400             });
32401             
32402             return pos;
32403             
32404         }
32405         
32406         pos.push({
32407             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32408             y : minY
32409         });
32410
32411         pos.push({
32412             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32413             y : minY + (this.unitWidth + this.gutter) * 2
32414         });
32415
32416         pos.push({
32417             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32418             y : minY + (this.unitWidth + this.gutter) * 2
32419         });
32420             
32421         return pos;
32422         
32423     },
32424     
32425     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32426     {
32427         var pos = [];
32428         
32429         if(box[0].size == 'xs'){
32430             
32431             pos.push({
32432                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32433                 y : minY
32434             });
32435
32436             pos.push({
32437                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32438                 y : minY
32439             });
32440             
32441             pos.push({
32442                 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),
32443                 y : minY
32444             });
32445             
32446             pos.push({
32447                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32448                 y : minY + (this.unitWidth + this.gutter) * 1
32449             });
32450             
32451             return pos;
32452             
32453         }
32454         
32455         pos.push({
32456             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32457             y : minY
32458         });
32459         
32460         pos.push({
32461             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32462             y : minY + (this.unitWidth + this.gutter) * 2
32463         });
32464         
32465         pos.push({
32466             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32467             y : minY + (this.unitWidth + this.gutter) * 2
32468         });
32469         
32470         pos.push({
32471             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),
32472             y : minY + (this.unitWidth + this.gutter) * 2
32473         });
32474
32475         return pos;
32476         
32477     },
32478     
32479     /**
32480     * remove a Masonry Brick
32481     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32482     */
32483     removeBrick : function(brick_id)
32484     {
32485         if (!brick_id) {
32486             return;
32487         }
32488         
32489         for (var i = 0; i<this.bricks.length; i++) {
32490             if (this.bricks[i].id == brick_id) {
32491                 this.bricks.splice(i,1);
32492                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32493                 this.initial();
32494             }
32495         }
32496     },
32497     
32498     /**
32499     * adds a Masonry Brick
32500     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32501     */
32502     addBrick : function(cfg)
32503     {
32504         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32505         //this.register(cn);
32506         cn.parentId = this.id;
32507         cn.render(this.el);
32508         return cn;
32509     },
32510     
32511     /**
32512     * register a Masonry Brick
32513     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32514     */
32515     
32516     register : function(brick)
32517     {
32518         this.bricks.push(brick);
32519         brick.masonryId = this.id;
32520     },
32521     
32522     /**
32523     * clear all the Masonry Brick
32524     */
32525     clearAll : function()
32526     {
32527         this.bricks = [];
32528         //this.getChildContainer().dom.innerHTML = "";
32529         this.el.dom.innerHTML = '';
32530     },
32531     
32532     getSelected : function()
32533     {
32534         if (!this.selectedBrick) {
32535             return false;
32536         }
32537         
32538         return this.selectedBrick;
32539     }
32540 });
32541
32542 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32543     
32544     groups: {},
32545      /**
32546     * register a Masonry Layout
32547     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32548     */
32549     
32550     register : function(layout)
32551     {
32552         this.groups[layout.id] = layout;
32553     },
32554     /**
32555     * fetch a  Masonry Layout based on the masonry layout ID
32556     * @param {string} the masonry layout to add
32557     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32558     */
32559     
32560     get: function(layout_id) {
32561         if (typeof(this.groups[layout_id]) == 'undefined') {
32562             return false;
32563         }
32564         return this.groups[layout_id] ;
32565     }
32566     
32567     
32568     
32569 });
32570
32571  
32572
32573  /**
32574  *
32575  * This is based on 
32576  * http://masonry.desandro.com
32577  *
32578  * The idea is to render all the bricks based on vertical width...
32579  *
32580  * The original code extends 'outlayer' - we might need to use that....
32581  * 
32582  */
32583
32584
32585 /**
32586  * @class Roo.bootstrap.LayoutMasonryAuto
32587  * @extends Roo.bootstrap.Component
32588  * Bootstrap Layout Masonry class
32589  * 
32590  * @constructor
32591  * Create a new Element
32592  * @param {Object} config The config object
32593  */
32594
32595 Roo.bootstrap.LayoutMasonryAuto = function(config){
32596     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32597 };
32598
32599 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32600     
32601       /**
32602      * @cfg {Boolean} isFitWidth  - resize the width..
32603      */   
32604     isFitWidth : false,  // options..
32605     /**
32606      * @cfg {Boolean} isOriginLeft = left align?
32607      */   
32608     isOriginLeft : true,
32609     /**
32610      * @cfg {Boolean} isOriginTop = top align?
32611      */   
32612     isOriginTop : false,
32613     /**
32614      * @cfg {Boolean} isLayoutInstant = no animation?
32615      */   
32616     isLayoutInstant : false, // needed?
32617     /**
32618      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32619      */   
32620     isResizingContainer : true,
32621     /**
32622      * @cfg {Number} columnWidth  width of the columns 
32623      */   
32624     
32625     columnWidth : 0,
32626     
32627     /**
32628      * @cfg {Number} maxCols maximum number of columns
32629      */   
32630     
32631     maxCols: 0,
32632     /**
32633      * @cfg {Number} padHeight padding below box..
32634      */   
32635     
32636     padHeight : 10, 
32637     
32638     /**
32639      * @cfg {Boolean} isAutoInitial defalut true
32640      */   
32641     
32642     isAutoInitial : true, 
32643     
32644     // private?
32645     gutter : 0,
32646     
32647     containerWidth: 0,
32648     initialColumnWidth : 0,
32649     currentSize : null,
32650     
32651     colYs : null, // array.
32652     maxY : 0,
32653     padWidth: 10,
32654     
32655     
32656     tag: 'div',
32657     cls: '',
32658     bricks: null, //CompositeElement
32659     cols : 0, // array?
32660     // element : null, // wrapped now this.el
32661     _isLayoutInited : null, 
32662     
32663     
32664     getAutoCreate : function(){
32665         
32666         var cfg = {
32667             tag: this.tag,
32668             cls: 'blog-masonary-wrapper ' + this.cls,
32669             cn : {
32670                 cls : 'mas-boxes masonary'
32671             }
32672         };
32673         
32674         return cfg;
32675     },
32676     
32677     getChildContainer: function( )
32678     {
32679         if (this.boxesEl) {
32680             return this.boxesEl;
32681         }
32682         
32683         this.boxesEl = this.el.select('.mas-boxes').first();
32684         
32685         return this.boxesEl;
32686     },
32687     
32688     
32689     initEvents : function()
32690     {
32691         var _this = this;
32692         
32693         if(this.isAutoInitial){
32694             Roo.log('hook children rendered');
32695             this.on('childrenrendered', function() {
32696                 Roo.log('children rendered');
32697                 _this.initial();
32698             } ,this);
32699         }
32700         
32701     },
32702     
32703     initial : function()
32704     {
32705         this.reloadItems();
32706
32707         this.currentSize = this.el.getBox(true);
32708
32709         /// was window resize... - let's see if this works..
32710         Roo.EventManager.onWindowResize(this.resize, this); 
32711
32712         if(!this.isAutoInitial){
32713             this.layout();
32714             return;
32715         }
32716         
32717         this.layout.defer(500,this);
32718     },
32719     
32720     reloadItems: function()
32721     {
32722         this.bricks = this.el.select('.masonry-brick', true);
32723         
32724         this.bricks.each(function(b) {
32725             //Roo.log(b.getSize());
32726             if (!b.attr('originalwidth')) {
32727                 b.attr('originalwidth',  b.getSize().width);
32728             }
32729             
32730         });
32731         
32732         Roo.log(this.bricks.elements.length);
32733     },
32734     
32735     resize : function()
32736     {
32737         Roo.log('resize');
32738         var cs = this.el.getBox(true);
32739         
32740         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32741             Roo.log("no change in with or X");
32742             return;
32743         }
32744         this.currentSize = cs;
32745         this.layout();
32746     },
32747     
32748     layout : function()
32749     {
32750          Roo.log('layout');
32751         this._resetLayout();
32752         //this._manageStamps();
32753       
32754         // don't animate first layout
32755         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32756         this.layoutItems( isInstant );
32757       
32758         // flag for initalized
32759         this._isLayoutInited = true;
32760     },
32761     
32762     layoutItems : function( isInstant )
32763     {
32764         //var items = this._getItemsForLayout( this.items );
32765         // original code supports filtering layout items.. we just ignore it..
32766         
32767         this._layoutItems( this.bricks , isInstant );
32768       
32769         this._postLayout();
32770     },
32771     _layoutItems : function ( items , isInstant)
32772     {
32773        //this.fireEvent( 'layout', this, items );
32774     
32775
32776         if ( !items || !items.elements.length ) {
32777           // no items, emit event with empty array
32778             return;
32779         }
32780
32781         var queue = [];
32782         items.each(function(item) {
32783             Roo.log("layout item");
32784             Roo.log(item);
32785             // get x/y object from method
32786             var position = this._getItemLayoutPosition( item );
32787             // enqueue
32788             position.item = item;
32789             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32790             queue.push( position );
32791         }, this);
32792       
32793         this._processLayoutQueue( queue );
32794     },
32795     /** Sets position of item in DOM
32796     * @param {Element} item
32797     * @param {Number} x - horizontal position
32798     * @param {Number} y - vertical position
32799     * @param {Boolean} isInstant - disables transitions
32800     */
32801     _processLayoutQueue : function( queue )
32802     {
32803         for ( var i=0, len = queue.length; i < len; i++ ) {
32804             var obj = queue[i];
32805             obj.item.position('absolute');
32806             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32807         }
32808     },
32809       
32810     
32811     /**
32812     * Any logic you want to do after each layout,
32813     * i.e. size the container
32814     */
32815     _postLayout : function()
32816     {
32817         this.resizeContainer();
32818     },
32819     
32820     resizeContainer : function()
32821     {
32822         if ( !this.isResizingContainer ) {
32823             return;
32824         }
32825         var size = this._getContainerSize();
32826         if ( size ) {
32827             this.el.setSize(size.width,size.height);
32828             this.boxesEl.setSize(size.width,size.height);
32829         }
32830     },
32831     
32832     
32833     
32834     _resetLayout : function()
32835     {
32836         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32837         this.colWidth = this.el.getWidth();
32838         //this.gutter = this.el.getWidth(); 
32839         
32840         this.measureColumns();
32841
32842         // reset column Y
32843         var i = this.cols;
32844         this.colYs = [];
32845         while (i--) {
32846             this.colYs.push( 0 );
32847         }
32848     
32849         this.maxY = 0;
32850     },
32851
32852     measureColumns : function()
32853     {
32854         this.getContainerWidth();
32855       // if columnWidth is 0, default to outerWidth of first item
32856         if ( !this.columnWidth ) {
32857             var firstItem = this.bricks.first();
32858             Roo.log(firstItem);
32859             this.columnWidth  = this.containerWidth;
32860             if (firstItem && firstItem.attr('originalwidth') ) {
32861                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32862             }
32863             // columnWidth fall back to item of first element
32864             Roo.log("set column width?");
32865                         this.initialColumnWidth = this.columnWidth  ;
32866
32867             // if first elem has no width, default to size of container
32868             
32869         }
32870         
32871         
32872         if (this.initialColumnWidth) {
32873             this.columnWidth = this.initialColumnWidth;
32874         }
32875         
32876         
32877             
32878         // column width is fixed at the top - however if container width get's smaller we should
32879         // reduce it...
32880         
32881         // this bit calcs how man columns..
32882             
32883         var columnWidth = this.columnWidth += this.gutter;
32884       
32885         // calculate columns
32886         var containerWidth = this.containerWidth + this.gutter;
32887         
32888         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32889         // fix rounding errors, typically with gutters
32890         var excess = columnWidth - containerWidth % columnWidth;
32891         
32892         
32893         // if overshoot is less than a pixel, round up, otherwise floor it
32894         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32895         cols = Math[ mathMethod ]( cols );
32896         this.cols = Math.max( cols, 1 );
32897         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32898         
32899          // padding positioning..
32900         var totalColWidth = this.cols * this.columnWidth;
32901         var padavail = this.containerWidth - totalColWidth;
32902         // so for 2 columns - we need 3 'pads'
32903         
32904         var padNeeded = (1+this.cols) * this.padWidth;
32905         
32906         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32907         
32908         this.columnWidth += padExtra
32909         //this.padWidth = Math.floor(padavail /  ( this.cols));
32910         
32911         // adjust colum width so that padding is fixed??
32912         
32913         // we have 3 columns ... total = width * 3
32914         // we have X left over... that should be used by 
32915         
32916         //if (this.expandC) {
32917             
32918         //}
32919         
32920         
32921         
32922     },
32923     
32924     getContainerWidth : function()
32925     {
32926        /* // container is parent if fit width
32927         var container = this.isFitWidth ? this.element.parentNode : this.element;
32928         // check that this.size and size are there
32929         // IE8 triggers resize on body size change, so they might not be
32930         
32931         var size = getSize( container );  //FIXME
32932         this.containerWidth = size && size.innerWidth; //FIXME
32933         */
32934          
32935         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32936         
32937     },
32938     
32939     _getItemLayoutPosition : function( item )  // what is item?
32940     {
32941         // we resize the item to our columnWidth..
32942       
32943         item.setWidth(this.columnWidth);
32944         item.autoBoxAdjust  = false;
32945         
32946         var sz = item.getSize();
32947  
32948         // how many columns does this brick span
32949         var remainder = this.containerWidth % this.columnWidth;
32950         
32951         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32952         // round if off by 1 pixel, otherwise use ceil
32953         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32954         colSpan = Math.min( colSpan, this.cols );
32955         
32956         // normally this should be '1' as we dont' currently allow multi width columns..
32957         
32958         var colGroup = this._getColGroup( colSpan );
32959         // get the minimum Y value from the columns
32960         var minimumY = Math.min.apply( Math, colGroup );
32961         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32962         
32963         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32964          
32965         // position the brick
32966         var position = {
32967             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32968             y: this.currentSize.y + minimumY + this.padHeight
32969         };
32970         
32971         Roo.log(position);
32972         // apply setHeight to necessary columns
32973         var setHeight = minimumY + sz.height + this.padHeight;
32974         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32975         
32976         var setSpan = this.cols + 1 - colGroup.length;
32977         for ( var i = 0; i < setSpan; i++ ) {
32978           this.colYs[ shortColIndex + i ] = setHeight ;
32979         }
32980       
32981         return position;
32982     },
32983     
32984     /**
32985      * @param {Number} colSpan - number of columns the element spans
32986      * @returns {Array} colGroup
32987      */
32988     _getColGroup : function( colSpan )
32989     {
32990         if ( colSpan < 2 ) {
32991           // if brick spans only one column, use all the column Ys
32992           return this.colYs;
32993         }
32994       
32995         var colGroup = [];
32996         // how many different places could this brick fit horizontally
32997         var groupCount = this.cols + 1 - colSpan;
32998         // for each group potential horizontal position
32999         for ( var i = 0; i < groupCount; i++ ) {
33000           // make an array of colY values for that one group
33001           var groupColYs = this.colYs.slice( i, i + colSpan );
33002           // and get the max value of the array
33003           colGroup[i] = Math.max.apply( Math, groupColYs );
33004         }
33005         return colGroup;
33006     },
33007     /*
33008     _manageStamp : function( stamp )
33009     {
33010         var stampSize =  stamp.getSize();
33011         var offset = stamp.getBox();
33012         // get the columns that this stamp affects
33013         var firstX = this.isOriginLeft ? offset.x : offset.right;
33014         var lastX = firstX + stampSize.width;
33015         var firstCol = Math.floor( firstX / this.columnWidth );
33016         firstCol = Math.max( 0, firstCol );
33017         
33018         var lastCol = Math.floor( lastX / this.columnWidth );
33019         // lastCol should not go over if multiple of columnWidth #425
33020         lastCol -= lastX % this.columnWidth ? 0 : 1;
33021         lastCol = Math.min( this.cols - 1, lastCol );
33022         
33023         // set colYs to bottom of the stamp
33024         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33025             stampSize.height;
33026             
33027         for ( var i = firstCol; i <= lastCol; i++ ) {
33028           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33029         }
33030     },
33031     */
33032     
33033     _getContainerSize : function()
33034     {
33035         this.maxY = Math.max.apply( Math, this.colYs );
33036         var size = {
33037             height: this.maxY
33038         };
33039       
33040         if ( this.isFitWidth ) {
33041             size.width = this._getContainerFitWidth();
33042         }
33043       
33044         return size;
33045     },
33046     
33047     _getContainerFitWidth : function()
33048     {
33049         var unusedCols = 0;
33050         // count unused columns
33051         var i = this.cols;
33052         while ( --i ) {
33053           if ( this.colYs[i] !== 0 ) {
33054             break;
33055           }
33056           unusedCols++;
33057         }
33058         // fit container to columns that have been used
33059         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33060     },
33061     
33062     needsResizeLayout : function()
33063     {
33064         var previousWidth = this.containerWidth;
33065         this.getContainerWidth();
33066         return previousWidth !== this.containerWidth;
33067     }
33068  
33069 });
33070
33071  
33072
33073  /*
33074  * - LGPL
33075  *
33076  * element
33077  * 
33078  */
33079
33080 /**
33081  * @class Roo.bootstrap.MasonryBrick
33082  * @extends Roo.bootstrap.Component
33083  * Bootstrap MasonryBrick class
33084  * 
33085  * @constructor
33086  * Create a new MasonryBrick
33087  * @param {Object} config The config object
33088  */
33089
33090 Roo.bootstrap.MasonryBrick = function(config){
33091     
33092     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33093     
33094     Roo.bootstrap.MasonryBrick.register(this);
33095     
33096     this.addEvents({
33097         // raw events
33098         /**
33099          * @event click
33100          * When a MasonryBrick is clcik
33101          * @param {Roo.bootstrap.MasonryBrick} this
33102          * @param {Roo.EventObject} e
33103          */
33104         "click" : true
33105     });
33106 };
33107
33108 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
33109     
33110     /**
33111      * @cfg {String} title
33112      */   
33113     title : '',
33114     /**
33115      * @cfg {String} html
33116      */   
33117     html : '',
33118     /**
33119      * @cfg {String} bgimage
33120      */   
33121     bgimage : '',
33122     /**
33123      * @cfg {String} videourl
33124      */   
33125     videourl : '',
33126     /**
33127      * @cfg {String} cls
33128      */   
33129     cls : '',
33130     /**
33131      * @cfg {String} href
33132      */   
33133     href : '',
33134     /**
33135      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33136      */   
33137     size : 'xs',
33138     
33139     /**
33140      * @cfg {String} placetitle (center|bottom)
33141      */   
33142     placetitle : '',
33143     
33144     /**
33145      * @cfg {Boolean} isFitContainer defalut true
33146      */   
33147     isFitContainer : true, 
33148     
33149     /**
33150      * @cfg {Boolean} preventDefault defalut false
33151      */   
33152     preventDefault : false, 
33153     
33154     /**
33155      * @cfg {Boolean} inverse defalut false
33156      */   
33157     maskInverse : false, 
33158     
33159     getAutoCreate : function()
33160     {
33161         if(!this.isFitContainer){
33162             return this.getSplitAutoCreate();
33163         }
33164         
33165         var cls = 'masonry-brick masonry-brick-full';
33166         
33167         if(this.href.length){
33168             cls += ' masonry-brick-link';
33169         }
33170         
33171         if(this.bgimage.length){
33172             cls += ' masonry-brick-image';
33173         }
33174         
33175         if(this.maskInverse){
33176             cls += ' mask-inverse';
33177         }
33178         
33179         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33180             cls += ' enable-mask';
33181         }
33182         
33183         if(this.size){
33184             cls += ' masonry-' + this.size + '-brick';
33185         }
33186         
33187         if(this.placetitle.length){
33188             
33189             switch (this.placetitle) {
33190                 case 'center' :
33191                     cls += ' masonry-center-title';
33192                     break;
33193                 case 'bottom' :
33194                     cls += ' masonry-bottom-title';
33195                     break;
33196                 default:
33197                     break;
33198             }
33199             
33200         } else {
33201             if(!this.html.length && !this.bgimage.length){
33202                 cls += ' masonry-center-title';
33203             }
33204
33205             if(!this.html.length && this.bgimage.length){
33206                 cls += ' masonry-bottom-title';
33207             }
33208         }
33209         
33210         if(this.cls){
33211             cls += ' ' + this.cls;
33212         }
33213         
33214         var cfg = {
33215             tag: (this.href.length) ? 'a' : 'div',
33216             cls: cls,
33217             cn: [
33218                 {
33219                     tag: 'div',
33220                     cls: 'masonry-brick-mask'
33221                 },
33222                 {
33223                     tag: 'div',
33224                     cls: 'masonry-brick-paragraph',
33225                     cn: []
33226                 }
33227             ]
33228         };
33229         
33230         if(this.href.length){
33231             cfg.href = this.href;
33232         }
33233         
33234         var cn = cfg.cn[1].cn;
33235         
33236         if(this.title.length){
33237             cn.push({
33238                 tag: 'h4',
33239                 cls: 'masonry-brick-title',
33240                 html: this.title
33241             });
33242         }
33243         
33244         if(this.html.length){
33245             cn.push({
33246                 tag: 'p',
33247                 cls: 'masonry-brick-text',
33248                 html: this.html
33249             });
33250         }
33251         
33252         if (!this.title.length && !this.html.length) {
33253             cfg.cn[1].cls += ' hide';
33254         }
33255         
33256         if(this.bgimage.length){
33257             cfg.cn.push({
33258                 tag: 'img',
33259                 cls: 'masonry-brick-image-view',
33260                 src: this.bgimage
33261             });
33262         }
33263         
33264         if(this.videourl.length){
33265             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33266             // youtube support only?
33267             cfg.cn.push({
33268                 tag: 'iframe',
33269                 cls: 'masonry-brick-image-view',
33270                 src: vurl,
33271                 frameborder : 0,
33272                 allowfullscreen : true
33273             });
33274         }
33275         
33276         return cfg;
33277         
33278     },
33279     
33280     getSplitAutoCreate : function()
33281     {
33282         var cls = 'masonry-brick masonry-brick-split';
33283         
33284         if(this.href.length){
33285             cls += ' masonry-brick-link';
33286         }
33287         
33288         if(this.bgimage.length){
33289             cls += ' masonry-brick-image';
33290         }
33291         
33292         if(this.size){
33293             cls += ' masonry-' + this.size + '-brick';
33294         }
33295         
33296         switch (this.placetitle) {
33297             case 'center' :
33298                 cls += ' masonry-center-title';
33299                 break;
33300             case 'bottom' :
33301                 cls += ' masonry-bottom-title';
33302                 break;
33303             default:
33304                 if(!this.bgimage.length){
33305                     cls += ' masonry-center-title';
33306                 }
33307
33308                 if(this.bgimage.length){
33309                     cls += ' masonry-bottom-title';
33310                 }
33311                 break;
33312         }
33313         
33314         if(this.cls){
33315             cls += ' ' + this.cls;
33316         }
33317         
33318         var cfg = {
33319             tag: (this.href.length) ? 'a' : 'div',
33320             cls: cls,
33321             cn: [
33322                 {
33323                     tag: 'div',
33324                     cls: 'masonry-brick-split-head',
33325                     cn: [
33326                         {
33327                             tag: 'div',
33328                             cls: 'masonry-brick-paragraph',
33329                             cn: []
33330                         }
33331                     ]
33332                 },
33333                 {
33334                     tag: 'div',
33335                     cls: 'masonry-brick-split-body',
33336                     cn: []
33337                 }
33338             ]
33339         };
33340         
33341         if(this.href.length){
33342             cfg.href = this.href;
33343         }
33344         
33345         if(this.title.length){
33346             cfg.cn[0].cn[0].cn.push({
33347                 tag: 'h4',
33348                 cls: 'masonry-brick-title',
33349                 html: this.title
33350             });
33351         }
33352         
33353         if(this.html.length){
33354             cfg.cn[1].cn.push({
33355                 tag: 'p',
33356                 cls: 'masonry-brick-text',
33357                 html: this.html
33358             });
33359         }
33360
33361         if(this.bgimage.length){
33362             cfg.cn[0].cn.push({
33363                 tag: 'img',
33364                 cls: 'masonry-brick-image-view',
33365                 src: this.bgimage
33366             });
33367         }
33368         
33369         if(this.videourl.length){
33370             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33371             // youtube support only?
33372             cfg.cn[0].cn.cn.push({
33373                 tag: 'iframe',
33374                 cls: 'masonry-brick-image-view',
33375                 src: vurl,
33376                 frameborder : 0,
33377                 allowfullscreen : true
33378             });
33379         }
33380         
33381         return cfg;
33382     },
33383     
33384     initEvents: function() 
33385     {
33386         switch (this.size) {
33387             case 'xs' :
33388                 this.x = 1;
33389                 this.y = 1;
33390                 break;
33391             case 'sm' :
33392                 this.x = 2;
33393                 this.y = 2;
33394                 break;
33395             case 'md' :
33396             case 'md-left' :
33397             case 'md-right' :
33398                 this.x = 3;
33399                 this.y = 3;
33400                 break;
33401             case 'tall' :
33402                 this.x = 2;
33403                 this.y = 3;
33404                 break;
33405             case 'wide' :
33406                 this.x = 3;
33407                 this.y = 2;
33408                 break;
33409             case 'wide-thin' :
33410                 this.x = 3;
33411                 this.y = 1;
33412                 break;
33413                         
33414             default :
33415                 break;
33416         }
33417         
33418         if(Roo.isTouch){
33419             this.el.on('touchstart', this.onTouchStart, this);
33420             this.el.on('touchmove', this.onTouchMove, this);
33421             this.el.on('touchend', this.onTouchEnd, this);
33422             this.el.on('contextmenu', this.onContextMenu, this);
33423         } else {
33424             this.el.on('mouseenter'  ,this.enter, this);
33425             this.el.on('mouseleave', this.leave, this);
33426             this.el.on('click', this.onClick, this);
33427         }
33428         
33429         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33430             this.parent().bricks.push(this);   
33431         }
33432         
33433     },
33434     
33435     onClick: function(e, el)
33436     {
33437         var time = this.endTimer - this.startTimer;
33438         // Roo.log(e.preventDefault());
33439         if(Roo.isTouch){
33440             if(time > 1000){
33441                 e.preventDefault();
33442                 return;
33443             }
33444         }
33445         
33446         if(!this.preventDefault){
33447             return;
33448         }
33449         
33450         e.preventDefault();
33451         
33452         if (this.activeClass != '') {
33453             this.selectBrick();
33454         }
33455         
33456         this.fireEvent('click', this, e);
33457     },
33458     
33459     enter: function(e, el)
33460     {
33461         e.preventDefault();
33462         
33463         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33464             return;
33465         }
33466         
33467         if(this.bgimage.length && this.html.length){
33468             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33469         }
33470     },
33471     
33472     leave: function(e, el)
33473     {
33474         e.preventDefault();
33475         
33476         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33477             return;
33478         }
33479         
33480         if(this.bgimage.length && this.html.length){
33481             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33482         }
33483     },
33484     
33485     onTouchStart: function(e, el)
33486     {
33487 //        e.preventDefault();
33488         
33489         this.touchmoved = false;
33490         
33491         if(!this.isFitContainer){
33492             return;
33493         }
33494         
33495         if(!this.bgimage.length || !this.html.length){
33496             return;
33497         }
33498         
33499         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33500         
33501         this.timer = new Date().getTime();
33502         
33503     },
33504     
33505     onTouchMove: function(e, el)
33506     {
33507         this.touchmoved = true;
33508     },
33509     
33510     onContextMenu : function(e,el)
33511     {
33512         e.preventDefault();
33513         e.stopPropagation();
33514         return false;
33515     },
33516     
33517     onTouchEnd: function(e, el)
33518     {
33519 //        e.preventDefault();
33520         
33521         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33522         
33523             this.leave(e,el);
33524             
33525             return;
33526         }
33527         
33528         if(!this.bgimage.length || !this.html.length){
33529             
33530             if(this.href.length){
33531                 window.location.href = this.href;
33532             }
33533             
33534             return;
33535         }
33536         
33537         if(!this.isFitContainer){
33538             return;
33539         }
33540         
33541         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33542         
33543         window.location.href = this.href;
33544     },
33545     
33546     //selection on single brick only
33547     selectBrick : function() {
33548         
33549         if (!this.parentId) {
33550             return;
33551         }
33552         
33553         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33554         var index = m.selectedBrick.indexOf(this.id);
33555         
33556         if ( index > -1) {
33557             m.selectedBrick.splice(index,1);
33558             this.el.removeClass(this.activeClass);
33559             return;
33560         }
33561         
33562         for(var i = 0; i < m.selectedBrick.length; i++) {
33563             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33564             b.el.removeClass(b.activeClass);
33565         }
33566         
33567         m.selectedBrick = [];
33568         
33569         m.selectedBrick.push(this.id);
33570         this.el.addClass(this.activeClass);
33571         return;
33572     },
33573     
33574     isSelected : function(){
33575         return this.el.hasClass(this.activeClass);
33576         
33577     }
33578 });
33579
33580 Roo.apply(Roo.bootstrap.MasonryBrick, {
33581     
33582     //groups: {},
33583     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33584      /**
33585     * register a Masonry Brick
33586     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33587     */
33588     
33589     register : function(brick)
33590     {
33591         //this.groups[brick.id] = brick;
33592         this.groups.add(brick.id, brick);
33593     },
33594     /**
33595     * fetch a  masonry brick based on the masonry brick ID
33596     * @param {string} the masonry brick to add
33597     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33598     */
33599     
33600     get: function(brick_id) 
33601     {
33602         // if (typeof(this.groups[brick_id]) == 'undefined') {
33603         //     return false;
33604         // }
33605         // return this.groups[brick_id] ;
33606         
33607         if(this.groups.key(brick_id)) {
33608             return this.groups.key(brick_id);
33609         }
33610         
33611         return false;
33612     }
33613     
33614     
33615     
33616 });
33617
33618  /*
33619  * - LGPL
33620  *
33621  * element
33622  * 
33623  */
33624
33625 /**
33626  * @class Roo.bootstrap.Brick
33627  * @extends Roo.bootstrap.Component
33628  * Bootstrap Brick class
33629  * 
33630  * @constructor
33631  * Create a new Brick
33632  * @param {Object} config The config object
33633  */
33634
33635 Roo.bootstrap.Brick = function(config){
33636     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33637     
33638     this.addEvents({
33639         // raw events
33640         /**
33641          * @event click
33642          * When a Brick is click
33643          * @param {Roo.bootstrap.Brick} this
33644          * @param {Roo.EventObject} e
33645          */
33646         "click" : true
33647     });
33648 };
33649
33650 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33651     
33652     /**
33653      * @cfg {String} title
33654      */   
33655     title : '',
33656     /**
33657      * @cfg {String} html
33658      */   
33659     html : '',
33660     /**
33661      * @cfg {String} bgimage
33662      */   
33663     bgimage : '',
33664     /**
33665      * @cfg {String} cls
33666      */   
33667     cls : '',
33668     /**
33669      * @cfg {String} href
33670      */   
33671     href : '',
33672     /**
33673      * @cfg {String} video
33674      */   
33675     video : '',
33676     /**
33677      * @cfg {Boolean} square
33678      */   
33679     square : true,
33680     
33681     getAutoCreate : function()
33682     {
33683         var cls = 'roo-brick';
33684         
33685         if(this.href.length){
33686             cls += ' roo-brick-link';
33687         }
33688         
33689         if(this.bgimage.length){
33690             cls += ' roo-brick-image';
33691         }
33692         
33693         if(!this.html.length && !this.bgimage.length){
33694             cls += ' roo-brick-center-title';
33695         }
33696         
33697         if(!this.html.length && this.bgimage.length){
33698             cls += ' roo-brick-bottom-title';
33699         }
33700         
33701         if(this.cls){
33702             cls += ' ' + this.cls;
33703         }
33704         
33705         var cfg = {
33706             tag: (this.href.length) ? 'a' : 'div',
33707             cls: cls,
33708             cn: [
33709                 {
33710                     tag: 'div',
33711                     cls: 'roo-brick-paragraph',
33712                     cn: []
33713                 }
33714             ]
33715         };
33716         
33717         if(this.href.length){
33718             cfg.href = this.href;
33719         }
33720         
33721         var cn = cfg.cn[0].cn;
33722         
33723         if(this.title.length){
33724             cn.push({
33725                 tag: 'h4',
33726                 cls: 'roo-brick-title',
33727                 html: this.title
33728             });
33729         }
33730         
33731         if(this.html.length){
33732             cn.push({
33733                 tag: 'p',
33734                 cls: 'roo-brick-text',
33735                 html: this.html
33736             });
33737         } else {
33738             cn.cls += ' hide';
33739         }
33740         
33741         if(this.bgimage.length){
33742             cfg.cn.push({
33743                 tag: 'img',
33744                 cls: 'roo-brick-image-view',
33745                 src: this.bgimage
33746             });
33747         }
33748         
33749         return cfg;
33750     },
33751     
33752     initEvents: function() 
33753     {
33754         if(this.title.length || this.html.length){
33755             this.el.on('mouseenter'  ,this.enter, this);
33756             this.el.on('mouseleave', this.leave, this);
33757         }
33758         
33759         Roo.EventManager.onWindowResize(this.resize, this); 
33760         
33761         if(this.bgimage.length){
33762             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33763             this.imageEl.on('load', this.onImageLoad, this);
33764             return;
33765         }
33766         
33767         this.resize();
33768     },
33769     
33770     onImageLoad : function()
33771     {
33772         this.resize();
33773     },
33774     
33775     resize : function()
33776     {
33777         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33778         
33779         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33780         
33781         if(this.bgimage.length){
33782             var image = this.el.select('.roo-brick-image-view', true).first();
33783             
33784             image.setWidth(paragraph.getWidth());
33785             
33786             if(this.square){
33787                 image.setHeight(paragraph.getWidth());
33788             }
33789             
33790             this.el.setHeight(image.getHeight());
33791             paragraph.setHeight(image.getHeight());
33792             
33793         }
33794         
33795     },
33796     
33797     enter: function(e, el)
33798     {
33799         e.preventDefault();
33800         
33801         if(this.bgimage.length){
33802             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33803             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33804         }
33805     },
33806     
33807     leave: function(e, el)
33808     {
33809         e.preventDefault();
33810         
33811         if(this.bgimage.length){
33812             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33813             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33814         }
33815     }
33816     
33817 });
33818
33819  
33820
33821  /*
33822  * - LGPL
33823  *
33824  * Number field 
33825  */
33826
33827 /**
33828  * @class Roo.bootstrap.NumberField
33829  * @extends Roo.bootstrap.Input
33830  * Bootstrap NumberField class
33831  * 
33832  * 
33833  * 
33834  * 
33835  * @constructor
33836  * Create a new NumberField
33837  * @param {Object} config The config object
33838  */
33839
33840 Roo.bootstrap.NumberField = function(config){
33841     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33842 };
33843
33844 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33845     
33846     /**
33847      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33848      */
33849     allowDecimals : true,
33850     /**
33851      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33852      */
33853     decimalSeparator : ".",
33854     /**
33855      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33856      */
33857     decimalPrecision : 2,
33858     /**
33859      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33860      */
33861     allowNegative : true,
33862     
33863     /**
33864      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33865      */
33866     allowZero: true,
33867     /**
33868      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33869      */
33870     minValue : Number.NEGATIVE_INFINITY,
33871     /**
33872      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33873      */
33874     maxValue : Number.MAX_VALUE,
33875     /**
33876      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33877      */
33878     minText : "The minimum value for this field is {0}",
33879     /**
33880      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33881      */
33882     maxText : "The maximum value for this field is {0}",
33883     /**
33884      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33885      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33886      */
33887     nanText : "{0} is not a valid number",
33888     /**
33889      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33890      */
33891     thousandsDelimiter : false,
33892     /**
33893      * @cfg {String} valueAlign alignment of value
33894      */
33895     valueAlign : "left",
33896
33897     getAutoCreate : function()
33898     {
33899         var hiddenInput = {
33900             tag: 'input',
33901             type: 'hidden',
33902             id: Roo.id(),
33903             cls: 'hidden-number-input'
33904         };
33905         
33906         if (this.name) {
33907             hiddenInput.name = this.name;
33908         }
33909         
33910         this.name = '';
33911         
33912         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33913         
33914         this.name = hiddenInput.name;
33915         
33916         if(cfg.cn.length > 0) {
33917             cfg.cn.push(hiddenInput);
33918         }
33919         
33920         return cfg;
33921     },
33922
33923     // private
33924     initEvents : function()
33925     {   
33926         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33927         
33928         var allowed = "0123456789";
33929         
33930         if(this.allowDecimals){
33931             allowed += this.decimalSeparator;
33932         }
33933         
33934         if(this.allowNegative){
33935             allowed += "-";
33936         }
33937         
33938         if(this.thousandsDelimiter) {
33939             allowed += ",";
33940         }
33941         
33942         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33943         
33944         var keyPress = function(e){
33945             
33946             var k = e.getKey();
33947             
33948             var c = e.getCharCode();
33949             
33950             if(
33951                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33952                     allowed.indexOf(String.fromCharCode(c)) === -1
33953             ){
33954                 e.stopEvent();
33955                 return;
33956             }
33957             
33958             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33959                 return;
33960             }
33961             
33962             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33963                 e.stopEvent();
33964             }
33965         };
33966         
33967         this.el.on("keypress", keyPress, this);
33968     },
33969     
33970     validateValue : function(value)
33971     {
33972         
33973         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33974             return false;
33975         }
33976         
33977         var num = this.parseValue(value);
33978         
33979         if(isNaN(num)){
33980             this.markInvalid(String.format(this.nanText, value));
33981             return false;
33982         }
33983         
33984         if(num < this.minValue){
33985             this.markInvalid(String.format(this.minText, this.minValue));
33986             return false;
33987         }
33988         
33989         if(num > this.maxValue){
33990             this.markInvalid(String.format(this.maxText, this.maxValue));
33991             return false;
33992         }
33993         
33994         return true;
33995     },
33996
33997     getValue : function()
33998     {
33999         var v = this.hiddenEl().getValue();
34000         
34001         return this.fixPrecision(this.parseValue(v));
34002     },
34003
34004     parseValue : function(value)
34005     {
34006         if(this.thousandsDelimiter) {
34007             value += "";
34008             r = new RegExp(",", "g");
34009             value = value.replace(r, "");
34010         }
34011         
34012         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34013         return isNaN(value) ? '' : value;
34014     },
34015
34016     fixPrecision : function(value)
34017     {
34018         if(this.thousandsDelimiter) {
34019             value += "";
34020             r = new RegExp(",", "g");
34021             value = value.replace(r, "");
34022         }
34023         
34024         var nan = isNaN(value);
34025         
34026         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34027             return nan ? '' : value;
34028         }
34029         return parseFloat(value).toFixed(this.decimalPrecision);
34030     },
34031
34032     setValue : function(v)
34033     {
34034         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34035         
34036         this.value = v;
34037         
34038         if(this.rendered){
34039             
34040             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34041             
34042             this.inputEl().dom.value = (v == '') ? '' :
34043                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34044             
34045             if(!this.allowZero && v === '0') {
34046                 this.hiddenEl().dom.value = '';
34047                 this.inputEl().dom.value = '';
34048             }
34049             
34050             this.validate();
34051         }
34052     },
34053
34054     decimalPrecisionFcn : function(v)
34055     {
34056         return Math.floor(v);
34057     },
34058
34059     beforeBlur : function()
34060     {
34061         var v = this.parseValue(this.getRawValue());
34062         
34063         if(v || v === 0 || v === ''){
34064             this.setValue(v);
34065         }
34066     },
34067     
34068     hiddenEl : function()
34069     {
34070         return this.el.select('input.hidden-number-input',true).first();
34071     }
34072     
34073 });
34074
34075  
34076
34077 /*
34078 * Licence: LGPL
34079 */
34080
34081 /**
34082  * @class Roo.bootstrap.DocumentSlider
34083  * @extends Roo.bootstrap.Component
34084  * Bootstrap DocumentSlider class
34085  * 
34086  * @constructor
34087  * Create a new DocumentViewer
34088  * @param {Object} config The config object
34089  */
34090
34091 Roo.bootstrap.DocumentSlider = function(config){
34092     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34093     
34094     this.files = [];
34095     
34096     this.addEvents({
34097         /**
34098          * @event initial
34099          * Fire after initEvent
34100          * @param {Roo.bootstrap.DocumentSlider} this
34101          */
34102         "initial" : true,
34103         /**
34104          * @event update
34105          * Fire after update
34106          * @param {Roo.bootstrap.DocumentSlider} this
34107          */
34108         "update" : true,
34109         /**
34110          * @event click
34111          * Fire after click
34112          * @param {Roo.bootstrap.DocumentSlider} this
34113          */
34114         "click" : true
34115     });
34116 };
34117
34118 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
34119     
34120     files : false,
34121     
34122     indicator : 0,
34123     
34124     getAutoCreate : function()
34125     {
34126         var cfg = {
34127             tag : 'div',
34128             cls : 'roo-document-slider',
34129             cn : [
34130                 {
34131                     tag : 'div',
34132                     cls : 'roo-document-slider-header',
34133                     cn : [
34134                         {
34135                             tag : 'div',
34136                             cls : 'roo-document-slider-header-title'
34137                         }
34138                     ]
34139                 },
34140                 {
34141                     tag : 'div',
34142                     cls : 'roo-document-slider-body',
34143                     cn : [
34144                         {
34145                             tag : 'div',
34146                             cls : 'roo-document-slider-prev',
34147                             cn : [
34148                                 {
34149                                     tag : 'i',
34150                                     cls : 'fa fa-chevron-left'
34151                                 }
34152                             ]
34153                         },
34154                         {
34155                             tag : 'div',
34156                             cls : 'roo-document-slider-thumb',
34157                             cn : [
34158                                 {
34159                                     tag : 'img',
34160                                     cls : 'roo-document-slider-image'
34161                                 }
34162                             ]
34163                         },
34164                         {
34165                             tag : 'div',
34166                             cls : 'roo-document-slider-next',
34167                             cn : [
34168                                 {
34169                                     tag : 'i',
34170                                     cls : 'fa fa-chevron-right'
34171                                 }
34172                             ]
34173                         }
34174                     ]
34175                 }
34176             ]
34177         };
34178         
34179         return cfg;
34180     },
34181     
34182     initEvents : function()
34183     {
34184         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34185         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34186         
34187         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34188         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34189         
34190         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34191         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34192         
34193         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34194         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34195         
34196         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34197         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34198         
34199         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34200         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34201         
34202         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34203         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34204         
34205         this.thumbEl.on('click', this.onClick, this);
34206         
34207         this.prevIndicator.on('click', this.prev, this);
34208         
34209         this.nextIndicator.on('click', this.next, this);
34210         
34211     },
34212     
34213     initial : function()
34214     {
34215         if(this.files.length){
34216             this.indicator = 1;
34217             this.update()
34218         }
34219         
34220         this.fireEvent('initial', this);
34221     },
34222     
34223     update : function()
34224     {
34225         this.imageEl.attr('src', this.files[this.indicator - 1]);
34226         
34227         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34228         
34229         this.prevIndicator.show();
34230         
34231         if(this.indicator == 1){
34232             this.prevIndicator.hide();
34233         }
34234         
34235         this.nextIndicator.show();
34236         
34237         if(this.indicator == this.files.length){
34238             this.nextIndicator.hide();
34239         }
34240         
34241         this.thumbEl.scrollTo('top');
34242         
34243         this.fireEvent('update', this);
34244     },
34245     
34246     onClick : function(e)
34247     {
34248         e.preventDefault();
34249         
34250         this.fireEvent('click', this);
34251     },
34252     
34253     prev : function(e)
34254     {
34255         e.preventDefault();
34256         
34257         this.indicator = Math.max(1, this.indicator - 1);
34258         
34259         this.update();
34260     },
34261     
34262     next : function(e)
34263     {
34264         e.preventDefault();
34265         
34266         this.indicator = Math.min(this.files.length, this.indicator + 1);
34267         
34268         this.update();
34269     }
34270 });
34271 /*
34272  * - LGPL
34273  *
34274  * RadioSet
34275  *
34276  *
34277  */
34278
34279 /**
34280  * @class Roo.bootstrap.RadioSet
34281  * @extends Roo.bootstrap.Input
34282  * Bootstrap RadioSet class
34283  * @cfg {String} indicatorpos (left|right) default left
34284  * @cfg {Boolean} inline (true|false) inline the element (default true)
34285  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34286  * @constructor
34287  * Create a new RadioSet
34288  * @param {Object} config The config object
34289  */
34290
34291 Roo.bootstrap.RadioSet = function(config){
34292     
34293     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34294     
34295     this.radioes = [];
34296     
34297     Roo.bootstrap.RadioSet.register(this);
34298     
34299     this.addEvents({
34300         /**
34301         * @event check
34302         * Fires when the element is checked or unchecked.
34303         * @param {Roo.bootstrap.RadioSet} this This radio
34304         * @param {Roo.bootstrap.Radio} item The checked item
34305         */
34306        check : true,
34307        /**
34308         * @event click
34309         * Fires when the element is click.
34310         * @param {Roo.bootstrap.RadioSet} this This radio set
34311         * @param {Roo.bootstrap.Radio} item The checked item
34312         * @param {Roo.EventObject} e The event object
34313         */
34314        click : true
34315     });
34316     
34317 };
34318
34319 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34320
34321     radioes : false,
34322     
34323     inline : true,
34324     
34325     weight : '',
34326     
34327     indicatorpos : 'left',
34328     
34329     getAutoCreate : function()
34330     {
34331         var label = {
34332             tag : 'label',
34333             cls : 'roo-radio-set-label',
34334             cn : [
34335                 {
34336                     tag : 'span',
34337                     html : this.fieldLabel
34338                 }
34339             ]
34340         };
34341         if (Roo.bootstrap.version == 3) {
34342             
34343             
34344             if(this.indicatorpos == 'left'){
34345                 label.cn.unshift({
34346                     tag : 'i',
34347                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34348                     tooltip : 'This field is required'
34349                 });
34350             } else {
34351                 label.cn.push({
34352                     tag : 'i',
34353                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34354                     tooltip : 'This field is required'
34355                 });
34356             }
34357         }
34358         var items = {
34359             tag : 'div',
34360             cls : 'roo-radio-set-items'
34361         };
34362         
34363         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34364         
34365         if (align === 'left' && this.fieldLabel.length) {
34366             
34367             items = {
34368                 cls : "roo-radio-set-right", 
34369                 cn: [
34370                     items
34371                 ]
34372             };
34373             
34374             if(this.labelWidth > 12){
34375                 label.style = "width: " + this.labelWidth + 'px';
34376             }
34377             
34378             if(this.labelWidth < 13 && this.labelmd == 0){
34379                 this.labelmd = this.labelWidth;
34380             }
34381             
34382             if(this.labellg > 0){
34383                 label.cls += ' col-lg-' + this.labellg;
34384                 items.cls += ' col-lg-' + (12 - this.labellg);
34385             }
34386             
34387             if(this.labelmd > 0){
34388                 label.cls += ' col-md-' + this.labelmd;
34389                 items.cls += ' col-md-' + (12 - this.labelmd);
34390             }
34391             
34392             if(this.labelsm > 0){
34393                 label.cls += ' col-sm-' + this.labelsm;
34394                 items.cls += ' col-sm-' + (12 - this.labelsm);
34395             }
34396             
34397             if(this.labelxs > 0){
34398                 label.cls += ' col-xs-' + this.labelxs;
34399                 items.cls += ' col-xs-' + (12 - this.labelxs);
34400             }
34401         }
34402         
34403         var cfg = {
34404             tag : 'div',
34405             cls : 'roo-radio-set',
34406             cn : [
34407                 {
34408                     tag : 'input',
34409                     cls : 'roo-radio-set-input',
34410                     type : 'hidden',
34411                     name : this.name,
34412                     value : this.value ? this.value :  ''
34413                 },
34414                 label,
34415                 items
34416             ]
34417         };
34418         
34419         if(this.weight.length){
34420             cfg.cls += ' roo-radio-' + this.weight;
34421         }
34422         
34423         if(this.inline) {
34424             cfg.cls += ' roo-radio-set-inline';
34425         }
34426         
34427         var settings=this;
34428         ['xs','sm','md','lg'].map(function(size){
34429             if (settings[size]) {
34430                 cfg.cls += ' col-' + size + '-' + settings[size];
34431             }
34432         });
34433         
34434         return cfg;
34435         
34436     },
34437
34438     initEvents : function()
34439     {
34440         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34441         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34442         
34443         if(!this.fieldLabel.length){
34444             this.labelEl.hide();
34445         }
34446         
34447         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34448         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34449         
34450         this.indicator = this.indicatorEl();
34451         
34452         if(this.indicator){
34453             this.indicator.addClass('invisible');
34454         }
34455         
34456         this.originalValue = this.getValue();
34457         
34458     },
34459     
34460     inputEl: function ()
34461     {
34462         return this.el.select('.roo-radio-set-input', true).first();
34463     },
34464     
34465     getChildContainer : function()
34466     {
34467         return this.itemsEl;
34468     },
34469     
34470     register : function(item)
34471     {
34472         this.radioes.push(item);
34473         
34474     },
34475     
34476     validate : function()
34477     {   
34478         if(this.getVisibilityEl().hasClass('hidden')){
34479             return true;
34480         }
34481         
34482         var valid = false;
34483         
34484         Roo.each(this.radioes, function(i){
34485             if(!i.checked){
34486                 return;
34487             }
34488             
34489             valid = true;
34490             return false;
34491         });
34492         
34493         if(this.allowBlank) {
34494             return true;
34495         }
34496         
34497         if(this.disabled || valid){
34498             this.markValid();
34499             return true;
34500         }
34501         
34502         this.markInvalid();
34503         return false;
34504         
34505     },
34506     
34507     markValid : function()
34508     {
34509         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34510             this.indicatorEl().removeClass('visible');
34511             this.indicatorEl().addClass('invisible');
34512         }
34513         
34514         
34515         if (Roo.bootstrap.version == 3) {
34516             this.el.removeClass([this.invalidClass, this.validClass]);
34517             this.el.addClass(this.validClass);
34518         } else {
34519             this.el.removeClass(['is-invalid','is-valid']);
34520             this.el.addClass(['is-valid']);
34521         }
34522         this.fireEvent('valid', this);
34523     },
34524     
34525     markInvalid : function(msg)
34526     {
34527         if(this.allowBlank || this.disabled){
34528             return;
34529         }
34530         
34531         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34532             this.indicatorEl().removeClass('invisible');
34533             this.indicatorEl().addClass('visible');
34534         }
34535         if (Roo.bootstrap.version == 3) {
34536             this.el.removeClass([this.invalidClass, this.validClass]);
34537             this.el.addClass(this.invalidClass);
34538         } else {
34539             this.el.removeClass(['is-invalid','is-valid']);
34540             this.el.addClass(['is-invalid']);
34541         }
34542         
34543         this.fireEvent('invalid', this, msg);
34544         
34545     },
34546     
34547     setValue : function(v, suppressEvent)
34548     {   
34549         if(this.value === v){
34550             return;
34551         }
34552         
34553         this.value = v;
34554         
34555         if(this.rendered){
34556             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34557         }
34558         
34559         Roo.each(this.radioes, function(i){
34560             i.checked = false;
34561             i.el.removeClass('checked');
34562         });
34563         
34564         Roo.each(this.radioes, function(i){
34565             
34566             if(i.value === v || i.value.toString() === v.toString()){
34567                 i.checked = true;
34568                 i.el.addClass('checked');
34569                 
34570                 if(suppressEvent !== true){
34571                     this.fireEvent('check', this, i);
34572                 }
34573                 
34574                 return false;
34575             }
34576             
34577         }, this);
34578         
34579         this.validate();
34580     },
34581     
34582     clearInvalid : function(){
34583         
34584         if(!this.el || this.preventMark){
34585             return;
34586         }
34587         
34588         this.el.removeClass([this.invalidClass]);
34589         
34590         this.fireEvent('valid', this);
34591     }
34592     
34593 });
34594
34595 Roo.apply(Roo.bootstrap.RadioSet, {
34596     
34597     groups: {},
34598     
34599     register : function(set)
34600     {
34601         this.groups[set.name] = set;
34602     },
34603     
34604     get: function(name) 
34605     {
34606         if (typeof(this.groups[name]) == 'undefined') {
34607             return false;
34608         }
34609         
34610         return this.groups[name] ;
34611     }
34612     
34613 });
34614 /*
34615  * Based on:
34616  * Ext JS Library 1.1.1
34617  * Copyright(c) 2006-2007, Ext JS, LLC.
34618  *
34619  * Originally Released Under LGPL - original licence link has changed is not relivant.
34620  *
34621  * Fork - LGPL
34622  * <script type="text/javascript">
34623  */
34624
34625
34626 /**
34627  * @class Roo.bootstrap.SplitBar
34628  * @extends Roo.util.Observable
34629  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34630  * <br><br>
34631  * Usage:
34632  * <pre><code>
34633 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34634                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34635 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34636 split.minSize = 100;
34637 split.maxSize = 600;
34638 split.animate = true;
34639 split.on('moved', splitterMoved);
34640 </code></pre>
34641  * @constructor
34642  * Create a new SplitBar
34643  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34644  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34645  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34646  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34647                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34648                         position of the SplitBar).
34649  */
34650 Roo.bootstrap.SplitBar = function(cfg){
34651     
34652     /** @private */
34653     
34654     //{
34655     //  dragElement : elm
34656     //  resizingElement: el,
34657         // optional..
34658     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34659     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34660         // existingProxy ???
34661     //}
34662     
34663     this.el = Roo.get(cfg.dragElement, true);
34664     this.el.dom.unselectable = "on";
34665     /** @private */
34666     this.resizingEl = Roo.get(cfg.resizingElement, true);
34667
34668     /**
34669      * @private
34670      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34671      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34672      * @type Number
34673      */
34674     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34675     
34676     /**
34677      * The minimum size of the resizing element. (Defaults to 0)
34678      * @type Number
34679      */
34680     this.minSize = 0;
34681     
34682     /**
34683      * The maximum size of the resizing element. (Defaults to 2000)
34684      * @type Number
34685      */
34686     this.maxSize = 2000;
34687     
34688     /**
34689      * Whether to animate the transition to the new size
34690      * @type Boolean
34691      */
34692     this.animate = false;
34693     
34694     /**
34695      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34696      * @type Boolean
34697      */
34698     this.useShim = false;
34699     
34700     /** @private */
34701     this.shim = null;
34702     
34703     if(!cfg.existingProxy){
34704         /** @private */
34705         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34706     }else{
34707         this.proxy = Roo.get(cfg.existingProxy).dom;
34708     }
34709     /** @private */
34710     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34711     
34712     /** @private */
34713     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34714     
34715     /** @private */
34716     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34717     
34718     /** @private */
34719     this.dragSpecs = {};
34720     
34721     /**
34722      * @private The adapter to use to positon and resize elements
34723      */
34724     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34725     this.adapter.init(this);
34726     
34727     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34728         /** @private */
34729         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34730         this.el.addClass("roo-splitbar-h");
34731     }else{
34732         /** @private */
34733         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34734         this.el.addClass("roo-splitbar-v");
34735     }
34736     
34737     this.addEvents({
34738         /**
34739          * @event resize
34740          * Fires when the splitter is moved (alias for {@link #event-moved})
34741          * @param {Roo.bootstrap.SplitBar} this
34742          * @param {Number} newSize the new width or height
34743          */
34744         "resize" : true,
34745         /**
34746          * @event moved
34747          * Fires when the splitter is moved
34748          * @param {Roo.bootstrap.SplitBar} this
34749          * @param {Number} newSize the new width or height
34750          */
34751         "moved" : true,
34752         /**
34753          * @event beforeresize
34754          * Fires before the splitter is dragged
34755          * @param {Roo.bootstrap.SplitBar} this
34756          */
34757         "beforeresize" : true,
34758
34759         "beforeapply" : true
34760     });
34761
34762     Roo.util.Observable.call(this);
34763 };
34764
34765 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34766     onStartProxyDrag : function(x, y){
34767         this.fireEvent("beforeresize", this);
34768         if(!this.overlay){
34769             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34770             o.unselectable();
34771             o.enableDisplayMode("block");
34772             // all splitbars share the same overlay
34773             Roo.bootstrap.SplitBar.prototype.overlay = o;
34774         }
34775         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34776         this.overlay.show();
34777         Roo.get(this.proxy).setDisplayed("block");
34778         var size = this.adapter.getElementSize(this);
34779         this.activeMinSize = this.getMinimumSize();;
34780         this.activeMaxSize = this.getMaximumSize();;
34781         var c1 = size - this.activeMinSize;
34782         var c2 = Math.max(this.activeMaxSize - size, 0);
34783         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34784             this.dd.resetConstraints();
34785             this.dd.setXConstraint(
34786                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34787                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34788             );
34789             this.dd.setYConstraint(0, 0);
34790         }else{
34791             this.dd.resetConstraints();
34792             this.dd.setXConstraint(0, 0);
34793             this.dd.setYConstraint(
34794                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34795                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34796             );
34797          }
34798         this.dragSpecs.startSize = size;
34799         this.dragSpecs.startPoint = [x, y];
34800         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34801     },
34802     
34803     /** 
34804      * @private Called after the drag operation by the DDProxy
34805      */
34806     onEndProxyDrag : function(e){
34807         Roo.get(this.proxy).setDisplayed(false);
34808         var endPoint = Roo.lib.Event.getXY(e);
34809         if(this.overlay){
34810             this.overlay.hide();
34811         }
34812         var newSize;
34813         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34814             newSize = this.dragSpecs.startSize + 
34815                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34816                     endPoint[0] - this.dragSpecs.startPoint[0] :
34817                     this.dragSpecs.startPoint[0] - endPoint[0]
34818                 );
34819         }else{
34820             newSize = this.dragSpecs.startSize + 
34821                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34822                     endPoint[1] - this.dragSpecs.startPoint[1] :
34823                     this.dragSpecs.startPoint[1] - endPoint[1]
34824                 );
34825         }
34826         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34827         if(newSize != this.dragSpecs.startSize){
34828             if(this.fireEvent('beforeapply', this, newSize) !== false){
34829                 this.adapter.setElementSize(this, newSize);
34830                 this.fireEvent("moved", this, newSize);
34831                 this.fireEvent("resize", this, newSize);
34832             }
34833         }
34834     },
34835     
34836     /**
34837      * Get the adapter this SplitBar uses
34838      * @return The adapter object
34839      */
34840     getAdapter : function(){
34841         return this.adapter;
34842     },
34843     
34844     /**
34845      * Set the adapter this SplitBar uses
34846      * @param {Object} adapter A SplitBar adapter object
34847      */
34848     setAdapter : function(adapter){
34849         this.adapter = adapter;
34850         this.adapter.init(this);
34851     },
34852     
34853     /**
34854      * Gets the minimum size for the resizing element
34855      * @return {Number} The minimum size
34856      */
34857     getMinimumSize : function(){
34858         return this.minSize;
34859     },
34860     
34861     /**
34862      * Sets the minimum size for the resizing element
34863      * @param {Number} minSize The minimum size
34864      */
34865     setMinimumSize : function(minSize){
34866         this.minSize = minSize;
34867     },
34868     
34869     /**
34870      * Gets the maximum size for the resizing element
34871      * @return {Number} The maximum size
34872      */
34873     getMaximumSize : function(){
34874         return this.maxSize;
34875     },
34876     
34877     /**
34878      * Sets the maximum size for the resizing element
34879      * @param {Number} maxSize The maximum size
34880      */
34881     setMaximumSize : function(maxSize){
34882         this.maxSize = maxSize;
34883     },
34884     
34885     /**
34886      * Sets the initialize size for the resizing element
34887      * @param {Number} size The initial size
34888      */
34889     setCurrentSize : function(size){
34890         var oldAnimate = this.animate;
34891         this.animate = false;
34892         this.adapter.setElementSize(this, size);
34893         this.animate = oldAnimate;
34894     },
34895     
34896     /**
34897      * Destroy this splitbar. 
34898      * @param {Boolean} removeEl True to remove the element
34899      */
34900     destroy : function(removeEl){
34901         if(this.shim){
34902             this.shim.remove();
34903         }
34904         this.dd.unreg();
34905         this.proxy.parentNode.removeChild(this.proxy);
34906         if(removeEl){
34907             this.el.remove();
34908         }
34909     }
34910 });
34911
34912 /**
34913  * @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.
34914  */
34915 Roo.bootstrap.SplitBar.createProxy = function(dir){
34916     var proxy = new Roo.Element(document.createElement("div"));
34917     proxy.unselectable();
34918     var cls = 'roo-splitbar-proxy';
34919     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34920     document.body.appendChild(proxy.dom);
34921     return proxy.dom;
34922 };
34923
34924 /** 
34925  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34926  * Default Adapter. It assumes the splitter and resizing element are not positioned
34927  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34928  */
34929 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34930 };
34931
34932 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34933     // do nothing for now
34934     init : function(s){
34935     
34936     },
34937     /**
34938      * Called before drag operations to get the current size of the resizing element. 
34939      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34940      */
34941      getElementSize : function(s){
34942         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34943             return s.resizingEl.getWidth();
34944         }else{
34945             return s.resizingEl.getHeight();
34946         }
34947     },
34948     
34949     /**
34950      * Called after drag operations to set the size of the resizing element.
34951      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34952      * @param {Number} newSize The new size to set
34953      * @param {Function} onComplete A function to be invoked when resizing is complete
34954      */
34955     setElementSize : function(s, newSize, onComplete){
34956         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34957             if(!s.animate){
34958                 s.resizingEl.setWidth(newSize);
34959                 if(onComplete){
34960                     onComplete(s, newSize);
34961                 }
34962             }else{
34963                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34964             }
34965         }else{
34966             
34967             if(!s.animate){
34968                 s.resizingEl.setHeight(newSize);
34969                 if(onComplete){
34970                     onComplete(s, newSize);
34971                 }
34972             }else{
34973                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34974             }
34975         }
34976     }
34977 };
34978
34979 /** 
34980  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34981  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34982  * Adapter that  moves the splitter element to align with the resized sizing element. 
34983  * Used with an absolute positioned SplitBar.
34984  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34985  * document.body, make sure you assign an id to the body element.
34986  */
34987 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34988     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34989     this.container = Roo.get(container);
34990 };
34991
34992 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34993     init : function(s){
34994         this.basic.init(s);
34995     },
34996     
34997     getElementSize : function(s){
34998         return this.basic.getElementSize(s);
34999     },
35000     
35001     setElementSize : function(s, newSize, onComplete){
35002         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35003     },
35004     
35005     moveSplitter : function(s){
35006         var yes = Roo.bootstrap.SplitBar;
35007         switch(s.placement){
35008             case yes.LEFT:
35009                 s.el.setX(s.resizingEl.getRight());
35010                 break;
35011             case yes.RIGHT:
35012                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35013                 break;
35014             case yes.TOP:
35015                 s.el.setY(s.resizingEl.getBottom());
35016                 break;
35017             case yes.BOTTOM:
35018                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35019                 break;
35020         }
35021     }
35022 };
35023
35024 /**
35025  * Orientation constant - Create a vertical SplitBar
35026  * @static
35027  * @type Number
35028  */
35029 Roo.bootstrap.SplitBar.VERTICAL = 1;
35030
35031 /**
35032  * Orientation constant - Create a horizontal SplitBar
35033  * @static
35034  * @type Number
35035  */
35036 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35037
35038 /**
35039  * Placement constant - The resizing element is to the left of the splitter element
35040  * @static
35041  * @type Number
35042  */
35043 Roo.bootstrap.SplitBar.LEFT = 1;
35044
35045 /**
35046  * Placement constant - The resizing element is to the right of the splitter element
35047  * @static
35048  * @type Number
35049  */
35050 Roo.bootstrap.SplitBar.RIGHT = 2;
35051
35052 /**
35053  * Placement constant - The resizing element is positioned above the splitter element
35054  * @static
35055  * @type Number
35056  */
35057 Roo.bootstrap.SplitBar.TOP = 3;
35058
35059 /**
35060  * Placement constant - The resizing element is positioned under splitter element
35061  * @static
35062  * @type Number
35063  */
35064 Roo.bootstrap.SplitBar.BOTTOM = 4;
35065 Roo.namespace("Roo.bootstrap.layout");/*
35066  * Based on:
35067  * Ext JS Library 1.1.1
35068  * Copyright(c) 2006-2007, Ext JS, LLC.
35069  *
35070  * Originally Released Under LGPL - original licence link has changed is not relivant.
35071  *
35072  * Fork - LGPL
35073  * <script type="text/javascript">
35074  */
35075
35076 /**
35077  * @class Roo.bootstrap.layout.Manager
35078  * @extends Roo.bootstrap.Component
35079  * Base class for layout managers.
35080  */
35081 Roo.bootstrap.layout.Manager = function(config)
35082 {
35083     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35084
35085
35086
35087
35088
35089     /** false to disable window resize monitoring @type Boolean */
35090     this.monitorWindowResize = true;
35091     this.regions = {};
35092     this.addEvents({
35093         /**
35094          * @event layout
35095          * Fires when a layout is performed.
35096          * @param {Roo.LayoutManager} this
35097          */
35098         "layout" : true,
35099         /**
35100          * @event regionresized
35101          * Fires when the user resizes a region.
35102          * @param {Roo.LayoutRegion} region The resized region
35103          * @param {Number} newSize The new size (width for east/west, height for north/south)
35104          */
35105         "regionresized" : true,
35106         /**
35107          * @event regioncollapsed
35108          * Fires when a region is collapsed.
35109          * @param {Roo.LayoutRegion} region The collapsed region
35110          */
35111         "regioncollapsed" : true,
35112         /**
35113          * @event regionexpanded
35114          * Fires when a region is expanded.
35115          * @param {Roo.LayoutRegion} region The expanded region
35116          */
35117         "regionexpanded" : true
35118     });
35119     this.updating = false;
35120
35121     if (config.el) {
35122         this.el = Roo.get(config.el);
35123         this.initEvents();
35124     }
35125
35126 };
35127
35128 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35129
35130
35131     regions : null,
35132
35133     monitorWindowResize : true,
35134
35135
35136     updating : false,
35137
35138
35139     onRender : function(ct, position)
35140     {
35141         if(!this.el){
35142             this.el = Roo.get(ct);
35143             this.initEvents();
35144         }
35145         //this.fireEvent('render',this);
35146     },
35147
35148
35149     initEvents: function()
35150     {
35151
35152
35153         // ie scrollbar fix
35154         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35155             document.body.scroll = "no";
35156         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35157             this.el.position('relative');
35158         }
35159         this.id = this.el.id;
35160         this.el.addClass("roo-layout-container");
35161         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35162         if(this.el.dom != document.body ) {
35163             this.el.on('resize', this.layout,this);
35164             this.el.on('show', this.layout,this);
35165         }
35166
35167     },
35168
35169     /**
35170      * Returns true if this layout is currently being updated
35171      * @return {Boolean}
35172      */
35173     isUpdating : function(){
35174         return this.updating;
35175     },
35176
35177     /**
35178      * Suspend the LayoutManager from doing auto-layouts while
35179      * making multiple add or remove calls
35180      */
35181     beginUpdate : function(){
35182         this.updating = true;
35183     },
35184
35185     /**
35186      * Restore auto-layouts and optionally disable the manager from performing a layout
35187      * @param {Boolean} noLayout true to disable a layout update
35188      */
35189     endUpdate : function(noLayout){
35190         this.updating = false;
35191         if(!noLayout){
35192             this.layout();
35193         }
35194     },
35195
35196     layout: function(){
35197         // abstract...
35198     },
35199
35200     onRegionResized : function(region, newSize){
35201         this.fireEvent("regionresized", region, newSize);
35202         this.layout();
35203     },
35204
35205     onRegionCollapsed : function(region){
35206         this.fireEvent("regioncollapsed", region);
35207     },
35208
35209     onRegionExpanded : function(region){
35210         this.fireEvent("regionexpanded", region);
35211     },
35212
35213     /**
35214      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35215      * performs box-model adjustments.
35216      * @return {Object} The size as an object {width: (the width), height: (the height)}
35217      */
35218     getViewSize : function()
35219     {
35220         var size;
35221         if(this.el.dom != document.body){
35222             size = this.el.getSize();
35223         }else{
35224             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35225         }
35226         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35227         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35228         return size;
35229     },
35230
35231     /**
35232      * Returns the Element this layout is bound to.
35233      * @return {Roo.Element}
35234      */
35235     getEl : function(){
35236         return this.el;
35237     },
35238
35239     /**
35240      * Returns the specified region.
35241      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35242      * @return {Roo.LayoutRegion}
35243      */
35244     getRegion : function(target){
35245         return this.regions[target.toLowerCase()];
35246     },
35247
35248     onWindowResize : function(){
35249         if(this.monitorWindowResize){
35250             this.layout();
35251         }
35252     }
35253 });
35254 /*
35255  * Based on:
35256  * Ext JS Library 1.1.1
35257  * Copyright(c) 2006-2007, Ext JS, LLC.
35258  *
35259  * Originally Released Under LGPL - original licence link has changed is not relivant.
35260  *
35261  * Fork - LGPL
35262  * <script type="text/javascript">
35263  */
35264 /**
35265  * @class Roo.bootstrap.layout.Border
35266  * @extends Roo.bootstrap.layout.Manager
35267  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35268  * please see: examples/bootstrap/nested.html<br><br>
35269  
35270 <b>The container the layout is rendered into can be either the body element or any other element.
35271 If it is not the body element, the container needs to either be an absolute positioned element,
35272 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35273 the container size if it is not the body element.</b>
35274
35275 * @constructor
35276 * Create a new Border
35277 * @param {Object} config Configuration options
35278  */
35279 Roo.bootstrap.layout.Border = function(config){
35280     config = config || {};
35281     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35282     
35283     
35284     
35285     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35286         if(config[region]){
35287             config[region].region = region;
35288             this.addRegion(config[region]);
35289         }
35290     },this);
35291     
35292 };
35293
35294 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35295
35296 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35297     
35298     parent : false, // this might point to a 'nest' or a ???
35299     
35300     /**
35301      * Creates and adds a new region if it doesn't already exist.
35302      * @param {String} target The target region key (north, south, east, west or center).
35303      * @param {Object} config The regions config object
35304      * @return {BorderLayoutRegion} The new region
35305      */
35306     addRegion : function(config)
35307     {
35308         if(!this.regions[config.region]){
35309             var r = this.factory(config);
35310             this.bindRegion(r);
35311         }
35312         return this.regions[config.region];
35313     },
35314
35315     // private (kinda)
35316     bindRegion : function(r){
35317         this.regions[r.config.region] = r;
35318         
35319         r.on("visibilitychange",    this.layout, this);
35320         r.on("paneladded",          this.layout, this);
35321         r.on("panelremoved",        this.layout, this);
35322         r.on("invalidated",         this.layout, this);
35323         r.on("resized",             this.onRegionResized, this);
35324         r.on("collapsed",           this.onRegionCollapsed, this);
35325         r.on("expanded",            this.onRegionExpanded, this);
35326     },
35327
35328     /**
35329      * Performs a layout update.
35330      */
35331     layout : function()
35332     {
35333         if(this.updating) {
35334             return;
35335         }
35336         
35337         // render all the rebions if they have not been done alreayd?
35338         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35339             if(this.regions[region] && !this.regions[region].bodyEl){
35340                 this.regions[region].onRender(this.el)
35341             }
35342         },this);
35343         
35344         var size = this.getViewSize();
35345         var w = size.width;
35346         var h = size.height;
35347         var centerW = w;
35348         var centerH = h;
35349         var centerY = 0;
35350         var centerX = 0;
35351         //var x = 0, y = 0;
35352
35353         var rs = this.regions;
35354         var north = rs["north"];
35355         var south = rs["south"]; 
35356         var west = rs["west"];
35357         var east = rs["east"];
35358         var center = rs["center"];
35359         //if(this.hideOnLayout){ // not supported anymore
35360             //c.el.setStyle("display", "none");
35361         //}
35362         if(north && north.isVisible()){
35363             var b = north.getBox();
35364             var m = north.getMargins();
35365             b.width = w - (m.left+m.right);
35366             b.x = m.left;
35367             b.y = m.top;
35368             centerY = b.height + b.y + m.bottom;
35369             centerH -= centerY;
35370             north.updateBox(this.safeBox(b));
35371         }
35372         if(south && south.isVisible()){
35373             var b = south.getBox();
35374             var m = south.getMargins();
35375             b.width = w - (m.left+m.right);
35376             b.x = m.left;
35377             var totalHeight = (b.height + m.top + m.bottom);
35378             b.y = h - totalHeight + m.top;
35379             centerH -= totalHeight;
35380             south.updateBox(this.safeBox(b));
35381         }
35382         if(west && west.isVisible()){
35383             var b = west.getBox();
35384             var m = west.getMargins();
35385             b.height = centerH - (m.top+m.bottom);
35386             b.x = m.left;
35387             b.y = centerY + m.top;
35388             var totalWidth = (b.width + m.left + m.right);
35389             centerX += totalWidth;
35390             centerW -= totalWidth;
35391             west.updateBox(this.safeBox(b));
35392         }
35393         if(east && east.isVisible()){
35394             var b = east.getBox();
35395             var m = east.getMargins();
35396             b.height = centerH - (m.top+m.bottom);
35397             var totalWidth = (b.width + m.left + m.right);
35398             b.x = w - totalWidth + m.left;
35399             b.y = centerY + m.top;
35400             centerW -= totalWidth;
35401             east.updateBox(this.safeBox(b));
35402         }
35403         if(center){
35404             var m = center.getMargins();
35405             var centerBox = {
35406                 x: centerX + m.left,
35407                 y: centerY + m.top,
35408                 width: centerW - (m.left+m.right),
35409                 height: centerH - (m.top+m.bottom)
35410             };
35411             //if(this.hideOnLayout){
35412                 //center.el.setStyle("display", "block");
35413             //}
35414             center.updateBox(this.safeBox(centerBox));
35415         }
35416         this.el.repaint();
35417         this.fireEvent("layout", this);
35418     },
35419
35420     // private
35421     safeBox : function(box){
35422         box.width = Math.max(0, box.width);
35423         box.height = Math.max(0, box.height);
35424         return box;
35425     },
35426
35427     /**
35428      * Adds a ContentPanel (or subclass) to this layout.
35429      * @param {String} target The target region key (north, south, east, west or center).
35430      * @param {Roo.ContentPanel} panel The panel to add
35431      * @return {Roo.ContentPanel} The added panel
35432      */
35433     add : function(target, panel){
35434          
35435         target = target.toLowerCase();
35436         return this.regions[target].add(panel);
35437     },
35438
35439     /**
35440      * Remove a ContentPanel (or subclass) to this layout.
35441      * @param {String} target The target region key (north, south, east, west or center).
35442      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35443      * @return {Roo.ContentPanel} The removed panel
35444      */
35445     remove : function(target, panel){
35446         target = target.toLowerCase();
35447         return this.regions[target].remove(panel);
35448     },
35449
35450     /**
35451      * Searches all regions for a panel with the specified id
35452      * @param {String} panelId
35453      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35454      */
35455     findPanel : function(panelId){
35456         var rs = this.regions;
35457         for(var target in rs){
35458             if(typeof rs[target] != "function"){
35459                 var p = rs[target].getPanel(panelId);
35460                 if(p){
35461                     return p;
35462                 }
35463             }
35464         }
35465         return null;
35466     },
35467
35468     /**
35469      * Searches all regions for a panel with the specified id and activates (shows) it.
35470      * @param {String/ContentPanel} panelId The panels id or the panel itself
35471      * @return {Roo.ContentPanel} The shown panel or null
35472      */
35473     showPanel : function(panelId) {
35474       var rs = this.regions;
35475       for(var target in rs){
35476          var r = rs[target];
35477          if(typeof r != "function"){
35478             if(r.hasPanel(panelId)){
35479                return r.showPanel(panelId);
35480             }
35481          }
35482       }
35483       return null;
35484    },
35485
35486    /**
35487      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35488      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35489      */
35490    /*
35491     restoreState : function(provider){
35492         if(!provider){
35493             provider = Roo.state.Manager;
35494         }
35495         var sm = new Roo.LayoutStateManager();
35496         sm.init(this, provider);
35497     },
35498 */
35499  
35500  
35501     /**
35502      * Adds a xtype elements to the layout.
35503      * <pre><code>
35504
35505 layout.addxtype({
35506        xtype : 'ContentPanel',
35507        region: 'west',
35508        items: [ .... ]
35509    }
35510 );
35511
35512 layout.addxtype({
35513         xtype : 'NestedLayoutPanel',
35514         region: 'west',
35515         layout: {
35516            center: { },
35517            west: { }   
35518         },
35519         items : [ ... list of content panels or nested layout panels.. ]
35520    }
35521 );
35522 </code></pre>
35523      * @param {Object} cfg Xtype definition of item to add.
35524      */
35525     addxtype : function(cfg)
35526     {
35527         // basically accepts a pannel...
35528         // can accept a layout region..!?!?
35529         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35530         
35531         
35532         // theory?  children can only be panels??
35533         
35534         //if (!cfg.xtype.match(/Panel$/)) {
35535         //    return false;
35536         //}
35537         var ret = false;
35538         
35539         if (typeof(cfg.region) == 'undefined') {
35540             Roo.log("Failed to add Panel, region was not set");
35541             Roo.log(cfg);
35542             return false;
35543         }
35544         var region = cfg.region;
35545         delete cfg.region;
35546         
35547           
35548         var xitems = [];
35549         if (cfg.items) {
35550             xitems = cfg.items;
35551             delete cfg.items;
35552         }
35553         var nb = false;
35554         
35555         if ( region == 'center') {
35556             Roo.log("Center: " + cfg.title);
35557         }
35558         
35559         
35560         switch(cfg.xtype) 
35561         {
35562             case 'Content':  // ContentPanel (el, cfg)
35563             case 'Scroll':  // ContentPanel (el, cfg)
35564             case 'View': 
35565                 cfg.autoCreate = cfg.autoCreate || true;
35566                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35567                 //} else {
35568                 //    var el = this.el.createChild();
35569                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35570                 //}
35571                 
35572                 this.add(region, ret);
35573                 break;
35574             
35575             /*
35576             case 'TreePanel': // our new panel!
35577                 cfg.el = this.el.createChild();
35578                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35579                 this.add(region, ret);
35580                 break;
35581             */
35582             
35583             case 'Nest': 
35584                 // create a new Layout (which is  a Border Layout...
35585                 
35586                 var clayout = cfg.layout;
35587                 clayout.el  = this.el.createChild();
35588                 clayout.items   = clayout.items  || [];
35589                 
35590                 delete cfg.layout;
35591                 
35592                 // replace this exitems with the clayout ones..
35593                 xitems = clayout.items;
35594                  
35595                 // force background off if it's in center...
35596                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35597                     cfg.background = false;
35598                 }
35599                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35600                 
35601                 
35602                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35603                 //console.log('adding nested layout panel '  + cfg.toSource());
35604                 this.add(region, ret);
35605                 nb = {}; /// find first...
35606                 break;
35607             
35608             case 'Grid':
35609                 
35610                 // needs grid and region
35611                 
35612                 //var el = this.getRegion(region).el.createChild();
35613                 /*
35614                  *var el = this.el.createChild();
35615                 // create the grid first...
35616                 cfg.grid.container = el;
35617                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35618                 */
35619                 
35620                 if (region == 'center' && this.active ) {
35621                     cfg.background = false;
35622                 }
35623                 
35624                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35625                 
35626                 this.add(region, ret);
35627                 /*
35628                 if (cfg.background) {
35629                     // render grid on panel activation (if panel background)
35630                     ret.on('activate', function(gp) {
35631                         if (!gp.grid.rendered) {
35632                     //        gp.grid.render(el);
35633                         }
35634                     });
35635                 } else {
35636                   //  cfg.grid.render(el);
35637                 }
35638                 */
35639                 break;
35640            
35641            
35642             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35643                 // it was the old xcomponent building that caused this before.
35644                 // espeically if border is the top element in the tree.
35645                 ret = this;
35646                 break; 
35647                 
35648                     
35649                 
35650                 
35651                 
35652             default:
35653                 /*
35654                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35655                     
35656                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35657                     this.add(region, ret);
35658                 } else {
35659                 */
35660                     Roo.log(cfg);
35661                     throw "Can not add '" + cfg.xtype + "' to Border";
35662                     return null;
35663              
35664                                 
35665              
35666         }
35667         this.beginUpdate();
35668         // add children..
35669         var region = '';
35670         var abn = {};
35671         Roo.each(xitems, function(i)  {
35672             region = nb && i.region ? i.region : false;
35673             
35674             var add = ret.addxtype(i);
35675            
35676             if (region) {
35677                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35678                 if (!i.background) {
35679                     abn[region] = nb[region] ;
35680                 }
35681             }
35682             
35683         });
35684         this.endUpdate();
35685
35686         // make the last non-background panel active..
35687         //if (nb) { Roo.log(abn); }
35688         if (nb) {
35689             
35690             for(var r in abn) {
35691                 region = this.getRegion(r);
35692                 if (region) {
35693                     // tried using nb[r], but it does not work..
35694                      
35695                     region.showPanel(abn[r]);
35696                    
35697                 }
35698             }
35699         }
35700         return ret;
35701         
35702     },
35703     
35704     
35705 // private
35706     factory : function(cfg)
35707     {
35708         
35709         var validRegions = Roo.bootstrap.layout.Border.regions;
35710
35711         var target = cfg.region;
35712         cfg.mgr = this;
35713         
35714         var r = Roo.bootstrap.layout;
35715         Roo.log(target);
35716         switch(target){
35717             case "north":
35718                 return new r.North(cfg);
35719             case "south":
35720                 return new r.South(cfg);
35721             case "east":
35722                 return new r.East(cfg);
35723             case "west":
35724                 return new r.West(cfg);
35725             case "center":
35726                 return new r.Center(cfg);
35727         }
35728         throw 'Layout region "'+target+'" not supported.';
35729     }
35730     
35731     
35732 });
35733  /*
35734  * Based on:
35735  * Ext JS Library 1.1.1
35736  * Copyright(c) 2006-2007, Ext JS, LLC.
35737  *
35738  * Originally Released Under LGPL - original licence link has changed is not relivant.
35739  *
35740  * Fork - LGPL
35741  * <script type="text/javascript">
35742  */
35743  
35744 /**
35745  * @class Roo.bootstrap.layout.Basic
35746  * @extends Roo.util.Observable
35747  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35748  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35749  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35750  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35751  * @cfg {string}   region  the region that it inhabits..
35752  * @cfg {bool}   skipConfig skip config?
35753  * 
35754
35755  */
35756 Roo.bootstrap.layout.Basic = function(config){
35757     
35758     this.mgr = config.mgr;
35759     
35760     this.position = config.region;
35761     
35762     var skipConfig = config.skipConfig;
35763     
35764     this.events = {
35765         /**
35766          * @scope Roo.BasicLayoutRegion
35767          */
35768         
35769         /**
35770          * @event beforeremove
35771          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35772          * @param {Roo.LayoutRegion} this
35773          * @param {Roo.ContentPanel} panel The panel
35774          * @param {Object} e The cancel event object
35775          */
35776         "beforeremove" : true,
35777         /**
35778          * @event invalidated
35779          * Fires when the layout for this region is changed.
35780          * @param {Roo.LayoutRegion} this
35781          */
35782         "invalidated" : true,
35783         /**
35784          * @event visibilitychange
35785          * Fires when this region is shown or hidden 
35786          * @param {Roo.LayoutRegion} this
35787          * @param {Boolean} visibility true or false
35788          */
35789         "visibilitychange" : true,
35790         /**
35791          * @event paneladded
35792          * Fires when a panel is added. 
35793          * @param {Roo.LayoutRegion} this
35794          * @param {Roo.ContentPanel} panel The panel
35795          */
35796         "paneladded" : true,
35797         /**
35798          * @event panelremoved
35799          * Fires when a panel is removed. 
35800          * @param {Roo.LayoutRegion} this
35801          * @param {Roo.ContentPanel} panel The panel
35802          */
35803         "panelremoved" : true,
35804         /**
35805          * @event beforecollapse
35806          * Fires when this region before collapse.
35807          * @param {Roo.LayoutRegion} this
35808          */
35809         "beforecollapse" : true,
35810         /**
35811          * @event collapsed
35812          * Fires when this region is collapsed.
35813          * @param {Roo.LayoutRegion} this
35814          */
35815         "collapsed" : true,
35816         /**
35817          * @event expanded
35818          * Fires when this region is expanded.
35819          * @param {Roo.LayoutRegion} this
35820          */
35821         "expanded" : true,
35822         /**
35823          * @event slideshow
35824          * Fires when this region is slid into view.
35825          * @param {Roo.LayoutRegion} this
35826          */
35827         "slideshow" : true,
35828         /**
35829          * @event slidehide
35830          * Fires when this region slides out of view. 
35831          * @param {Roo.LayoutRegion} this
35832          */
35833         "slidehide" : true,
35834         /**
35835          * @event panelactivated
35836          * Fires when a panel is activated. 
35837          * @param {Roo.LayoutRegion} this
35838          * @param {Roo.ContentPanel} panel The activated panel
35839          */
35840         "panelactivated" : true,
35841         /**
35842          * @event resized
35843          * Fires when the user resizes this region. 
35844          * @param {Roo.LayoutRegion} this
35845          * @param {Number} newSize The new size (width for east/west, height for north/south)
35846          */
35847         "resized" : true
35848     };
35849     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35850     this.panels = new Roo.util.MixedCollection();
35851     this.panels.getKey = this.getPanelId.createDelegate(this);
35852     this.box = null;
35853     this.activePanel = null;
35854     // ensure listeners are added...
35855     
35856     if (config.listeners || config.events) {
35857         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35858             listeners : config.listeners || {},
35859             events : config.events || {}
35860         });
35861     }
35862     
35863     if(skipConfig !== true){
35864         this.applyConfig(config);
35865     }
35866 };
35867
35868 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35869 {
35870     getPanelId : function(p){
35871         return p.getId();
35872     },
35873     
35874     applyConfig : function(config){
35875         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35876         this.config = config;
35877         
35878     },
35879     
35880     /**
35881      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35882      * the width, for horizontal (north, south) the height.
35883      * @param {Number} newSize The new width or height
35884      */
35885     resizeTo : function(newSize){
35886         var el = this.el ? this.el :
35887                  (this.activePanel ? this.activePanel.getEl() : null);
35888         if(el){
35889             switch(this.position){
35890                 case "east":
35891                 case "west":
35892                     el.setWidth(newSize);
35893                     this.fireEvent("resized", this, newSize);
35894                 break;
35895                 case "north":
35896                 case "south":
35897                     el.setHeight(newSize);
35898                     this.fireEvent("resized", this, newSize);
35899                 break;                
35900             }
35901         }
35902     },
35903     
35904     getBox : function(){
35905         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35906     },
35907     
35908     getMargins : function(){
35909         return this.margins;
35910     },
35911     
35912     updateBox : function(box){
35913         this.box = box;
35914         var el = this.activePanel.getEl();
35915         el.dom.style.left = box.x + "px";
35916         el.dom.style.top = box.y + "px";
35917         this.activePanel.setSize(box.width, box.height);
35918     },
35919     
35920     /**
35921      * Returns the container element for this region.
35922      * @return {Roo.Element}
35923      */
35924     getEl : function(){
35925         return this.activePanel;
35926     },
35927     
35928     /**
35929      * Returns true if this region is currently visible.
35930      * @return {Boolean}
35931      */
35932     isVisible : function(){
35933         return this.activePanel ? true : false;
35934     },
35935     
35936     setActivePanel : function(panel){
35937         panel = this.getPanel(panel);
35938         if(this.activePanel && this.activePanel != panel){
35939             this.activePanel.setActiveState(false);
35940             this.activePanel.getEl().setLeftTop(-10000,-10000);
35941         }
35942         this.activePanel = panel;
35943         panel.setActiveState(true);
35944         if(this.box){
35945             panel.setSize(this.box.width, this.box.height);
35946         }
35947         this.fireEvent("panelactivated", this, panel);
35948         this.fireEvent("invalidated");
35949     },
35950     
35951     /**
35952      * Show the specified panel.
35953      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35954      * @return {Roo.ContentPanel} The shown panel or null
35955      */
35956     showPanel : function(panel){
35957         panel = this.getPanel(panel);
35958         if(panel){
35959             this.setActivePanel(panel);
35960         }
35961         return panel;
35962     },
35963     
35964     /**
35965      * Get the active panel for this region.
35966      * @return {Roo.ContentPanel} The active panel or null
35967      */
35968     getActivePanel : function(){
35969         return this.activePanel;
35970     },
35971     
35972     /**
35973      * Add the passed ContentPanel(s)
35974      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35975      * @return {Roo.ContentPanel} The panel added (if only one was added)
35976      */
35977     add : function(panel){
35978         if(arguments.length > 1){
35979             for(var i = 0, len = arguments.length; i < len; i++) {
35980                 this.add(arguments[i]);
35981             }
35982             return null;
35983         }
35984         if(this.hasPanel(panel)){
35985             this.showPanel(panel);
35986             return panel;
35987         }
35988         var el = panel.getEl();
35989         if(el.dom.parentNode != this.mgr.el.dom){
35990             this.mgr.el.dom.appendChild(el.dom);
35991         }
35992         if(panel.setRegion){
35993             panel.setRegion(this);
35994         }
35995         this.panels.add(panel);
35996         el.setStyle("position", "absolute");
35997         if(!panel.background){
35998             this.setActivePanel(panel);
35999             if(this.config.initialSize && this.panels.getCount()==1){
36000                 this.resizeTo(this.config.initialSize);
36001             }
36002         }
36003         this.fireEvent("paneladded", this, panel);
36004         return panel;
36005     },
36006     
36007     /**
36008      * Returns true if the panel is in this region.
36009      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36010      * @return {Boolean}
36011      */
36012     hasPanel : function(panel){
36013         if(typeof panel == "object"){ // must be panel obj
36014             panel = panel.getId();
36015         }
36016         return this.getPanel(panel) ? true : false;
36017     },
36018     
36019     /**
36020      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36021      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36022      * @param {Boolean} preservePanel Overrides the config preservePanel option
36023      * @return {Roo.ContentPanel} The panel that was removed
36024      */
36025     remove : function(panel, preservePanel){
36026         panel = this.getPanel(panel);
36027         if(!panel){
36028             return null;
36029         }
36030         var e = {};
36031         this.fireEvent("beforeremove", this, panel, e);
36032         if(e.cancel === true){
36033             return null;
36034         }
36035         var panelId = panel.getId();
36036         this.panels.removeKey(panelId);
36037         return panel;
36038     },
36039     
36040     /**
36041      * Returns the panel specified or null if it's not in this region.
36042      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36043      * @return {Roo.ContentPanel}
36044      */
36045     getPanel : function(id){
36046         if(typeof id == "object"){ // must be panel obj
36047             return id;
36048         }
36049         return this.panels.get(id);
36050     },
36051     
36052     /**
36053      * Returns this regions position (north/south/east/west/center).
36054      * @return {String} 
36055      */
36056     getPosition: function(){
36057         return this.position;    
36058     }
36059 });/*
36060  * Based on:
36061  * Ext JS Library 1.1.1
36062  * Copyright(c) 2006-2007, Ext JS, LLC.
36063  *
36064  * Originally Released Under LGPL - original licence link has changed is not relivant.
36065  *
36066  * Fork - LGPL
36067  * <script type="text/javascript">
36068  */
36069  
36070 /**
36071  * @class Roo.bootstrap.layout.Region
36072  * @extends Roo.bootstrap.layout.Basic
36073  * This class represents a region in a layout manager.
36074  
36075  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36076  * @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})
36077  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
36078  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
36079  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
36080  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
36081  * @cfg {String}    title           The title for the region (overrides panel titles)
36082  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
36083  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36084  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
36085  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36086  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
36087  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36088  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
36089  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
36090  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
36091  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
36092
36093  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
36094  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
36095  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
36096  * @cfg {Number}    width           For East/West panels
36097  * @cfg {Number}    height          For North/South panels
36098  * @cfg {Boolean}   split           To show the splitter
36099  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
36100  * 
36101  * @cfg {string}   cls             Extra CSS classes to add to region
36102  * 
36103  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36104  * @cfg {string}   region  the region that it inhabits..
36105  *
36106
36107  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
36108  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
36109
36110  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
36111  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
36112  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
36113  */
36114 Roo.bootstrap.layout.Region = function(config)
36115 {
36116     this.applyConfig(config);
36117
36118     var mgr = config.mgr;
36119     var pos = config.region;
36120     config.skipConfig = true;
36121     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36122     
36123     if (mgr.el) {
36124         this.onRender(mgr.el);   
36125     }
36126      
36127     this.visible = true;
36128     this.collapsed = false;
36129     this.unrendered_panels = [];
36130 };
36131
36132 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36133
36134     position: '', // set by wrapper (eg. north/south etc..)
36135     unrendered_panels : null,  // unrendered panels.
36136     
36137     tabPosition : false,
36138     
36139     mgr: false, // points to 'Border'
36140     
36141     
36142     createBody : function(){
36143         /** This region's body element 
36144         * @type Roo.Element */
36145         this.bodyEl = this.el.createChild({
36146                 tag: "div",
36147                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36148         });
36149     },
36150
36151     onRender: function(ctr, pos)
36152     {
36153         var dh = Roo.DomHelper;
36154         /** This region's container element 
36155         * @type Roo.Element */
36156         this.el = dh.append(ctr.dom, {
36157                 tag: "div",
36158                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36159             }, true);
36160         /** This region's title element 
36161         * @type Roo.Element */
36162     
36163         this.titleEl = dh.append(this.el.dom,  {
36164                 tag: "div",
36165                 unselectable: "on",
36166                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36167                 children:[
36168                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36169                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36170                 ]
36171             }, true);
36172         
36173         this.titleEl.enableDisplayMode();
36174         /** This region's title text element 
36175         * @type HTMLElement */
36176         this.titleTextEl = this.titleEl.dom.firstChild;
36177         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36178         /*
36179         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36180         this.closeBtn.enableDisplayMode();
36181         this.closeBtn.on("click", this.closeClicked, this);
36182         this.closeBtn.hide();
36183     */
36184         this.createBody(this.config);
36185         if(this.config.hideWhenEmpty){
36186             this.hide();
36187             this.on("paneladded", this.validateVisibility, this);
36188             this.on("panelremoved", this.validateVisibility, this);
36189         }
36190         if(this.autoScroll){
36191             this.bodyEl.setStyle("overflow", "auto");
36192         }else{
36193             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36194         }
36195         //if(c.titlebar !== false){
36196             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36197                 this.titleEl.hide();
36198             }else{
36199                 this.titleEl.show();
36200                 if(this.config.title){
36201                     this.titleTextEl.innerHTML = this.config.title;
36202                 }
36203             }
36204         //}
36205         if(this.config.collapsed){
36206             this.collapse(true);
36207         }
36208         if(this.config.hidden){
36209             this.hide();
36210         }
36211         
36212         if (this.unrendered_panels && this.unrendered_panels.length) {
36213             for (var i =0;i< this.unrendered_panels.length; i++) {
36214                 this.add(this.unrendered_panels[i]);
36215             }
36216             this.unrendered_panels = null;
36217             
36218         }
36219         
36220     },
36221     
36222     applyConfig : function(c)
36223     {
36224         /*
36225          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36226             var dh = Roo.DomHelper;
36227             if(c.titlebar !== false){
36228                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36229                 this.collapseBtn.on("click", this.collapse, this);
36230                 this.collapseBtn.enableDisplayMode();
36231                 /*
36232                 if(c.showPin === true || this.showPin){
36233                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36234                     this.stickBtn.enableDisplayMode();
36235                     this.stickBtn.on("click", this.expand, this);
36236                     this.stickBtn.hide();
36237                 }
36238                 
36239             }
36240             */
36241             /** This region's collapsed element
36242             * @type Roo.Element */
36243             /*
36244              *
36245             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36246                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36247             ]}, true);
36248             
36249             if(c.floatable !== false){
36250                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36251                this.collapsedEl.on("click", this.collapseClick, this);
36252             }
36253
36254             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36255                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36256                    id: "message", unselectable: "on", style:{"float":"left"}});
36257                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36258              }
36259             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36260             this.expandBtn.on("click", this.expand, this);
36261             
36262         }
36263         
36264         if(this.collapseBtn){
36265             this.collapseBtn.setVisible(c.collapsible == true);
36266         }
36267         
36268         this.cmargins = c.cmargins || this.cmargins ||
36269                          (this.position == "west" || this.position == "east" ?
36270                              {top: 0, left: 2, right:2, bottom: 0} :
36271                              {top: 2, left: 0, right:0, bottom: 2});
36272         */
36273         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36274         
36275         
36276         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36277         
36278         this.autoScroll = c.autoScroll || false;
36279         
36280         
36281        
36282         
36283         this.duration = c.duration || .30;
36284         this.slideDuration = c.slideDuration || .45;
36285         this.config = c;
36286        
36287     },
36288     /**
36289      * Returns true if this region is currently visible.
36290      * @return {Boolean}
36291      */
36292     isVisible : function(){
36293         return this.visible;
36294     },
36295
36296     /**
36297      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36298      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36299      */
36300     //setCollapsedTitle : function(title){
36301     //    title = title || "&#160;";
36302      //   if(this.collapsedTitleTextEl){
36303       //      this.collapsedTitleTextEl.innerHTML = title;
36304        // }
36305     //},
36306
36307     getBox : function(){
36308         var b;
36309       //  if(!this.collapsed){
36310             b = this.el.getBox(false, true);
36311        // }else{
36312           //  b = this.collapsedEl.getBox(false, true);
36313         //}
36314         return b;
36315     },
36316
36317     getMargins : function(){
36318         return this.margins;
36319         //return this.collapsed ? this.cmargins : this.margins;
36320     },
36321 /*
36322     highlight : function(){
36323         this.el.addClass("x-layout-panel-dragover");
36324     },
36325
36326     unhighlight : function(){
36327         this.el.removeClass("x-layout-panel-dragover");
36328     },
36329 */
36330     updateBox : function(box)
36331     {
36332         if (!this.bodyEl) {
36333             return; // not rendered yet..
36334         }
36335         
36336         this.box = box;
36337         if(!this.collapsed){
36338             this.el.dom.style.left = box.x + "px";
36339             this.el.dom.style.top = box.y + "px";
36340             this.updateBody(box.width, box.height);
36341         }else{
36342             this.collapsedEl.dom.style.left = box.x + "px";
36343             this.collapsedEl.dom.style.top = box.y + "px";
36344             this.collapsedEl.setSize(box.width, box.height);
36345         }
36346         if(this.tabs){
36347             this.tabs.autoSizeTabs();
36348         }
36349     },
36350
36351     updateBody : function(w, h)
36352     {
36353         if(w !== null){
36354             this.el.setWidth(w);
36355             w -= this.el.getBorderWidth("rl");
36356             if(this.config.adjustments){
36357                 w += this.config.adjustments[0];
36358             }
36359         }
36360         if(h !== null && h > 0){
36361             this.el.setHeight(h);
36362             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36363             h -= this.el.getBorderWidth("tb");
36364             if(this.config.adjustments){
36365                 h += this.config.adjustments[1];
36366             }
36367             this.bodyEl.setHeight(h);
36368             if(this.tabs){
36369                 h = this.tabs.syncHeight(h);
36370             }
36371         }
36372         if(this.panelSize){
36373             w = w !== null ? w : this.panelSize.width;
36374             h = h !== null ? h : this.panelSize.height;
36375         }
36376         if(this.activePanel){
36377             var el = this.activePanel.getEl();
36378             w = w !== null ? w : el.getWidth();
36379             h = h !== null ? h : el.getHeight();
36380             this.panelSize = {width: w, height: h};
36381             this.activePanel.setSize(w, h);
36382         }
36383         if(Roo.isIE && this.tabs){
36384             this.tabs.el.repaint();
36385         }
36386     },
36387
36388     /**
36389      * Returns the container element for this region.
36390      * @return {Roo.Element}
36391      */
36392     getEl : function(){
36393         return this.el;
36394     },
36395
36396     /**
36397      * Hides this region.
36398      */
36399     hide : function(){
36400         //if(!this.collapsed){
36401             this.el.dom.style.left = "-2000px";
36402             this.el.hide();
36403         //}else{
36404          //   this.collapsedEl.dom.style.left = "-2000px";
36405          //   this.collapsedEl.hide();
36406        // }
36407         this.visible = false;
36408         this.fireEvent("visibilitychange", this, false);
36409     },
36410
36411     /**
36412      * Shows this region if it was previously hidden.
36413      */
36414     show : function(){
36415         //if(!this.collapsed){
36416             this.el.show();
36417         //}else{
36418         //    this.collapsedEl.show();
36419        // }
36420         this.visible = true;
36421         this.fireEvent("visibilitychange", this, true);
36422     },
36423 /*
36424     closeClicked : function(){
36425         if(this.activePanel){
36426             this.remove(this.activePanel);
36427         }
36428     },
36429
36430     collapseClick : function(e){
36431         if(this.isSlid){
36432            e.stopPropagation();
36433            this.slideIn();
36434         }else{
36435            e.stopPropagation();
36436            this.slideOut();
36437         }
36438     },
36439 */
36440     /**
36441      * Collapses this region.
36442      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36443      */
36444     /*
36445     collapse : function(skipAnim, skipCheck = false){
36446         if(this.collapsed) {
36447             return;
36448         }
36449         
36450         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36451             
36452             this.collapsed = true;
36453             if(this.split){
36454                 this.split.el.hide();
36455             }
36456             if(this.config.animate && skipAnim !== true){
36457                 this.fireEvent("invalidated", this);
36458                 this.animateCollapse();
36459             }else{
36460                 this.el.setLocation(-20000,-20000);
36461                 this.el.hide();
36462                 this.collapsedEl.show();
36463                 this.fireEvent("collapsed", this);
36464                 this.fireEvent("invalidated", this);
36465             }
36466         }
36467         
36468     },
36469 */
36470     animateCollapse : function(){
36471         // overridden
36472     },
36473
36474     /**
36475      * Expands this region if it was previously collapsed.
36476      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36477      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36478      */
36479     /*
36480     expand : function(e, skipAnim){
36481         if(e) {
36482             e.stopPropagation();
36483         }
36484         if(!this.collapsed || this.el.hasActiveFx()) {
36485             return;
36486         }
36487         if(this.isSlid){
36488             this.afterSlideIn();
36489             skipAnim = true;
36490         }
36491         this.collapsed = false;
36492         if(this.config.animate && skipAnim !== true){
36493             this.animateExpand();
36494         }else{
36495             this.el.show();
36496             if(this.split){
36497                 this.split.el.show();
36498             }
36499             this.collapsedEl.setLocation(-2000,-2000);
36500             this.collapsedEl.hide();
36501             this.fireEvent("invalidated", this);
36502             this.fireEvent("expanded", this);
36503         }
36504     },
36505 */
36506     animateExpand : function(){
36507         // overridden
36508     },
36509
36510     initTabs : function()
36511     {
36512         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36513         
36514         var ts = new Roo.bootstrap.panel.Tabs({
36515             el: this.bodyEl.dom,
36516             region : this,
36517             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36518             disableTooltips: this.config.disableTabTips,
36519             toolbar : this.config.toolbar
36520         });
36521         
36522         if(this.config.hideTabs){
36523             ts.stripWrap.setDisplayed(false);
36524         }
36525         this.tabs = ts;
36526         ts.resizeTabs = this.config.resizeTabs === true;
36527         ts.minTabWidth = this.config.minTabWidth || 40;
36528         ts.maxTabWidth = this.config.maxTabWidth || 250;
36529         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36530         ts.monitorResize = false;
36531         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36532         ts.bodyEl.addClass('roo-layout-tabs-body');
36533         this.panels.each(this.initPanelAsTab, this);
36534     },
36535
36536     initPanelAsTab : function(panel){
36537         var ti = this.tabs.addTab(
36538             panel.getEl().id,
36539             panel.getTitle(),
36540             null,
36541             this.config.closeOnTab && panel.isClosable(),
36542             panel.tpl
36543         );
36544         if(panel.tabTip !== undefined){
36545             ti.setTooltip(panel.tabTip);
36546         }
36547         ti.on("activate", function(){
36548               this.setActivePanel(panel);
36549         }, this);
36550         
36551         if(this.config.closeOnTab){
36552             ti.on("beforeclose", function(t, e){
36553                 e.cancel = true;
36554                 this.remove(panel);
36555             }, this);
36556         }
36557         
36558         panel.tabItem = ti;
36559         
36560         return ti;
36561     },
36562
36563     updatePanelTitle : function(panel, title)
36564     {
36565         if(this.activePanel == panel){
36566             this.updateTitle(title);
36567         }
36568         if(this.tabs){
36569             var ti = this.tabs.getTab(panel.getEl().id);
36570             ti.setText(title);
36571             if(panel.tabTip !== undefined){
36572                 ti.setTooltip(panel.tabTip);
36573             }
36574         }
36575     },
36576
36577     updateTitle : function(title){
36578         if(this.titleTextEl && !this.config.title){
36579             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36580         }
36581     },
36582
36583     setActivePanel : function(panel)
36584     {
36585         panel = this.getPanel(panel);
36586         if(this.activePanel && this.activePanel != panel){
36587             if(this.activePanel.setActiveState(false) === false){
36588                 return;
36589             }
36590         }
36591         this.activePanel = panel;
36592         panel.setActiveState(true);
36593         if(this.panelSize){
36594             panel.setSize(this.panelSize.width, this.panelSize.height);
36595         }
36596         if(this.closeBtn){
36597             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36598         }
36599         this.updateTitle(panel.getTitle());
36600         if(this.tabs){
36601             this.fireEvent("invalidated", this);
36602         }
36603         this.fireEvent("panelactivated", this, panel);
36604     },
36605
36606     /**
36607      * Shows the specified panel.
36608      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36609      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36610      */
36611     showPanel : function(panel)
36612     {
36613         panel = this.getPanel(panel);
36614         if(panel){
36615             if(this.tabs){
36616                 var tab = this.tabs.getTab(panel.getEl().id);
36617                 if(tab.isHidden()){
36618                     this.tabs.unhideTab(tab.id);
36619                 }
36620                 tab.activate();
36621             }else{
36622                 this.setActivePanel(panel);
36623             }
36624         }
36625         return panel;
36626     },
36627
36628     /**
36629      * Get the active panel for this region.
36630      * @return {Roo.ContentPanel} The active panel or null
36631      */
36632     getActivePanel : function(){
36633         return this.activePanel;
36634     },
36635
36636     validateVisibility : function(){
36637         if(this.panels.getCount() < 1){
36638             this.updateTitle("&#160;");
36639             this.closeBtn.hide();
36640             this.hide();
36641         }else{
36642             if(!this.isVisible()){
36643                 this.show();
36644             }
36645         }
36646     },
36647
36648     /**
36649      * Adds the passed ContentPanel(s) to this region.
36650      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36651      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36652      */
36653     add : function(panel)
36654     {
36655         if(arguments.length > 1){
36656             for(var i = 0, len = arguments.length; i < len; i++) {
36657                 this.add(arguments[i]);
36658             }
36659             return null;
36660         }
36661         
36662         // if we have not been rendered yet, then we can not really do much of this..
36663         if (!this.bodyEl) {
36664             this.unrendered_panels.push(panel);
36665             return panel;
36666         }
36667         
36668         
36669         
36670         
36671         if(this.hasPanel(panel)){
36672             this.showPanel(panel);
36673             return panel;
36674         }
36675         panel.setRegion(this);
36676         this.panels.add(panel);
36677        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36678             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36679             // and hide them... ???
36680             this.bodyEl.dom.appendChild(panel.getEl().dom);
36681             if(panel.background !== true){
36682                 this.setActivePanel(panel);
36683             }
36684             this.fireEvent("paneladded", this, panel);
36685             return panel;
36686         }
36687         */
36688         if(!this.tabs){
36689             this.initTabs();
36690         }else{
36691             this.initPanelAsTab(panel);
36692         }
36693         
36694         
36695         if(panel.background !== true){
36696             this.tabs.activate(panel.getEl().id);
36697         }
36698         this.fireEvent("paneladded", this, panel);
36699         return panel;
36700     },
36701
36702     /**
36703      * Hides the tab for the specified panel.
36704      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36705      */
36706     hidePanel : function(panel){
36707         if(this.tabs && (panel = this.getPanel(panel))){
36708             this.tabs.hideTab(panel.getEl().id);
36709         }
36710     },
36711
36712     /**
36713      * Unhides the tab for a previously hidden panel.
36714      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36715      */
36716     unhidePanel : function(panel){
36717         if(this.tabs && (panel = this.getPanel(panel))){
36718             this.tabs.unhideTab(panel.getEl().id);
36719         }
36720     },
36721
36722     clearPanels : function(){
36723         while(this.panels.getCount() > 0){
36724              this.remove(this.panels.first());
36725         }
36726     },
36727
36728     /**
36729      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36730      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36731      * @param {Boolean} preservePanel Overrides the config preservePanel option
36732      * @return {Roo.ContentPanel} The panel that was removed
36733      */
36734     remove : function(panel, preservePanel)
36735     {
36736         panel = this.getPanel(panel);
36737         if(!panel){
36738             return null;
36739         }
36740         var e = {};
36741         this.fireEvent("beforeremove", this, panel, e);
36742         if(e.cancel === true){
36743             return null;
36744         }
36745         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36746         var panelId = panel.getId();
36747         this.panels.removeKey(panelId);
36748         if(preservePanel){
36749             document.body.appendChild(panel.getEl().dom);
36750         }
36751         if(this.tabs){
36752             this.tabs.removeTab(panel.getEl().id);
36753         }else if (!preservePanel){
36754             this.bodyEl.dom.removeChild(panel.getEl().dom);
36755         }
36756         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36757             var p = this.panels.first();
36758             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36759             tempEl.appendChild(p.getEl().dom);
36760             this.bodyEl.update("");
36761             this.bodyEl.dom.appendChild(p.getEl().dom);
36762             tempEl = null;
36763             this.updateTitle(p.getTitle());
36764             this.tabs = null;
36765             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36766             this.setActivePanel(p);
36767         }
36768         panel.setRegion(null);
36769         if(this.activePanel == panel){
36770             this.activePanel = null;
36771         }
36772         if(this.config.autoDestroy !== false && preservePanel !== true){
36773             try{panel.destroy();}catch(e){}
36774         }
36775         this.fireEvent("panelremoved", this, panel);
36776         return panel;
36777     },
36778
36779     /**
36780      * Returns the TabPanel component used by this region
36781      * @return {Roo.TabPanel}
36782      */
36783     getTabs : function(){
36784         return this.tabs;
36785     },
36786
36787     createTool : function(parentEl, className){
36788         var btn = Roo.DomHelper.append(parentEl, {
36789             tag: "div",
36790             cls: "x-layout-tools-button",
36791             children: [ {
36792                 tag: "div",
36793                 cls: "roo-layout-tools-button-inner " + className,
36794                 html: "&#160;"
36795             }]
36796         }, true);
36797         btn.addClassOnOver("roo-layout-tools-button-over");
36798         return btn;
36799     }
36800 });/*
36801  * Based on:
36802  * Ext JS Library 1.1.1
36803  * Copyright(c) 2006-2007, Ext JS, LLC.
36804  *
36805  * Originally Released Under LGPL - original licence link has changed is not relivant.
36806  *
36807  * Fork - LGPL
36808  * <script type="text/javascript">
36809  */
36810  
36811
36812
36813 /**
36814  * @class Roo.SplitLayoutRegion
36815  * @extends Roo.LayoutRegion
36816  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36817  */
36818 Roo.bootstrap.layout.Split = function(config){
36819     this.cursor = config.cursor;
36820     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36821 };
36822
36823 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36824 {
36825     splitTip : "Drag to resize.",
36826     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36827     useSplitTips : false,
36828
36829     applyConfig : function(config){
36830         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36831     },
36832     
36833     onRender : function(ctr,pos) {
36834         
36835         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36836         if(!this.config.split){
36837             return;
36838         }
36839         if(!this.split){
36840             
36841             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36842                             tag: "div",
36843                             id: this.el.id + "-split",
36844                             cls: "roo-layout-split roo-layout-split-"+this.position,
36845                             html: "&#160;"
36846             });
36847             /** The SplitBar for this region 
36848             * @type Roo.SplitBar */
36849             // does not exist yet...
36850             Roo.log([this.position, this.orientation]);
36851             
36852             this.split = new Roo.bootstrap.SplitBar({
36853                 dragElement : splitEl,
36854                 resizingElement: this.el,
36855                 orientation : this.orientation
36856             });
36857             
36858             this.split.on("moved", this.onSplitMove, this);
36859             this.split.useShim = this.config.useShim === true;
36860             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36861             if(this.useSplitTips){
36862                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36863             }
36864             //if(config.collapsible){
36865             //    this.split.el.on("dblclick", this.collapse,  this);
36866             //}
36867         }
36868         if(typeof this.config.minSize != "undefined"){
36869             this.split.minSize = this.config.minSize;
36870         }
36871         if(typeof this.config.maxSize != "undefined"){
36872             this.split.maxSize = this.config.maxSize;
36873         }
36874         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36875             this.hideSplitter();
36876         }
36877         
36878     },
36879
36880     getHMaxSize : function(){
36881          var cmax = this.config.maxSize || 10000;
36882          var center = this.mgr.getRegion("center");
36883          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36884     },
36885
36886     getVMaxSize : function(){
36887          var cmax = this.config.maxSize || 10000;
36888          var center = this.mgr.getRegion("center");
36889          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36890     },
36891
36892     onSplitMove : function(split, newSize){
36893         this.fireEvent("resized", this, newSize);
36894     },
36895     
36896     /** 
36897      * Returns the {@link Roo.SplitBar} for this region.
36898      * @return {Roo.SplitBar}
36899      */
36900     getSplitBar : function(){
36901         return this.split;
36902     },
36903     
36904     hide : function(){
36905         this.hideSplitter();
36906         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36907     },
36908
36909     hideSplitter : function(){
36910         if(this.split){
36911             this.split.el.setLocation(-2000,-2000);
36912             this.split.el.hide();
36913         }
36914     },
36915
36916     show : function(){
36917         if(this.split){
36918             this.split.el.show();
36919         }
36920         Roo.bootstrap.layout.Split.superclass.show.call(this);
36921     },
36922     
36923     beforeSlide: function(){
36924         if(Roo.isGecko){// firefox overflow auto bug workaround
36925             this.bodyEl.clip();
36926             if(this.tabs) {
36927                 this.tabs.bodyEl.clip();
36928             }
36929             if(this.activePanel){
36930                 this.activePanel.getEl().clip();
36931                 
36932                 if(this.activePanel.beforeSlide){
36933                     this.activePanel.beforeSlide();
36934                 }
36935             }
36936         }
36937     },
36938     
36939     afterSlide : function(){
36940         if(Roo.isGecko){// firefox overflow auto bug workaround
36941             this.bodyEl.unclip();
36942             if(this.tabs) {
36943                 this.tabs.bodyEl.unclip();
36944             }
36945             if(this.activePanel){
36946                 this.activePanel.getEl().unclip();
36947                 if(this.activePanel.afterSlide){
36948                     this.activePanel.afterSlide();
36949                 }
36950             }
36951         }
36952     },
36953
36954     initAutoHide : function(){
36955         if(this.autoHide !== false){
36956             if(!this.autoHideHd){
36957                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36958                 this.autoHideHd = {
36959                     "mouseout": function(e){
36960                         if(!e.within(this.el, true)){
36961                             st.delay(500);
36962                         }
36963                     },
36964                     "mouseover" : function(e){
36965                         st.cancel();
36966                     },
36967                     scope : this
36968                 };
36969             }
36970             this.el.on(this.autoHideHd);
36971         }
36972     },
36973
36974     clearAutoHide : function(){
36975         if(this.autoHide !== false){
36976             this.el.un("mouseout", this.autoHideHd.mouseout);
36977             this.el.un("mouseover", this.autoHideHd.mouseover);
36978         }
36979     },
36980
36981     clearMonitor : function(){
36982         Roo.get(document).un("click", this.slideInIf, this);
36983     },
36984
36985     // these names are backwards but not changed for compat
36986     slideOut : function(){
36987         if(this.isSlid || this.el.hasActiveFx()){
36988             return;
36989         }
36990         this.isSlid = true;
36991         if(this.collapseBtn){
36992             this.collapseBtn.hide();
36993         }
36994         this.closeBtnState = this.closeBtn.getStyle('display');
36995         this.closeBtn.hide();
36996         if(this.stickBtn){
36997             this.stickBtn.show();
36998         }
36999         this.el.show();
37000         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37001         this.beforeSlide();
37002         this.el.setStyle("z-index", 10001);
37003         this.el.slideIn(this.getSlideAnchor(), {
37004             callback: function(){
37005                 this.afterSlide();
37006                 this.initAutoHide();
37007                 Roo.get(document).on("click", this.slideInIf, this);
37008                 this.fireEvent("slideshow", this);
37009             },
37010             scope: this,
37011             block: true
37012         });
37013     },
37014
37015     afterSlideIn : function(){
37016         this.clearAutoHide();
37017         this.isSlid = false;
37018         this.clearMonitor();
37019         this.el.setStyle("z-index", "");
37020         if(this.collapseBtn){
37021             this.collapseBtn.show();
37022         }
37023         this.closeBtn.setStyle('display', this.closeBtnState);
37024         if(this.stickBtn){
37025             this.stickBtn.hide();
37026         }
37027         this.fireEvent("slidehide", this);
37028     },
37029
37030     slideIn : function(cb){
37031         if(!this.isSlid || this.el.hasActiveFx()){
37032             Roo.callback(cb);
37033             return;
37034         }
37035         this.isSlid = false;
37036         this.beforeSlide();
37037         this.el.slideOut(this.getSlideAnchor(), {
37038             callback: function(){
37039                 this.el.setLeftTop(-10000, -10000);
37040                 this.afterSlide();
37041                 this.afterSlideIn();
37042                 Roo.callback(cb);
37043             },
37044             scope: this,
37045             block: true
37046         });
37047     },
37048     
37049     slideInIf : function(e){
37050         if(!e.within(this.el)){
37051             this.slideIn();
37052         }
37053     },
37054
37055     animateCollapse : function(){
37056         this.beforeSlide();
37057         this.el.setStyle("z-index", 20000);
37058         var anchor = this.getSlideAnchor();
37059         this.el.slideOut(anchor, {
37060             callback : function(){
37061                 this.el.setStyle("z-index", "");
37062                 this.collapsedEl.slideIn(anchor, {duration:.3});
37063                 this.afterSlide();
37064                 this.el.setLocation(-10000,-10000);
37065                 this.el.hide();
37066                 this.fireEvent("collapsed", this);
37067             },
37068             scope: this,
37069             block: true
37070         });
37071     },
37072
37073     animateExpand : function(){
37074         this.beforeSlide();
37075         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37076         this.el.setStyle("z-index", 20000);
37077         this.collapsedEl.hide({
37078             duration:.1
37079         });
37080         this.el.slideIn(this.getSlideAnchor(), {
37081             callback : function(){
37082                 this.el.setStyle("z-index", "");
37083                 this.afterSlide();
37084                 if(this.split){
37085                     this.split.el.show();
37086                 }
37087                 this.fireEvent("invalidated", this);
37088                 this.fireEvent("expanded", this);
37089             },
37090             scope: this,
37091             block: true
37092         });
37093     },
37094
37095     anchors : {
37096         "west" : "left",
37097         "east" : "right",
37098         "north" : "top",
37099         "south" : "bottom"
37100     },
37101
37102     sanchors : {
37103         "west" : "l",
37104         "east" : "r",
37105         "north" : "t",
37106         "south" : "b"
37107     },
37108
37109     canchors : {
37110         "west" : "tl-tr",
37111         "east" : "tr-tl",
37112         "north" : "tl-bl",
37113         "south" : "bl-tl"
37114     },
37115
37116     getAnchor : function(){
37117         return this.anchors[this.position];
37118     },
37119
37120     getCollapseAnchor : function(){
37121         return this.canchors[this.position];
37122     },
37123
37124     getSlideAnchor : function(){
37125         return this.sanchors[this.position];
37126     },
37127
37128     getAlignAdj : function(){
37129         var cm = this.cmargins;
37130         switch(this.position){
37131             case "west":
37132                 return [0, 0];
37133             break;
37134             case "east":
37135                 return [0, 0];
37136             break;
37137             case "north":
37138                 return [0, 0];
37139             break;
37140             case "south":
37141                 return [0, 0];
37142             break;
37143         }
37144     },
37145
37146     getExpandAdj : function(){
37147         var c = this.collapsedEl, cm = this.cmargins;
37148         switch(this.position){
37149             case "west":
37150                 return [-(cm.right+c.getWidth()+cm.left), 0];
37151             break;
37152             case "east":
37153                 return [cm.right+c.getWidth()+cm.left, 0];
37154             break;
37155             case "north":
37156                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37157             break;
37158             case "south":
37159                 return [0, cm.top+cm.bottom+c.getHeight()];
37160             break;
37161         }
37162     }
37163 });/*
37164  * Based on:
37165  * Ext JS Library 1.1.1
37166  * Copyright(c) 2006-2007, Ext JS, LLC.
37167  *
37168  * Originally Released Under LGPL - original licence link has changed is not relivant.
37169  *
37170  * Fork - LGPL
37171  * <script type="text/javascript">
37172  */
37173 /*
37174  * These classes are private internal classes
37175  */
37176 Roo.bootstrap.layout.Center = function(config){
37177     config.region = "center";
37178     Roo.bootstrap.layout.Region.call(this, config);
37179     this.visible = true;
37180     this.minWidth = config.minWidth || 20;
37181     this.minHeight = config.minHeight || 20;
37182 };
37183
37184 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37185     hide : function(){
37186         // center panel can't be hidden
37187     },
37188     
37189     show : function(){
37190         // center panel can't be hidden
37191     },
37192     
37193     getMinWidth: function(){
37194         return this.minWidth;
37195     },
37196     
37197     getMinHeight: function(){
37198         return this.minHeight;
37199     }
37200 });
37201
37202
37203
37204
37205  
37206
37207
37208
37209
37210
37211
37212 Roo.bootstrap.layout.North = function(config)
37213 {
37214     config.region = 'north';
37215     config.cursor = 'n-resize';
37216     
37217     Roo.bootstrap.layout.Split.call(this, config);
37218     
37219     
37220     if(this.split){
37221         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37222         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37223         this.split.el.addClass("roo-layout-split-v");
37224     }
37225     var size = config.initialSize || config.height;
37226     if(typeof size != "undefined"){
37227         this.el.setHeight(size);
37228     }
37229 };
37230 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37231 {
37232     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37233     
37234     
37235     
37236     getBox : function(){
37237         if(this.collapsed){
37238             return this.collapsedEl.getBox();
37239         }
37240         var box = this.el.getBox();
37241         if(this.split){
37242             box.height += this.split.el.getHeight();
37243         }
37244         return box;
37245     },
37246     
37247     updateBox : function(box){
37248         if(this.split && !this.collapsed){
37249             box.height -= this.split.el.getHeight();
37250             this.split.el.setLeft(box.x);
37251             this.split.el.setTop(box.y+box.height);
37252             this.split.el.setWidth(box.width);
37253         }
37254         if(this.collapsed){
37255             this.updateBody(box.width, null);
37256         }
37257         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37258     }
37259 });
37260
37261
37262
37263
37264
37265 Roo.bootstrap.layout.South = function(config){
37266     config.region = 'south';
37267     config.cursor = 's-resize';
37268     Roo.bootstrap.layout.Split.call(this, config);
37269     if(this.split){
37270         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37271         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37272         this.split.el.addClass("roo-layout-split-v");
37273     }
37274     var size = config.initialSize || config.height;
37275     if(typeof size != "undefined"){
37276         this.el.setHeight(size);
37277     }
37278 };
37279
37280 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37281     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37282     getBox : function(){
37283         if(this.collapsed){
37284             return this.collapsedEl.getBox();
37285         }
37286         var box = this.el.getBox();
37287         if(this.split){
37288             var sh = this.split.el.getHeight();
37289             box.height += sh;
37290             box.y -= sh;
37291         }
37292         return box;
37293     },
37294     
37295     updateBox : function(box){
37296         if(this.split && !this.collapsed){
37297             var sh = this.split.el.getHeight();
37298             box.height -= sh;
37299             box.y += sh;
37300             this.split.el.setLeft(box.x);
37301             this.split.el.setTop(box.y-sh);
37302             this.split.el.setWidth(box.width);
37303         }
37304         if(this.collapsed){
37305             this.updateBody(box.width, null);
37306         }
37307         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37308     }
37309 });
37310
37311 Roo.bootstrap.layout.East = function(config){
37312     config.region = "east";
37313     config.cursor = "e-resize";
37314     Roo.bootstrap.layout.Split.call(this, config);
37315     if(this.split){
37316         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37317         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37318         this.split.el.addClass("roo-layout-split-h");
37319     }
37320     var size = config.initialSize || config.width;
37321     if(typeof size != "undefined"){
37322         this.el.setWidth(size);
37323     }
37324 };
37325 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37326     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37327     getBox : function(){
37328         if(this.collapsed){
37329             return this.collapsedEl.getBox();
37330         }
37331         var box = this.el.getBox();
37332         if(this.split){
37333             var sw = this.split.el.getWidth();
37334             box.width += sw;
37335             box.x -= sw;
37336         }
37337         return box;
37338     },
37339
37340     updateBox : function(box){
37341         if(this.split && !this.collapsed){
37342             var sw = this.split.el.getWidth();
37343             box.width -= sw;
37344             this.split.el.setLeft(box.x);
37345             this.split.el.setTop(box.y);
37346             this.split.el.setHeight(box.height);
37347             box.x += sw;
37348         }
37349         if(this.collapsed){
37350             this.updateBody(null, box.height);
37351         }
37352         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37353     }
37354 });
37355
37356 Roo.bootstrap.layout.West = function(config){
37357     config.region = "west";
37358     config.cursor = "w-resize";
37359     
37360     Roo.bootstrap.layout.Split.call(this, config);
37361     if(this.split){
37362         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37363         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37364         this.split.el.addClass("roo-layout-split-h");
37365     }
37366     
37367 };
37368 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37369     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37370     
37371     onRender: function(ctr, pos)
37372     {
37373         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37374         var size = this.config.initialSize || this.config.width;
37375         if(typeof size != "undefined"){
37376             this.el.setWidth(size);
37377         }
37378     },
37379     
37380     getBox : function(){
37381         if(this.collapsed){
37382             return this.collapsedEl.getBox();
37383         }
37384         var box = this.el.getBox();
37385         if(this.split){
37386             box.width += this.split.el.getWidth();
37387         }
37388         return box;
37389     },
37390     
37391     updateBox : function(box){
37392         if(this.split && !this.collapsed){
37393             var sw = this.split.el.getWidth();
37394             box.width -= sw;
37395             this.split.el.setLeft(box.x+box.width);
37396             this.split.el.setTop(box.y);
37397             this.split.el.setHeight(box.height);
37398         }
37399         if(this.collapsed){
37400             this.updateBody(null, box.height);
37401         }
37402         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37403     }
37404 });Roo.namespace("Roo.bootstrap.panel");/*
37405  * Based on:
37406  * Ext JS Library 1.1.1
37407  * Copyright(c) 2006-2007, Ext JS, LLC.
37408  *
37409  * Originally Released Under LGPL - original licence link has changed is not relivant.
37410  *
37411  * Fork - LGPL
37412  * <script type="text/javascript">
37413  */
37414 /**
37415  * @class Roo.ContentPanel
37416  * @extends Roo.util.Observable
37417  * A basic ContentPanel element.
37418  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37419  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37420  * @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
37421  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37422  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37423  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37424  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37425  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37426  * @cfg {String} title          The title for this panel
37427  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37428  * @cfg {String} url            Calls {@link #setUrl} with this value
37429  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37430  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37431  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37432  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37433  * @cfg {Boolean} badges render the badges
37434
37435  * @constructor
37436  * Create a new ContentPanel.
37437  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37438  * @param {String/Object} config A string to set only the title or a config object
37439  * @param {String} content (optional) Set the HTML content for this panel
37440  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37441  */
37442 Roo.bootstrap.panel.Content = function( config){
37443     
37444     this.tpl = config.tpl || false;
37445     
37446     var el = config.el;
37447     var content = config.content;
37448
37449     if(config.autoCreate){ // xtype is available if this is called from factory
37450         el = Roo.id();
37451     }
37452     this.el = Roo.get(el);
37453     if(!this.el && config && config.autoCreate){
37454         if(typeof config.autoCreate == "object"){
37455             if(!config.autoCreate.id){
37456                 config.autoCreate.id = config.id||el;
37457             }
37458             this.el = Roo.DomHelper.append(document.body,
37459                         config.autoCreate, true);
37460         }else{
37461             var elcfg =  {   tag: "div",
37462                             cls: "roo-layout-inactive-content",
37463                             id: config.id||el
37464                             };
37465             if (config.html) {
37466                 elcfg.html = config.html;
37467                 
37468             }
37469                         
37470             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37471         }
37472     } 
37473     this.closable = false;
37474     this.loaded = false;
37475     this.active = false;
37476    
37477       
37478     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37479         
37480         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37481         
37482         this.wrapEl = this.el; //this.el.wrap();
37483         var ti = [];
37484         if (config.toolbar.items) {
37485             ti = config.toolbar.items ;
37486             delete config.toolbar.items ;
37487         }
37488         
37489         var nitems = [];
37490         this.toolbar.render(this.wrapEl, 'before');
37491         for(var i =0;i < ti.length;i++) {
37492           //  Roo.log(['add child', items[i]]);
37493             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37494         }
37495         this.toolbar.items = nitems;
37496         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37497         delete config.toolbar;
37498         
37499     }
37500     /*
37501     // xtype created footer. - not sure if will work as we normally have to render first..
37502     if (this.footer && !this.footer.el && this.footer.xtype) {
37503         if (!this.wrapEl) {
37504             this.wrapEl = this.el.wrap();
37505         }
37506     
37507         this.footer.container = this.wrapEl.createChild();
37508          
37509         this.footer = Roo.factory(this.footer, Roo);
37510         
37511     }
37512     */
37513     
37514      if(typeof config == "string"){
37515         this.title = config;
37516     }else{
37517         Roo.apply(this, config);
37518     }
37519     
37520     if(this.resizeEl){
37521         this.resizeEl = Roo.get(this.resizeEl, true);
37522     }else{
37523         this.resizeEl = this.el;
37524     }
37525     // handle view.xtype
37526     
37527  
37528     
37529     
37530     this.addEvents({
37531         /**
37532          * @event activate
37533          * Fires when this panel is activated. 
37534          * @param {Roo.ContentPanel} this
37535          */
37536         "activate" : true,
37537         /**
37538          * @event deactivate
37539          * Fires when this panel is activated. 
37540          * @param {Roo.ContentPanel} this
37541          */
37542         "deactivate" : true,
37543
37544         /**
37545          * @event resize
37546          * Fires when this panel is resized if fitToFrame is true.
37547          * @param {Roo.ContentPanel} this
37548          * @param {Number} width The width after any component adjustments
37549          * @param {Number} height The height after any component adjustments
37550          */
37551         "resize" : true,
37552         
37553          /**
37554          * @event render
37555          * Fires when this tab is created
37556          * @param {Roo.ContentPanel} this
37557          */
37558         "render" : true
37559         
37560         
37561         
37562     });
37563     
37564
37565     
37566     
37567     if(this.autoScroll){
37568         this.resizeEl.setStyle("overflow", "auto");
37569     } else {
37570         // fix randome scrolling
37571         //this.el.on('scroll', function() {
37572         //    Roo.log('fix random scolling');
37573         //    this.scrollTo('top',0); 
37574         //});
37575     }
37576     content = content || this.content;
37577     if(content){
37578         this.setContent(content);
37579     }
37580     if(config && config.url){
37581         this.setUrl(this.url, this.params, this.loadOnce);
37582     }
37583     
37584     
37585     
37586     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37587     
37588     if (this.view && typeof(this.view.xtype) != 'undefined') {
37589         this.view.el = this.el.appendChild(document.createElement("div"));
37590         this.view = Roo.factory(this.view); 
37591         this.view.render  &&  this.view.render(false, '');  
37592     }
37593     
37594     
37595     this.fireEvent('render', this);
37596 };
37597
37598 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37599     
37600     tabTip : '',
37601     
37602     setRegion : function(region){
37603         this.region = region;
37604         this.setActiveClass(region && !this.background);
37605     },
37606     
37607     
37608     setActiveClass: function(state)
37609     {
37610         if(state){
37611            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37612            this.el.setStyle('position','relative');
37613         }else{
37614            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37615            this.el.setStyle('position', 'absolute');
37616         } 
37617     },
37618     
37619     /**
37620      * Returns the toolbar for this Panel if one was configured. 
37621      * @return {Roo.Toolbar} 
37622      */
37623     getToolbar : function(){
37624         return this.toolbar;
37625     },
37626     
37627     setActiveState : function(active)
37628     {
37629         this.active = active;
37630         this.setActiveClass(active);
37631         if(!active){
37632             if(this.fireEvent("deactivate", this) === false){
37633                 return false;
37634             }
37635             return true;
37636         }
37637         this.fireEvent("activate", this);
37638         return true;
37639     },
37640     /**
37641      * Updates this panel's element
37642      * @param {String} content The new content
37643      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37644     */
37645     setContent : function(content, loadScripts){
37646         this.el.update(content, loadScripts);
37647     },
37648
37649     ignoreResize : function(w, h){
37650         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37651             return true;
37652         }else{
37653             this.lastSize = {width: w, height: h};
37654             return false;
37655         }
37656     },
37657     /**
37658      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37659      * @return {Roo.UpdateManager} The UpdateManager
37660      */
37661     getUpdateManager : function(){
37662         return this.el.getUpdateManager();
37663     },
37664      /**
37665      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37666      * @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:
37667 <pre><code>
37668 panel.load({
37669     url: "your-url.php",
37670     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37671     callback: yourFunction,
37672     scope: yourObject, //(optional scope)
37673     discardUrl: false,
37674     nocache: false,
37675     text: "Loading...",
37676     timeout: 30,
37677     scripts: false
37678 });
37679 </code></pre>
37680      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37681      * 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.
37682      * @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}
37683      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37684      * @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.
37685      * @return {Roo.ContentPanel} this
37686      */
37687     load : function(){
37688         var um = this.el.getUpdateManager();
37689         um.update.apply(um, arguments);
37690         return this;
37691     },
37692
37693
37694     /**
37695      * 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.
37696      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37697      * @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)
37698      * @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)
37699      * @return {Roo.UpdateManager} The UpdateManager
37700      */
37701     setUrl : function(url, params, loadOnce){
37702         if(this.refreshDelegate){
37703             this.removeListener("activate", this.refreshDelegate);
37704         }
37705         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37706         this.on("activate", this.refreshDelegate);
37707         return this.el.getUpdateManager();
37708     },
37709     
37710     _handleRefresh : function(url, params, loadOnce){
37711         if(!loadOnce || !this.loaded){
37712             var updater = this.el.getUpdateManager();
37713             updater.update(url, params, this._setLoaded.createDelegate(this));
37714         }
37715     },
37716     
37717     _setLoaded : function(){
37718         this.loaded = true;
37719     }, 
37720     
37721     /**
37722      * Returns this panel's id
37723      * @return {String} 
37724      */
37725     getId : function(){
37726         return this.el.id;
37727     },
37728     
37729     /** 
37730      * Returns this panel's element - used by regiosn to add.
37731      * @return {Roo.Element} 
37732      */
37733     getEl : function(){
37734         return this.wrapEl || this.el;
37735     },
37736     
37737    
37738     
37739     adjustForComponents : function(width, height)
37740     {
37741         //Roo.log('adjustForComponents ');
37742         if(this.resizeEl != this.el){
37743             width -= this.el.getFrameWidth('lr');
37744             height -= this.el.getFrameWidth('tb');
37745         }
37746         if(this.toolbar){
37747             var te = this.toolbar.getEl();
37748             te.setWidth(width);
37749             height -= te.getHeight();
37750         }
37751         if(this.footer){
37752             var te = this.footer.getEl();
37753             te.setWidth(width);
37754             height -= te.getHeight();
37755         }
37756         
37757         
37758         if(this.adjustments){
37759             width += this.adjustments[0];
37760             height += this.adjustments[1];
37761         }
37762         return {"width": width, "height": height};
37763     },
37764     
37765     setSize : function(width, height){
37766         if(this.fitToFrame && !this.ignoreResize(width, height)){
37767             if(this.fitContainer && this.resizeEl != this.el){
37768                 this.el.setSize(width, height);
37769             }
37770             var size = this.adjustForComponents(width, height);
37771             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37772             this.fireEvent('resize', this, size.width, size.height);
37773         }
37774     },
37775     
37776     /**
37777      * Returns this panel's title
37778      * @return {String} 
37779      */
37780     getTitle : function(){
37781         
37782         if (typeof(this.title) != 'object') {
37783             return this.title;
37784         }
37785         
37786         var t = '';
37787         for (var k in this.title) {
37788             if (!this.title.hasOwnProperty(k)) {
37789                 continue;
37790             }
37791             
37792             if (k.indexOf('-') >= 0) {
37793                 var s = k.split('-');
37794                 for (var i = 0; i<s.length; i++) {
37795                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37796                 }
37797             } else {
37798                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37799             }
37800         }
37801         return t;
37802     },
37803     
37804     /**
37805      * Set this panel's title
37806      * @param {String} title
37807      */
37808     setTitle : function(title){
37809         this.title = title;
37810         if(this.region){
37811             this.region.updatePanelTitle(this, title);
37812         }
37813     },
37814     
37815     /**
37816      * Returns true is this panel was configured to be closable
37817      * @return {Boolean} 
37818      */
37819     isClosable : function(){
37820         return this.closable;
37821     },
37822     
37823     beforeSlide : function(){
37824         this.el.clip();
37825         this.resizeEl.clip();
37826     },
37827     
37828     afterSlide : function(){
37829         this.el.unclip();
37830         this.resizeEl.unclip();
37831     },
37832     
37833     /**
37834      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37835      *   Will fail silently if the {@link #setUrl} method has not been called.
37836      *   This does not activate the panel, just updates its content.
37837      */
37838     refresh : function(){
37839         if(this.refreshDelegate){
37840            this.loaded = false;
37841            this.refreshDelegate();
37842         }
37843     },
37844     
37845     /**
37846      * Destroys this panel
37847      */
37848     destroy : function(){
37849         this.el.removeAllListeners();
37850         var tempEl = document.createElement("span");
37851         tempEl.appendChild(this.el.dom);
37852         tempEl.innerHTML = "";
37853         this.el.remove();
37854         this.el = null;
37855     },
37856     
37857     /**
37858      * form - if the content panel contains a form - this is a reference to it.
37859      * @type {Roo.form.Form}
37860      */
37861     form : false,
37862     /**
37863      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37864      *    This contains a reference to it.
37865      * @type {Roo.View}
37866      */
37867     view : false,
37868     
37869       /**
37870      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37871      * <pre><code>
37872
37873 layout.addxtype({
37874        xtype : 'Form',
37875        items: [ .... ]
37876    }
37877 );
37878
37879 </code></pre>
37880      * @param {Object} cfg Xtype definition of item to add.
37881      */
37882     
37883     
37884     getChildContainer: function () {
37885         return this.getEl();
37886     }
37887     
37888     
37889     /*
37890         var  ret = new Roo.factory(cfg);
37891         return ret;
37892         
37893         
37894         // add form..
37895         if (cfg.xtype.match(/^Form$/)) {
37896             
37897             var el;
37898             //if (this.footer) {
37899             //    el = this.footer.container.insertSibling(false, 'before');
37900             //} else {
37901                 el = this.el.createChild();
37902             //}
37903
37904             this.form = new  Roo.form.Form(cfg);
37905             
37906             
37907             if ( this.form.allItems.length) {
37908                 this.form.render(el.dom);
37909             }
37910             return this.form;
37911         }
37912         // should only have one of theses..
37913         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37914             // views.. should not be just added - used named prop 'view''
37915             
37916             cfg.el = this.el.appendChild(document.createElement("div"));
37917             // factory?
37918             
37919             var ret = new Roo.factory(cfg);
37920              
37921              ret.render && ret.render(false, ''); // render blank..
37922             this.view = ret;
37923             return ret;
37924         }
37925         return false;
37926     }
37927     \*/
37928 });
37929  
37930 /**
37931  * @class Roo.bootstrap.panel.Grid
37932  * @extends Roo.bootstrap.panel.Content
37933  * @constructor
37934  * Create a new GridPanel.
37935  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37936  * @param {Object} config A the config object
37937   
37938  */
37939
37940
37941
37942 Roo.bootstrap.panel.Grid = function(config)
37943 {
37944     
37945       
37946     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37947         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37948
37949     config.el = this.wrapper;
37950     //this.el = this.wrapper;
37951     
37952       if (config.container) {
37953         // ctor'ed from a Border/panel.grid
37954         
37955         
37956         this.wrapper.setStyle("overflow", "hidden");
37957         this.wrapper.addClass('roo-grid-container');
37958
37959     }
37960     
37961     
37962     if(config.toolbar){
37963         var tool_el = this.wrapper.createChild();    
37964         this.toolbar = Roo.factory(config.toolbar);
37965         var ti = [];
37966         if (config.toolbar.items) {
37967             ti = config.toolbar.items ;
37968             delete config.toolbar.items ;
37969         }
37970         
37971         var nitems = [];
37972         this.toolbar.render(tool_el);
37973         for(var i =0;i < ti.length;i++) {
37974           //  Roo.log(['add child', items[i]]);
37975             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37976         }
37977         this.toolbar.items = nitems;
37978         
37979         delete config.toolbar;
37980     }
37981     
37982     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37983     config.grid.scrollBody = true;;
37984     config.grid.monitorWindowResize = false; // turn off autosizing
37985     config.grid.autoHeight = false;
37986     config.grid.autoWidth = false;
37987     
37988     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37989     
37990     if (config.background) {
37991         // render grid on panel activation (if panel background)
37992         this.on('activate', function(gp) {
37993             if (!gp.grid.rendered) {
37994                 gp.grid.render(this.wrapper);
37995                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37996             }
37997         });
37998             
37999     } else {
38000         this.grid.render(this.wrapper);
38001         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
38002
38003     }
38004     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38005     // ??? needed ??? config.el = this.wrapper;
38006     
38007     
38008     
38009   
38010     // xtype created footer. - not sure if will work as we normally have to render first..
38011     if (this.footer && !this.footer.el && this.footer.xtype) {
38012         
38013         var ctr = this.grid.getView().getFooterPanel(true);
38014         this.footer.dataSource = this.grid.dataSource;
38015         this.footer = Roo.factory(this.footer, Roo);
38016         this.footer.render(ctr);
38017         
38018     }
38019     
38020     
38021     
38022     
38023      
38024 };
38025
38026 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38027     getId : function(){
38028         return this.grid.id;
38029     },
38030     
38031     /**
38032      * Returns the grid for this panel
38033      * @return {Roo.bootstrap.Table} 
38034      */
38035     getGrid : function(){
38036         return this.grid;    
38037     },
38038     
38039     setSize : function(width, height){
38040         if(!this.ignoreResize(width, height)){
38041             var grid = this.grid;
38042             var size = this.adjustForComponents(width, height);
38043             var gridel = grid.getGridEl();
38044             gridel.setSize(size.width, size.height);
38045             /*
38046             var thd = grid.getGridEl().select('thead',true).first();
38047             var tbd = grid.getGridEl().select('tbody', true).first();
38048             if (tbd) {
38049                 tbd.setSize(width, height - thd.getHeight());
38050             }
38051             */
38052             grid.autoSize();
38053         }
38054     },
38055      
38056     
38057     
38058     beforeSlide : function(){
38059         this.grid.getView().scroller.clip();
38060     },
38061     
38062     afterSlide : function(){
38063         this.grid.getView().scroller.unclip();
38064     },
38065     
38066     destroy : function(){
38067         this.grid.destroy();
38068         delete this.grid;
38069         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
38070     }
38071 });
38072
38073 /**
38074  * @class Roo.bootstrap.panel.Nest
38075  * @extends Roo.bootstrap.panel.Content
38076  * @constructor
38077  * Create a new Panel, that can contain a layout.Border.
38078  * 
38079  * 
38080  * @param {Roo.BorderLayout} layout The layout for this panel
38081  * @param {String/Object} config A string to set only the title or a config object
38082  */
38083 Roo.bootstrap.panel.Nest = function(config)
38084 {
38085     // construct with only one argument..
38086     /* FIXME - implement nicer consturctors
38087     if (layout.layout) {
38088         config = layout;
38089         layout = config.layout;
38090         delete config.layout;
38091     }
38092     if (layout.xtype && !layout.getEl) {
38093         // then layout needs constructing..
38094         layout = Roo.factory(layout, Roo);
38095     }
38096     */
38097     
38098     config.el =  config.layout.getEl();
38099     
38100     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38101     
38102     config.layout.monitorWindowResize = false; // turn off autosizing
38103     this.layout = config.layout;
38104     this.layout.getEl().addClass("roo-layout-nested-layout");
38105     this.layout.parent = this;
38106     
38107     
38108     
38109     
38110 };
38111
38112 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38113
38114     setSize : function(width, height){
38115         if(!this.ignoreResize(width, height)){
38116             var size = this.adjustForComponents(width, height);
38117             var el = this.layout.getEl();
38118             if (size.height < 1) {
38119                 el.setWidth(size.width);   
38120             } else {
38121                 el.setSize(size.width, size.height);
38122             }
38123             var touch = el.dom.offsetWidth;
38124             this.layout.layout();
38125             // ie requires a double layout on the first pass
38126             if(Roo.isIE && !this.initialized){
38127                 this.initialized = true;
38128                 this.layout.layout();
38129             }
38130         }
38131     },
38132     
38133     // activate all subpanels if not currently active..
38134     
38135     setActiveState : function(active){
38136         this.active = active;
38137         this.setActiveClass(active);
38138         
38139         if(!active){
38140             this.fireEvent("deactivate", this);
38141             return;
38142         }
38143         
38144         this.fireEvent("activate", this);
38145         // not sure if this should happen before or after..
38146         if (!this.layout) {
38147             return; // should not happen..
38148         }
38149         var reg = false;
38150         for (var r in this.layout.regions) {
38151             reg = this.layout.getRegion(r);
38152             if (reg.getActivePanel()) {
38153                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38154                 reg.setActivePanel(reg.getActivePanel());
38155                 continue;
38156             }
38157             if (!reg.panels.length) {
38158                 continue;
38159             }
38160             reg.showPanel(reg.getPanel(0));
38161         }
38162         
38163         
38164         
38165         
38166     },
38167     
38168     /**
38169      * Returns the nested BorderLayout for this panel
38170      * @return {Roo.BorderLayout} 
38171      */
38172     getLayout : function(){
38173         return this.layout;
38174     },
38175     
38176      /**
38177      * Adds a xtype elements to the layout of the nested panel
38178      * <pre><code>
38179
38180 panel.addxtype({
38181        xtype : 'ContentPanel',
38182        region: 'west',
38183        items: [ .... ]
38184    }
38185 );
38186
38187 panel.addxtype({
38188         xtype : 'NestedLayoutPanel',
38189         region: 'west',
38190         layout: {
38191            center: { },
38192            west: { }   
38193         },
38194         items : [ ... list of content panels or nested layout panels.. ]
38195    }
38196 );
38197 </code></pre>
38198      * @param {Object} cfg Xtype definition of item to add.
38199      */
38200     addxtype : function(cfg) {
38201         return this.layout.addxtype(cfg);
38202     
38203     }
38204 });/*
38205  * Based on:
38206  * Ext JS Library 1.1.1
38207  * Copyright(c) 2006-2007, Ext JS, LLC.
38208  *
38209  * Originally Released Under LGPL - original licence link has changed is not relivant.
38210  *
38211  * Fork - LGPL
38212  * <script type="text/javascript">
38213  */
38214 /**
38215  * @class Roo.TabPanel
38216  * @extends Roo.util.Observable
38217  * A lightweight tab container.
38218  * <br><br>
38219  * Usage:
38220  * <pre><code>
38221 // basic tabs 1, built from existing content
38222 var tabs = new Roo.TabPanel("tabs1");
38223 tabs.addTab("script", "View Script");
38224 tabs.addTab("markup", "View Markup");
38225 tabs.activate("script");
38226
38227 // more advanced tabs, built from javascript
38228 var jtabs = new Roo.TabPanel("jtabs");
38229 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38230
38231 // set up the UpdateManager
38232 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38233 var updater = tab2.getUpdateManager();
38234 updater.setDefaultUrl("ajax1.htm");
38235 tab2.on('activate', updater.refresh, updater, true);
38236
38237 // Use setUrl for Ajax loading
38238 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38239 tab3.setUrl("ajax2.htm", null, true);
38240
38241 // Disabled tab
38242 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38243 tab4.disable();
38244
38245 jtabs.activate("jtabs-1");
38246  * </code></pre>
38247  * @constructor
38248  * Create a new TabPanel.
38249  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38250  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38251  */
38252 Roo.bootstrap.panel.Tabs = function(config){
38253     /**
38254     * The container element for this TabPanel.
38255     * @type Roo.Element
38256     */
38257     this.el = Roo.get(config.el);
38258     delete config.el;
38259     if(config){
38260         if(typeof config == "boolean"){
38261             this.tabPosition = config ? "bottom" : "top";
38262         }else{
38263             Roo.apply(this, config);
38264         }
38265     }
38266     
38267     if(this.tabPosition == "bottom"){
38268         // if tabs are at the bottom = create the body first.
38269         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38270         this.el.addClass("roo-tabs-bottom");
38271     }
38272     // next create the tabs holders
38273     
38274     if (this.tabPosition == "west"){
38275         
38276         var reg = this.region; // fake it..
38277         while (reg) {
38278             if (!reg.mgr.parent) {
38279                 break;
38280             }
38281             reg = reg.mgr.parent.region;
38282         }
38283         Roo.log("got nest?");
38284         Roo.log(reg);
38285         if (reg.mgr.getRegion('west')) {
38286             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38287             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38288             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38289             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38290             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38291         
38292             
38293         }
38294         
38295         
38296     } else {
38297      
38298         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38299         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38300         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38301         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38302     }
38303     
38304     
38305     if(Roo.isIE){
38306         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38307     }
38308     
38309     // finally - if tabs are at the top, then create the body last..
38310     if(this.tabPosition != "bottom"){
38311         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38312          * @type Roo.Element
38313          */
38314         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38315         this.el.addClass("roo-tabs-top");
38316     }
38317     this.items = [];
38318
38319     this.bodyEl.setStyle("position", "relative");
38320
38321     this.active = null;
38322     this.activateDelegate = this.activate.createDelegate(this);
38323
38324     this.addEvents({
38325         /**
38326          * @event tabchange
38327          * Fires when the active tab changes
38328          * @param {Roo.TabPanel} this
38329          * @param {Roo.TabPanelItem} activePanel The new active tab
38330          */
38331         "tabchange": true,
38332         /**
38333          * @event beforetabchange
38334          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38335          * @param {Roo.TabPanel} this
38336          * @param {Object} e Set cancel to true on this object to cancel the tab change
38337          * @param {Roo.TabPanelItem} tab The tab being changed to
38338          */
38339         "beforetabchange" : true
38340     });
38341
38342     Roo.EventManager.onWindowResize(this.onResize, this);
38343     this.cpad = this.el.getPadding("lr");
38344     this.hiddenCount = 0;
38345
38346
38347     // toolbar on the tabbar support...
38348     if (this.toolbar) {
38349         alert("no toolbar support yet");
38350         this.toolbar  = false;
38351         /*
38352         var tcfg = this.toolbar;
38353         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38354         this.toolbar = new Roo.Toolbar(tcfg);
38355         if (Roo.isSafari) {
38356             var tbl = tcfg.container.child('table', true);
38357             tbl.setAttribute('width', '100%');
38358         }
38359         */
38360         
38361     }
38362    
38363
38364
38365     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38366 };
38367
38368 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38369     /*
38370      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38371      */
38372     tabPosition : "top",
38373     /*
38374      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38375      */
38376     currentTabWidth : 0,
38377     /*
38378      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38379      */
38380     minTabWidth : 40,
38381     /*
38382      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38383      */
38384     maxTabWidth : 250,
38385     /*
38386      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38387      */
38388     preferredTabWidth : 175,
38389     /*
38390      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38391      */
38392     resizeTabs : false,
38393     /*
38394      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38395      */
38396     monitorResize : true,
38397     /*
38398      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38399      */
38400     toolbar : false,  // set by caller..
38401     
38402     region : false, /// set by caller
38403     
38404     disableTooltips : true, // not used yet...
38405
38406     /**
38407      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38408      * @param {String} id The id of the div to use <b>or create</b>
38409      * @param {String} text The text for the tab
38410      * @param {String} content (optional) Content to put in the TabPanelItem body
38411      * @param {Boolean} closable (optional) True to create a close icon on the tab
38412      * @return {Roo.TabPanelItem} The created TabPanelItem
38413      */
38414     addTab : function(id, text, content, closable, tpl)
38415     {
38416         var item = new Roo.bootstrap.panel.TabItem({
38417             panel: this,
38418             id : id,
38419             text : text,
38420             closable : closable,
38421             tpl : tpl
38422         });
38423         this.addTabItem(item);
38424         if(content){
38425             item.setContent(content);
38426         }
38427         return item;
38428     },
38429
38430     /**
38431      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38432      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38433      * @return {Roo.TabPanelItem}
38434      */
38435     getTab : function(id){
38436         return this.items[id];
38437     },
38438
38439     /**
38440      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38441      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38442      */
38443     hideTab : function(id){
38444         var t = this.items[id];
38445         if(!t.isHidden()){
38446            t.setHidden(true);
38447            this.hiddenCount++;
38448            this.autoSizeTabs();
38449         }
38450     },
38451
38452     /**
38453      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38454      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38455      */
38456     unhideTab : function(id){
38457         var t = this.items[id];
38458         if(t.isHidden()){
38459            t.setHidden(false);
38460            this.hiddenCount--;
38461            this.autoSizeTabs();
38462         }
38463     },
38464
38465     /**
38466      * Adds an existing {@link Roo.TabPanelItem}.
38467      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38468      */
38469     addTabItem : function(item)
38470     {
38471         this.items[item.id] = item;
38472         this.items.push(item);
38473         this.autoSizeTabs();
38474       //  if(this.resizeTabs){
38475     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38476   //         this.autoSizeTabs();
38477 //        }else{
38478 //            item.autoSize();
38479        // }
38480     },
38481
38482     /**
38483      * Removes a {@link Roo.TabPanelItem}.
38484      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38485      */
38486     removeTab : function(id){
38487         var items = this.items;
38488         var tab = items[id];
38489         if(!tab) { return; }
38490         var index = items.indexOf(tab);
38491         if(this.active == tab && items.length > 1){
38492             var newTab = this.getNextAvailable(index);
38493             if(newTab) {
38494                 newTab.activate();
38495             }
38496         }
38497         this.stripEl.dom.removeChild(tab.pnode.dom);
38498         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38499             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38500         }
38501         items.splice(index, 1);
38502         delete this.items[tab.id];
38503         tab.fireEvent("close", tab);
38504         tab.purgeListeners();
38505         this.autoSizeTabs();
38506     },
38507
38508     getNextAvailable : function(start){
38509         var items = this.items;
38510         var index = start;
38511         // look for a next tab that will slide over to
38512         // replace the one being removed
38513         while(index < items.length){
38514             var item = items[++index];
38515             if(item && !item.isHidden()){
38516                 return item;
38517             }
38518         }
38519         // if one isn't found select the previous tab (on the left)
38520         index = start;
38521         while(index >= 0){
38522             var item = items[--index];
38523             if(item && !item.isHidden()){
38524                 return item;
38525             }
38526         }
38527         return null;
38528     },
38529
38530     /**
38531      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38532      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38533      */
38534     disableTab : function(id){
38535         var tab = this.items[id];
38536         if(tab && this.active != tab){
38537             tab.disable();
38538         }
38539     },
38540
38541     /**
38542      * Enables a {@link Roo.TabPanelItem} that is disabled.
38543      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38544      */
38545     enableTab : function(id){
38546         var tab = this.items[id];
38547         tab.enable();
38548     },
38549
38550     /**
38551      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38552      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38553      * @return {Roo.TabPanelItem} The TabPanelItem.
38554      */
38555     activate : function(id)
38556     {
38557         //Roo.log('activite:'  + id);
38558         
38559         var tab = this.items[id];
38560         if(!tab){
38561             return null;
38562         }
38563         if(tab == this.active || tab.disabled){
38564             return tab;
38565         }
38566         var e = {};
38567         this.fireEvent("beforetabchange", this, e, tab);
38568         if(e.cancel !== true && !tab.disabled){
38569             if(this.active){
38570                 this.active.hide();
38571             }
38572             this.active = this.items[id];
38573             this.active.show();
38574             this.fireEvent("tabchange", this, this.active);
38575         }
38576         return tab;
38577     },
38578
38579     /**
38580      * Gets the active {@link Roo.TabPanelItem}.
38581      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38582      */
38583     getActiveTab : function(){
38584         return this.active;
38585     },
38586
38587     /**
38588      * Updates the tab body element to fit the height of the container element
38589      * for overflow scrolling
38590      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38591      */
38592     syncHeight : function(targetHeight){
38593         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38594         var bm = this.bodyEl.getMargins();
38595         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38596         this.bodyEl.setHeight(newHeight);
38597         return newHeight;
38598     },
38599
38600     onResize : function(){
38601         if(this.monitorResize){
38602             this.autoSizeTabs();
38603         }
38604     },
38605
38606     /**
38607      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38608      */
38609     beginUpdate : function(){
38610         this.updating = true;
38611     },
38612
38613     /**
38614      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38615      */
38616     endUpdate : function(){
38617         this.updating = false;
38618         this.autoSizeTabs();
38619     },
38620
38621     /**
38622      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38623      */
38624     autoSizeTabs : function()
38625     {
38626         var count = this.items.length;
38627         var vcount = count - this.hiddenCount;
38628         
38629         if (vcount < 2) {
38630             this.stripEl.hide();
38631         } else {
38632             this.stripEl.show();
38633         }
38634         
38635         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38636             return;
38637         }
38638         
38639         
38640         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38641         var availWidth = Math.floor(w / vcount);
38642         var b = this.stripBody;
38643         if(b.getWidth() > w){
38644             var tabs = this.items;
38645             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38646             if(availWidth < this.minTabWidth){
38647                 /*if(!this.sleft){    // incomplete scrolling code
38648                     this.createScrollButtons();
38649                 }
38650                 this.showScroll();
38651                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38652             }
38653         }else{
38654             if(this.currentTabWidth < this.preferredTabWidth){
38655                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38656             }
38657         }
38658     },
38659
38660     /**
38661      * Returns the number of tabs in this TabPanel.
38662      * @return {Number}
38663      */
38664      getCount : function(){
38665          return this.items.length;
38666      },
38667
38668     /**
38669      * Resizes all the tabs to the passed width
38670      * @param {Number} The new width
38671      */
38672     setTabWidth : function(width){
38673         this.currentTabWidth = width;
38674         for(var i = 0, len = this.items.length; i < len; i++) {
38675                 if(!this.items[i].isHidden()) {
38676                 this.items[i].setWidth(width);
38677             }
38678         }
38679     },
38680
38681     /**
38682      * Destroys this TabPanel
38683      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38684      */
38685     destroy : function(removeEl){
38686         Roo.EventManager.removeResizeListener(this.onResize, this);
38687         for(var i = 0, len = this.items.length; i < len; i++){
38688             this.items[i].purgeListeners();
38689         }
38690         if(removeEl === true){
38691             this.el.update("");
38692             this.el.remove();
38693         }
38694     },
38695     
38696     createStrip : function(container)
38697     {
38698         var strip = document.createElement("nav");
38699         strip.className = Roo.bootstrap.version == 4 ?
38700             "navbar-light bg-light" : 
38701             "navbar navbar-default"; //"x-tabs-wrap";
38702         container.appendChild(strip);
38703         return strip;
38704     },
38705     
38706     createStripList : function(strip)
38707     {
38708         // div wrapper for retard IE
38709         // returns the "tr" element.
38710         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38711         //'<div class="x-tabs-strip-wrap">'+
38712           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38713           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38714         return strip.firstChild; //.firstChild.firstChild.firstChild;
38715     },
38716     createBody : function(container)
38717     {
38718         var body = document.createElement("div");
38719         Roo.id(body, "tab-body");
38720         //Roo.fly(body).addClass("x-tabs-body");
38721         Roo.fly(body).addClass("tab-content");
38722         container.appendChild(body);
38723         return body;
38724     },
38725     createItemBody :function(bodyEl, id){
38726         var body = Roo.getDom(id);
38727         if(!body){
38728             body = document.createElement("div");
38729             body.id = id;
38730         }
38731         //Roo.fly(body).addClass("x-tabs-item-body");
38732         Roo.fly(body).addClass("tab-pane");
38733          bodyEl.insertBefore(body, bodyEl.firstChild);
38734         return body;
38735     },
38736     /** @private */
38737     createStripElements :  function(stripEl, text, closable, tpl)
38738     {
38739         var td = document.createElement("li"); // was td..
38740         td.className = 'nav-item';
38741         
38742         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38743         
38744         
38745         stripEl.appendChild(td);
38746         /*if(closable){
38747             td.className = "x-tabs-closable";
38748             if(!this.closeTpl){
38749                 this.closeTpl = new Roo.Template(
38750                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38751                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38752                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38753                 );
38754             }
38755             var el = this.closeTpl.overwrite(td, {"text": text});
38756             var close = el.getElementsByTagName("div")[0];
38757             var inner = el.getElementsByTagName("em")[0];
38758             return {"el": el, "close": close, "inner": inner};
38759         } else {
38760         */
38761         // not sure what this is..
38762 //            if(!this.tabTpl){
38763                 //this.tabTpl = new Roo.Template(
38764                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38765                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38766                 //);
38767 //                this.tabTpl = new Roo.Template(
38768 //                   '<a href="#">' +
38769 //                   '<span unselectable="on"' +
38770 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38771 //                            ' >{text}</span></a>'
38772 //                );
38773 //                
38774 //            }
38775
38776
38777             var template = tpl || this.tabTpl || false;
38778             
38779             if(!template){
38780                 template =  new Roo.Template(
38781                         Roo.bootstrap.version == 4 ? 
38782                             (
38783                                 '<a class="nav-link" href="#" unselectable="on"' +
38784                                      (this.disableTooltips ? '' : ' title="{text}"') +
38785                                      ' >{text}</a>'
38786                             ) : (
38787                                 '<a class="nav-link" href="#">' +
38788                                 '<span unselectable="on"' +
38789                                          (this.disableTooltips ? '' : ' title="{text}"') +
38790                                     ' >{text}</span></a>'
38791                             )
38792                 );
38793             }
38794             
38795             switch (typeof(template)) {
38796                 case 'object' :
38797                     break;
38798                 case 'string' :
38799                     template = new Roo.Template(template);
38800                     break;
38801                 default :
38802                     break;
38803             }
38804             
38805             var el = template.overwrite(td, {"text": text});
38806             
38807             var inner = el.getElementsByTagName("span")[0];
38808             
38809             return {"el": el, "inner": inner};
38810             
38811     }
38812         
38813     
38814 });
38815
38816 /**
38817  * @class Roo.TabPanelItem
38818  * @extends Roo.util.Observable
38819  * Represents an individual item (tab plus body) in a TabPanel.
38820  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38821  * @param {String} id The id of this TabPanelItem
38822  * @param {String} text The text for the tab of this TabPanelItem
38823  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38824  */
38825 Roo.bootstrap.panel.TabItem = function(config){
38826     /**
38827      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38828      * @type Roo.TabPanel
38829      */
38830     this.tabPanel = config.panel;
38831     /**
38832      * The id for this TabPanelItem
38833      * @type String
38834      */
38835     this.id = config.id;
38836     /** @private */
38837     this.disabled = false;
38838     /** @private */
38839     this.text = config.text;
38840     /** @private */
38841     this.loaded = false;
38842     this.closable = config.closable;
38843
38844     /**
38845      * The body element for this TabPanelItem.
38846      * @type Roo.Element
38847      */
38848     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38849     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38850     this.bodyEl.setStyle("display", "block");
38851     this.bodyEl.setStyle("zoom", "1");
38852     //this.hideAction();
38853
38854     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38855     /** @private */
38856     this.el = Roo.get(els.el);
38857     this.inner = Roo.get(els.inner, true);
38858      this.textEl = Roo.bootstrap.version == 4 ?
38859         this.el : Roo.get(this.el.dom.firstChild, true);
38860
38861     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38862     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38863
38864     
38865 //    this.el.on("mousedown", this.onTabMouseDown, this);
38866     this.el.on("click", this.onTabClick, this);
38867     /** @private */
38868     if(config.closable){
38869         var c = Roo.get(els.close, true);
38870         c.dom.title = this.closeText;
38871         c.addClassOnOver("close-over");
38872         c.on("click", this.closeClick, this);
38873      }
38874
38875     this.addEvents({
38876          /**
38877          * @event activate
38878          * Fires when this tab becomes the active tab.
38879          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38880          * @param {Roo.TabPanelItem} this
38881          */
38882         "activate": true,
38883         /**
38884          * @event beforeclose
38885          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38886          * @param {Roo.TabPanelItem} this
38887          * @param {Object} e Set cancel to true on this object to cancel the close.
38888          */
38889         "beforeclose": true,
38890         /**
38891          * @event close
38892          * Fires when this tab is closed.
38893          * @param {Roo.TabPanelItem} this
38894          */
38895          "close": true,
38896         /**
38897          * @event deactivate
38898          * Fires when this tab is no longer the active tab.
38899          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38900          * @param {Roo.TabPanelItem} this
38901          */
38902          "deactivate" : true
38903     });
38904     this.hidden = false;
38905
38906     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38907 };
38908
38909 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38910            {
38911     purgeListeners : function(){
38912        Roo.util.Observable.prototype.purgeListeners.call(this);
38913        this.el.removeAllListeners();
38914     },
38915     /**
38916      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38917      */
38918     show : function(){
38919         this.status_node.addClass("active");
38920         this.showAction();
38921         if(Roo.isOpera){
38922             this.tabPanel.stripWrap.repaint();
38923         }
38924         this.fireEvent("activate", this.tabPanel, this);
38925     },
38926
38927     /**
38928      * Returns true if this tab is the active tab.
38929      * @return {Boolean}
38930      */
38931     isActive : function(){
38932         return this.tabPanel.getActiveTab() == this;
38933     },
38934
38935     /**
38936      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38937      */
38938     hide : function(){
38939         this.status_node.removeClass("active");
38940         this.hideAction();
38941         this.fireEvent("deactivate", this.tabPanel, this);
38942     },
38943
38944     hideAction : function(){
38945         this.bodyEl.hide();
38946         this.bodyEl.setStyle("position", "absolute");
38947         this.bodyEl.setLeft("-20000px");
38948         this.bodyEl.setTop("-20000px");
38949     },
38950
38951     showAction : function(){
38952         this.bodyEl.setStyle("position", "relative");
38953         this.bodyEl.setTop("");
38954         this.bodyEl.setLeft("");
38955         this.bodyEl.show();
38956     },
38957
38958     /**
38959      * Set the tooltip for the tab.
38960      * @param {String} tooltip The tab's tooltip
38961      */
38962     setTooltip : function(text){
38963         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38964             this.textEl.dom.qtip = text;
38965             this.textEl.dom.removeAttribute('title');
38966         }else{
38967             this.textEl.dom.title = text;
38968         }
38969     },
38970
38971     onTabClick : function(e){
38972         e.preventDefault();
38973         this.tabPanel.activate(this.id);
38974     },
38975
38976     onTabMouseDown : function(e){
38977         e.preventDefault();
38978         this.tabPanel.activate(this.id);
38979     },
38980 /*
38981     getWidth : function(){
38982         return this.inner.getWidth();
38983     },
38984
38985     setWidth : function(width){
38986         var iwidth = width - this.linode.getPadding("lr");
38987         this.inner.setWidth(iwidth);
38988         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38989         this.linode.setWidth(width);
38990     },
38991 */
38992     /**
38993      * Show or hide the tab
38994      * @param {Boolean} hidden True to hide or false to show.
38995      */
38996     setHidden : function(hidden){
38997         this.hidden = hidden;
38998         this.linode.setStyle("display", hidden ? "none" : "");
38999     },
39000
39001     /**
39002      * Returns true if this tab is "hidden"
39003      * @return {Boolean}
39004      */
39005     isHidden : function(){
39006         return this.hidden;
39007     },
39008
39009     /**
39010      * Returns the text for this tab
39011      * @return {String}
39012      */
39013     getText : function(){
39014         return this.text;
39015     },
39016     /*
39017     autoSize : function(){
39018         //this.el.beginMeasure();
39019         this.textEl.setWidth(1);
39020         /*
39021          *  #2804 [new] Tabs in Roojs
39022          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39023          */
39024         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39025         //this.el.endMeasure();
39026     //},
39027
39028     /**
39029      * Sets the text for the tab (Note: this also sets the tooltip text)
39030      * @param {String} text The tab's text and tooltip
39031      */
39032     setText : function(text){
39033         this.text = text;
39034         this.textEl.update(text);
39035         this.setTooltip(text);
39036         //if(!this.tabPanel.resizeTabs){
39037         //    this.autoSize();
39038         //}
39039     },
39040     /**
39041      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39042      */
39043     activate : function(){
39044         this.tabPanel.activate(this.id);
39045     },
39046
39047     /**
39048      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39049      */
39050     disable : function(){
39051         if(this.tabPanel.active != this){
39052             this.disabled = true;
39053             this.status_node.addClass("disabled");
39054         }
39055     },
39056
39057     /**
39058      * Enables this TabPanelItem if it was previously disabled.
39059      */
39060     enable : function(){
39061         this.disabled = false;
39062         this.status_node.removeClass("disabled");
39063     },
39064
39065     /**
39066      * Sets the content for this TabPanelItem.
39067      * @param {String} content The content
39068      * @param {Boolean} loadScripts true to look for and load scripts
39069      */
39070     setContent : function(content, loadScripts){
39071         this.bodyEl.update(content, loadScripts);
39072     },
39073
39074     /**
39075      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39076      * @return {Roo.UpdateManager} The UpdateManager
39077      */
39078     getUpdateManager : function(){
39079         return this.bodyEl.getUpdateManager();
39080     },
39081
39082     /**
39083      * Set a URL to be used to load the content for this TabPanelItem.
39084      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39085      * @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)
39086      * @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)
39087      * @return {Roo.UpdateManager} The UpdateManager
39088      */
39089     setUrl : function(url, params, loadOnce){
39090         if(this.refreshDelegate){
39091             this.un('activate', this.refreshDelegate);
39092         }
39093         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39094         this.on("activate", this.refreshDelegate);
39095         return this.bodyEl.getUpdateManager();
39096     },
39097
39098     /** @private */
39099     _handleRefresh : function(url, params, loadOnce){
39100         if(!loadOnce || !this.loaded){
39101             var updater = this.bodyEl.getUpdateManager();
39102             updater.update(url, params, this._setLoaded.createDelegate(this));
39103         }
39104     },
39105
39106     /**
39107      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
39108      *   Will fail silently if the setUrl method has not been called.
39109      *   This does not activate the panel, just updates its content.
39110      */
39111     refresh : function(){
39112         if(this.refreshDelegate){
39113            this.loaded = false;
39114            this.refreshDelegate();
39115         }
39116     },
39117
39118     /** @private */
39119     _setLoaded : function(){
39120         this.loaded = true;
39121     },
39122
39123     /** @private */
39124     closeClick : function(e){
39125         var o = {};
39126         e.stopEvent();
39127         this.fireEvent("beforeclose", this, o);
39128         if(o.cancel !== true){
39129             this.tabPanel.removeTab(this.id);
39130         }
39131     },
39132     /**
39133      * The text displayed in the tooltip for the close icon.
39134      * @type String
39135      */
39136     closeText : "Close this tab"
39137 });
39138 /**
39139 *    This script refer to:
39140 *    Title: International Telephone Input
39141 *    Author: Jack O'Connor
39142 *    Code version:  v12.1.12
39143 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39144 **/
39145
39146 Roo.bootstrap.PhoneInputData = function() {
39147     var d = [
39148       [
39149         "Afghanistan (‫افغانستان‬‎)",
39150         "af",
39151         "93"
39152       ],
39153       [
39154         "Albania (Shqipëri)",
39155         "al",
39156         "355"
39157       ],
39158       [
39159         "Algeria (‫الجزائر‬‎)",
39160         "dz",
39161         "213"
39162       ],
39163       [
39164         "American Samoa",
39165         "as",
39166         "1684"
39167       ],
39168       [
39169         "Andorra",
39170         "ad",
39171         "376"
39172       ],
39173       [
39174         "Angola",
39175         "ao",
39176         "244"
39177       ],
39178       [
39179         "Anguilla",
39180         "ai",
39181         "1264"
39182       ],
39183       [
39184         "Antigua and Barbuda",
39185         "ag",
39186         "1268"
39187       ],
39188       [
39189         "Argentina",
39190         "ar",
39191         "54"
39192       ],
39193       [
39194         "Armenia (Հայաստան)",
39195         "am",
39196         "374"
39197       ],
39198       [
39199         "Aruba",
39200         "aw",
39201         "297"
39202       ],
39203       [
39204         "Australia",
39205         "au",
39206         "61",
39207         0
39208       ],
39209       [
39210         "Austria (Österreich)",
39211         "at",
39212         "43"
39213       ],
39214       [
39215         "Azerbaijan (Azərbaycan)",
39216         "az",
39217         "994"
39218       ],
39219       [
39220         "Bahamas",
39221         "bs",
39222         "1242"
39223       ],
39224       [
39225         "Bahrain (‫البحرين‬‎)",
39226         "bh",
39227         "973"
39228       ],
39229       [
39230         "Bangladesh (বাংলাদেশ)",
39231         "bd",
39232         "880"
39233       ],
39234       [
39235         "Barbados",
39236         "bb",
39237         "1246"
39238       ],
39239       [
39240         "Belarus (Беларусь)",
39241         "by",
39242         "375"
39243       ],
39244       [
39245         "Belgium (België)",
39246         "be",
39247         "32"
39248       ],
39249       [
39250         "Belize",
39251         "bz",
39252         "501"
39253       ],
39254       [
39255         "Benin (Bénin)",
39256         "bj",
39257         "229"
39258       ],
39259       [
39260         "Bermuda",
39261         "bm",
39262         "1441"
39263       ],
39264       [
39265         "Bhutan (འབྲུག)",
39266         "bt",
39267         "975"
39268       ],
39269       [
39270         "Bolivia",
39271         "bo",
39272         "591"
39273       ],
39274       [
39275         "Bosnia and Herzegovina (Босна и Херцеговина)",
39276         "ba",
39277         "387"
39278       ],
39279       [
39280         "Botswana",
39281         "bw",
39282         "267"
39283       ],
39284       [
39285         "Brazil (Brasil)",
39286         "br",
39287         "55"
39288       ],
39289       [
39290         "British Indian Ocean Territory",
39291         "io",
39292         "246"
39293       ],
39294       [
39295         "British Virgin Islands",
39296         "vg",
39297         "1284"
39298       ],
39299       [
39300         "Brunei",
39301         "bn",
39302         "673"
39303       ],
39304       [
39305         "Bulgaria (България)",
39306         "bg",
39307         "359"
39308       ],
39309       [
39310         "Burkina Faso",
39311         "bf",
39312         "226"
39313       ],
39314       [
39315         "Burundi (Uburundi)",
39316         "bi",
39317         "257"
39318       ],
39319       [
39320         "Cambodia (កម្ពុជា)",
39321         "kh",
39322         "855"
39323       ],
39324       [
39325         "Cameroon (Cameroun)",
39326         "cm",
39327         "237"
39328       ],
39329       [
39330         "Canada",
39331         "ca",
39332         "1",
39333         1,
39334         ["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"]
39335       ],
39336       [
39337         "Cape Verde (Kabu Verdi)",
39338         "cv",
39339         "238"
39340       ],
39341       [
39342         "Caribbean Netherlands",
39343         "bq",
39344         "599",
39345         1
39346       ],
39347       [
39348         "Cayman Islands",
39349         "ky",
39350         "1345"
39351       ],
39352       [
39353         "Central African Republic (République centrafricaine)",
39354         "cf",
39355         "236"
39356       ],
39357       [
39358         "Chad (Tchad)",
39359         "td",
39360         "235"
39361       ],
39362       [
39363         "Chile",
39364         "cl",
39365         "56"
39366       ],
39367       [
39368         "China (中国)",
39369         "cn",
39370         "86"
39371       ],
39372       [
39373         "Christmas Island",
39374         "cx",
39375         "61",
39376         2
39377       ],
39378       [
39379         "Cocos (Keeling) Islands",
39380         "cc",
39381         "61",
39382         1
39383       ],
39384       [
39385         "Colombia",
39386         "co",
39387         "57"
39388       ],
39389       [
39390         "Comoros (‫جزر القمر‬‎)",
39391         "km",
39392         "269"
39393       ],
39394       [
39395         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39396         "cd",
39397         "243"
39398       ],
39399       [
39400         "Congo (Republic) (Congo-Brazzaville)",
39401         "cg",
39402         "242"
39403       ],
39404       [
39405         "Cook Islands",
39406         "ck",
39407         "682"
39408       ],
39409       [
39410         "Costa Rica",
39411         "cr",
39412         "506"
39413       ],
39414       [
39415         "Côte d’Ivoire",
39416         "ci",
39417         "225"
39418       ],
39419       [
39420         "Croatia (Hrvatska)",
39421         "hr",
39422         "385"
39423       ],
39424       [
39425         "Cuba",
39426         "cu",
39427         "53"
39428       ],
39429       [
39430         "Curaçao",
39431         "cw",
39432         "599",
39433         0
39434       ],
39435       [
39436         "Cyprus (Κύπρος)",
39437         "cy",
39438         "357"
39439       ],
39440       [
39441         "Czech Republic (Česká republika)",
39442         "cz",
39443         "420"
39444       ],
39445       [
39446         "Denmark (Danmark)",
39447         "dk",
39448         "45"
39449       ],
39450       [
39451         "Djibouti",
39452         "dj",
39453         "253"
39454       ],
39455       [
39456         "Dominica",
39457         "dm",
39458         "1767"
39459       ],
39460       [
39461         "Dominican Republic (República Dominicana)",
39462         "do",
39463         "1",
39464         2,
39465         ["809", "829", "849"]
39466       ],
39467       [
39468         "Ecuador",
39469         "ec",
39470         "593"
39471       ],
39472       [
39473         "Egypt (‫مصر‬‎)",
39474         "eg",
39475         "20"
39476       ],
39477       [
39478         "El Salvador",
39479         "sv",
39480         "503"
39481       ],
39482       [
39483         "Equatorial Guinea (Guinea Ecuatorial)",
39484         "gq",
39485         "240"
39486       ],
39487       [
39488         "Eritrea",
39489         "er",
39490         "291"
39491       ],
39492       [
39493         "Estonia (Eesti)",
39494         "ee",
39495         "372"
39496       ],
39497       [
39498         "Ethiopia",
39499         "et",
39500         "251"
39501       ],
39502       [
39503         "Falkland Islands (Islas Malvinas)",
39504         "fk",
39505         "500"
39506       ],
39507       [
39508         "Faroe Islands (Føroyar)",
39509         "fo",
39510         "298"
39511       ],
39512       [
39513         "Fiji",
39514         "fj",
39515         "679"
39516       ],
39517       [
39518         "Finland (Suomi)",
39519         "fi",
39520         "358",
39521         0
39522       ],
39523       [
39524         "France",
39525         "fr",
39526         "33"
39527       ],
39528       [
39529         "French Guiana (Guyane française)",
39530         "gf",
39531         "594"
39532       ],
39533       [
39534         "French Polynesia (Polynésie française)",
39535         "pf",
39536         "689"
39537       ],
39538       [
39539         "Gabon",
39540         "ga",
39541         "241"
39542       ],
39543       [
39544         "Gambia",
39545         "gm",
39546         "220"
39547       ],
39548       [
39549         "Georgia (საქართველო)",
39550         "ge",
39551         "995"
39552       ],
39553       [
39554         "Germany (Deutschland)",
39555         "de",
39556         "49"
39557       ],
39558       [
39559         "Ghana (Gaana)",
39560         "gh",
39561         "233"
39562       ],
39563       [
39564         "Gibraltar",
39565         "gi",
39566         "350"
39567       ],
39568       [
39569         "Greece (Ελλάδα)",
39570         "gr",
39571         "30"
39572       ],
39573       [
39574         "Greenland (Kalaallit Nunaat)",
39575         "gl",
39576         "299"
39577       ],
39578       [
39579         "Grenada",
39580         "gd",
39581         "1473"
39582       ],
39583       [
39584         "Guadeloupe",
39585         "gp",
39586         "590",
39587         0
39588       ],
39589       [
39590         "Guam",
39591         "gu",
39592         "1671"
39593       ],
39594       [
39595         "Guatemala",
39596         "gt",
39597         "502"
39598       ],
39599       [
39600         "Guernsey",
39601         "gg",
39602         "44",
39603         1
39604       ],
39605       [
39606         "Guinea (Guinée)",
39607         "gn",
39608         "224"
39609       ],
39610       [
39611         "Guinea-Bissau (Guiné Bissau)",
39612         "gw",
39613         "245"
39614       ],
39615       [
39616         "Guyana",
39617         "gy",
39618         "592"
39619       ],
39620       [
39621         "Haiti",
39622         "ht",
39623         "509"
39624       ],
39625       [
39626         "Honduras",
39627         "hn",
39628         "504"
39629       ],
39630       [
39631         "Hong Kong (香港)",
39632         "hk",
39633         "852"
39634       ],
39635       [
39636         "Hungary (Magyarország)",
39637         "hu",
39638         "36"
39639       ],
39640       [
39641         "Iceland (Ísland)",
39642         "is",
39643         "354"
39644       ],
39645       [
39646         "India (भारत)",
39647         "in",
39648         "91"
39649       ],
39650       [
39651         "Indonesia",
39652         "id",
39653         "62"
39654       ],
39655       [
39656         "Iran (‫ایران‬‎)",
39657         "ir",
39658         "98"
39659       ],
39660       [
39661         "Iraq (‫العراق‬‎)",
39662         "iq",
39663         "964"
39664       ],
39665       [
39666         "Ireland",
39667         "ie",
39668         "353"
39669       ],
39670       [
39671         "Isle of Man",
39672         "im",
39673         "44",
39674         2
39675       ],
39676       [
39677         "Israel (‫ישראל‬‎)",
39678         "il",
39679         "972"
39680       ],
39681       [
39682         "Italy (Italia)",
39683         "it",
39684         "39",
39685         0
39686       ],
39687       [
39688         "Jamaica",
39689         "jm",
39690         "1876"
39691       ],
39692       [
39693         "Japan (日本)",
39694         "jp",
39695         "81"
39696       ],
39697       [
39698         "Jersey",
39699         "je",
39700         "44",
39701         3
39702       ],
39703       [
39704         "Jordan (‫الأردن‬‎)",
39705         "jo",
39706         "962"
39707       ],
39708       [
39709         "Kazakhstan (Казахстан)",
39710         "kz",
39711         "7",
39712         1
39713       ],
39714       [
39715         "Kenya",
39716         "ke",
39717         "254"
39718       ],
39719       [
39720         "Kiribati",
39721         "ki",
39722         "686"
39723       ],
39724       [
39725         "Kosovo",
39726         "xk",
39727         "383"
39728       ],
39729       [
39730         "Kuwait (‫الكويت‬‎)",
39731         "kw",
39732         "965"
39733       ],
39734       [
39735         "Kyrgyzstan (Кыргызстан)",
39736         "kg",
39737         "996"
39738       ],
39739       [
39740         "Laos (ລາວ)",
39741         "la",
39742         "856"
39743       ],
39744       [
39745         "Latvia (Latvija)",
39746         "lv",
39747         "371"
39748       ],
39749       [
39750         "Lebanon (‫لبنان‬‎)",
39751         "lb",
39752         "961"
39753       ],
39754       [
39755         "Lesotho",
39756         "ls",
39757         "266"
39758       ],
39759       [
39760         "Liberia",
39761         "lr",
39762         "231"
39763       ],
39764       [
39765         "Libya (‫ليبيا‬‎)",
39766         "ly",
39767         "218"
39768       ],
39769       [
39770         "Liechtenstein",
39771         "li",
39772         "423"
39773       ],
39774       [
39775         "Lithuania (Lietuva)",
39776         "lt",
39777         "370"
39778       ],
39779       [
39780         "Luxembourg",
39781         "lu",
39782         "352"
39783       ],
39784       [
39785         "Macau (澳門)",
39786         "mo",
39787         "853"
39788       ],
39789       [
39790         "Macedonia (FYROM) (Македонија)",
39791         "mk",
39792         "389"
39793       ],
39794       [
39795         "Madagascar (Madagasikara)",
39796         "mg",
39797         "261"
39798       ],
39799       [
39800         "Malawi",
39801         "mw",
39802         "265"
39803       ],
39804       [
39805         "Malaysia",
39806         "my",
39807         "60"
39808       ],
39809       [
39810         "Maldives",
39811         "mv",
39812         "960"
39813       ],
39814       [
39815         "Mali",
39816         "ml",
39817         "223"
39818       ],
39819       [
39820         "Malta",
39821         "mt",
39822         "356"
39823       ],
39824       [
39825         "Marshall Islands",
39826         "mh",
39827         "692"
39828       ],
39829       [
39830         "Martinique",
39831         "mq",
39832         "596"
39833       ],
39834       [
39835         "Mauritania (‫موريتانيا‬‎)",
39836         "mr",
39837         "222"
39838       ],
39839       [
39840         "Mauritius (Moris)",
39841         "mu",
39842         "230"
39843       ],
39844       [
39845         "Mayotte",
39846         "yt",
39847         "262",
39848         1
39849       ],
39850       [
39851         "Mexico (México)",
39852         "mx",
39853         "52"
39854       ],
39855       [
39856         "Micronesia",
39857         "fm",
39858         "691"
39859       ],
39860       [
39861         "Moldova (Republica Moldova)",
39862         "md",
39863         "373"
39864       ],
39865       [
39866         "Monaco",
39867         "mc",
39868         "377"
39869       ],
39870       [
39871         "Mongolia (Монгол)",
39872         "mn",
39873         "976"
39874       ],
39875       [
39876         "Montenegro (Crna Gora)",
39877         "me",
39878         "382"
39879       ],
39880       [
39881         "Montserrat",
39882         "ms",
39883         "1664"
39884       ],
39885       [
39886         "Morocco (‫المغرب‬‎)",
39887         "ma",
39888         "212",
39889         0
39890       ],
39891       [
39892         "Mozambique (Moçambique)",
39893         "mz",
39894         "258"
39895       ],
39896       [
39897         "Myanmar (Burma) (မြန်မာ)",
39898         "mm",
39899         "95"
39900       ],
39901       [
39902         "Namibia (Namibië)",
39903         "na",
39904         "264"
39905       ],
39906       [
39907         "Nauru",
39908         "nr",
39909         "674"
39910       ],
39911       [
39912         "Nepal (नेपाल)",
39913         "np",
39914         "977"
39915       ],
39916       [
39917         "Netherlands (Nederland)",
39918         "nl",
39919         "31"
39920       ],
39921       [
39922         "New Caledonia (Nouvelle-Calédonie)",
39923         "nc",
39924         "687"
39925       ],
39926       [
39927         "New Zealand",
39928         "nz",
39929         "64"
39930       ],
39931       [
39932         "Nicaragua",
39933         "ni",
39934         "505"
39935       ],
39936       [
39937         "Niger (Nijar)",
39938         "ne",
39939         "227"
39940       ],
39941       [
39942         "Nigeria",
39943         "ng",
39944         "234"
39945       ],
39946       [
39947         "Niue",
39948         "nu",
39949         "683"
39950       ],
39951       [
39952         "Norfolk Island",
39953         "nf",
39954         "672"
39955       ],
39956       [
39957         "North Korea (조선 민주주의 인민 공화국)",
39958         "kp",
39959         "850"
39960       ],
39961       [
39962         "Northern Mariana Islands",
39963         "mp",
39964         "1670"
39965       ],
39966       [
39967         "Norway (Norge)",
39968         "no",
39969         "47",
39970         0
39971       ],
39972       [
39973         "Oman (‫عُمان‬‎)",
39974         "om",
39975         "968"
39976       ],
39977       [
39978         "Pakistan (‫پاکستان‬‎)",
39979         "pk",
39980         "92"
39981       ],
39982       [
39983         "Palau",
39984         "pw",
39985         "680"
39986       ],
39987       [
39988         "Palestine (‫فلسطين‬‎)",
39989         "ps",
39990         "970"
39991       ],
39992       [
39993         "Panama (Panamá)",
39994         "pa",
39995         "507"
39996       ],
39997       [
39998         "Papua New Guinea",
39999         "pg",
40000         "675"
40001       ],
40002       [
40003         "Paraguay",
40004         "py",
40005         "595"
40006       ],
40007       [
40008         "Peru (Perú)",
40009         "pe",
40010         "51"
40011       ],
40012       [
40013         "Philippines",
40014         "ph",
40015         "63"
40016       ],
40017       [
40018         "Poland (Polska)",
40019         "pl",
40020         "48"
40021       ],
40022       [
40023         "Portugal",
40024         "pt",
40025         "351"
40026       ],
40027       [
40028         "Puerto Rico",
40029         "pr",
40030         "1",
40031         3,
40032         ["787", "939"]
40033       ],
40034       [
40035         "Qatar (‫قطر‬‎)",
40036         "qa",
40037         "974"
40038       ],
40039       [
40040         "Réunion (La Réunion)",
40041         "re",
40042         "262",
40043         0
40044       ],
40045       [
40046         "Romania (România)",
40047         "ro",
40048         "40"
40049       ],
40050       [
40051         "Russia (Россия)",
40052         "ru",
40053         "7",
40054         0
40055       ],
40056       [
40057         "Rwanda",
40058         "rw",
40059         "250"
40060       ],
40061       [
40062         "Saint Barthélemy",
40063         "bl",
40064         "590",
40065         1
40066       ],
40067       [
40068         "Saint Helena",
40069         "sh",
40070         "290"
40071       ],
40072       [
40073         "Saint Kitts and Nevis",
40074         "kn",
40075         "1869"
40076       ],
40077       [
40078         "Saint Lucia",
40079         "lc",
40080         "1758"
40081       ],
40082       [
40083         "Saint Martin (Saint-Martin (partie française))",
40084         "mf",
40085         "590",
40086         2
40087       ],
40088       [
40089         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40090         "pm",
40091         "508"
40092       ],
40093       [
40094         "Saint Vincent and the Grenadines",
40095         "vc",
40096         "1784"
40097       ],
40098       [
40099         "Samoa",
40100         "ws",
40101         "685"
40102       ],
40103       [
40104         "San Marino",
40105         "sm",
40106         "378"
40107       ],
40108       [
40109         "São Tomé and Príncipe (São Tomé e Príncipe)",
40110         "st",
40111         "239"
40112       ],
40113       [
40114         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
40115         "sa",
40116         "966"
40117       ],
40118       [
40119         "Senegal (Sénégal)",
40120         "sn",
40121         "221"
40122       ],
40123       [
40124         "Serbia (Србија)",
40125         "rs",
40126         "381"
40127       ],
40128       [
40129         "Seychelles",
40130         "sc",
40131         "248"
40132       ],
40133       [
40134         "Sierra Leone",
40135         "sl",
40136         "232"
40137       ],
40138       [
40139         "Singapore",
40140         "sg",
40141         "65"
40142       ],
40143       [
40144         "Sint Maarten",
40145         "sx",
40146         "1721"
40147       ],
40148       [
40149         "Slovakia (Slovensko)",
40150         "sk",
40151         "421"
40152       ],
40153       [
40154         "Slovenia (Slovenija)",
40155         "si",
40156         "386"
40157       ],
40158       [
40159         "Solomon Islands",
40160         "sb",
40161         "677"
40162       ],
40163       [
40164         "Somalia (Soomaaliya)",
40165         "so",
40166         "252"
40167       ],
40168       [
40169         "South Africa",
40170         "za",
40171         "27"
40172       ],
40173       [
40174         "South Korea (대한민국)",
40175         "kr",
40176         "82"
40177       ],
40178       [
40179         "South Sudan (‫جنوب السودان‬‎)",
40180         "ss",
40181         "211"
40182       ],
40183       [
40184         "Spain (España)",
40185         "es",
40186         "34"
40187       ],
40188       [
40189         "Sri Lanka (ශ්‍රී ලංකාව)",
40190         "lk",
40191         "94"
40192       ],
40193       [
40194         "Sudan (‫السودان‬‎)",
40195         "sd",
40196         "249"
40197       ],
40198       [
40199         "Suriname",
40200         "sr",
40201         "597"
40202       ],
40203       [
40204         "Svalbard and Jan Mayen",
40205         "sj",
40206         "47",
40207         1
40208       ],
40209       [
40210         "Swaziland",
40211         "sz",
40212         "268"
40213       ],
40214       [
40215         "Sweden (Sverige)",
40216         "se",
40217         "46"
40218       ],
40219       [
40220         "Switzerland (Schweiz)",
40221         "ch",
40222         "41"
40223       ],
40224       [
40225         "Syria (‫سوريا‬‎)",
40226         "sy",
40227         "963"
40228       ],
40229       [
40230         "Taiwan (台灣)",
40231         "tw",
40232         "886"
40233       ],
40234       [
40235         "Tajikistan",
40236         "tj",
40237         "992"
40238       ],
40239       [
40240         "Tanzania",
40241         "tz",
40242         "255"
40243       ],
40244       [
40245         "Thailand (ไทย)",
40246         "th",
40247         "66"
40248       ],
40249       [
40250         "Timor-Leste",
40251         "tl",
40252         "670"
40253       ],
40254       [
40255         "Togo",
40256         "tg",
40257         "228"
40258       ],
40259       [
40260         "Tokelau",
40261         "tk",
40262         "690"
40263       ],
40264       [
40265         "Tonga",
40266         "to",
40267         "676"
40268       ],
40269       [
40270         "Trinidad and Tobago",
40271         "tt",
40272         "1868"
40273       ],
40274       [
40275         "Tunisia (‫تونس‬‎)",
40276         "tn",
40277         "216"
40278       ],
40279       [
40280         "Turkey (Türkiye)",
40281         "tr",
40282         "90"
40283       ],
40284       [
40285         "Turkmenistan",
40286         "tm",
40287         "993"
40288       ],
40289       [
40290         "Turks and Caicos Islands",
40291         "tc",
40292         "1649"
40293       ],
40294       [
40295         "Tuvalu",
40296         "tv",
40297         "688"
40298       ],
40299       [
40300         "U.S. Virgin Islands",
40301         "vi",
40302         "1340"
40303       ],
40304       [
40305         "Uganda",
40306         "ug",
40307         "256"
40308       ],
40309       [
40310         "Ukraine (Україна)",
40311         "ua",
40312         "380"
40313       ],
40314       [
40315         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40316         "ae",
40317         "971"
40318       ],
40319       [
40320         "United Kingdom",
40321         "gb",
40322         "44",
40323         0
40324       ],
40325       [
40326         "United States",
40327         "us",
40328         "1",
40329         0
40330       ],
40331       [
40332         "Uruguay",
40333         "uy",
40334         "598"
40335       ],
40336       [
40337         "Uzbekistan (Oʻzbekiston)",
40338         "uz",
40339         "998"
40340       ],
40341       [
40342         "Vanuatu",
40343         "vu",
40344         "678"
40345       ],
40346       [
40347         "Vatican City (Città del Vaticano)",
40348         "va",
40349         "39",
40350         1
40351       ],
40352       [
40353         "Venezuela",
40354         "ve",
40355         "58"
40356       ],
40357       [
40358         "Vietnam (Việt Nam)",
40359         "vn",
40360         "84"
40361       ],
40362       [
40363         "Wallis and Futuna (Wallis-et-Futuna)",
40364         "wf",
40365         "681"
40366       ],
40367       [
40368         "Western Sahara (‫الصحراء الغربية‬‎)",
40369         "eh",
40370         "212",
40371         1
40372       ],
40373       [
40374         "Yemen (‫اليمن‬‎)",
40375         "ye",
40376         "967"
40377       ],
40378       [
40379         "Zambia",
40380         "zm",
40381         "260"
40382       ],
40383       [
40384         "Zimbabwe",
40385         "zw",
40386         "263"
40387       ],
40388       [
40389         "Åland Islands",
40390         "ax",
40391         "358",
40392         1
40393       ]
40394   ];
40395   
40396   return d;
40397 }/**
40398 *    This script refer to:
40399 *    Title: International Telephone Input
40400 *    Author: Jack O'Connor
40401 *    Code version:  v12.1.12
40402 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40403 **/
40404
40405 /**
40406  * @class Roo.bootstrap.PhoneInput
40407  * @extends Roo.bootstrap.TriggerField
40408  * An input with International dial-code selection
40409  
40410  * @cfg {String} defaultDialCode default '+852'
40411  * @cfg {Array} preferedCountries default []
40412   
40413  * @constructor
40414  * Create a new PhoneInput.
40415  * @param {Object} config Configuration options
40416  */
40417
40418 Roo.bootstrap.PhoneInput = function(config) {
40419     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40420 };
40421
40422 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40423         
40424         listWidth: undefined,
40425         
40426         selectedClass: 'active',
40427         
40428         invalidClass : "has-warning",
40429         
40430         validClass: 'has-success',
40431         
40432         allowed: '0123456789',
40433         
40434         max_length: 15,
40435         
40436         /**
40437          * @cfg {String} defaultDialCode The default dial code when initializing the input
40438          */
40439         defaultDialCode: '+852',
40440         
40441         /**
40442          * @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
40443          */
40444         preferedCountries: false,
40445         
40446         getAutoCreate : function()
40447         {
40448             var data = Roo.bootstrap.PhoneInputData();
40449             var align = this.labelAlign || this.parentLabelAlign();
40450             var id = Roo.id();
40451             
40452             this.allCountries = [];
40453             this.dialCodeMapping = [];
40454             
40455             for (var i = 0; i < data.length; i++) {
40456               var c = data[i];
40457               this.allCountries[i] = {
40458                 name: c[0],
40459                 iso2: c[1],
40460                 dialCode: c[2],
40461                 priority: c[3] || 0,
40462                 areaCodes: c[4] || null
40463               };
40464               this.dialCodeMapping[c[2]] = {
40465                   name: c[0],
40466                   iso2: c[1],
40467                   priority: c[3] || 0,
40468                   areaCodes: c[4] || null
40469               };
40470             }
40471             
40472             var cfg = {
40473                 cls: 'form-group',
40474                 cn: []
40475             };
40476             
40477             var input =  {
40478                 tag: 'input',
40479                 id : id,
40480                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40481                 maxlength: this.max_length,
40482                 cls : 'form-control tel-input',
40483                 autocomplete: 'new-password'
40484             };
40485             
40486             var hiddenInput = {
40487                 tag: 'input',
40488                 type: 'hidden',
40489                 cls: 'hidden-tel-input'
40490             };
40491             
40492             if (this.name) {
40493                 hiddenInput.name = this.name;
40494             }
40495             
40496             if (this.disabled) {
40497                 input.disabled = true;
40498             }
40499             
40500             var flag_container = {
40501                 tag: 'div',
40502                 cls: 'flag-box',
40503                 cn: [
40504                     {
40505                         tag: 'div',
40506                         cls: 'flag'
40507                     },
40508                     {
40509                         tag: 'div',
40510                         cls: 'caret'
40511                     }
40512                 ]
40513             };
40514             
40515             var box = {
40516                 tag: 'div',
40517                 cls: this.hasFeedback ? 'has-feedback' : '',
40518                 cn: [
40519                     hiddenInput,
40520                     input,
40521                     {
40522                         tag: 'input',
40523                         cls: 'dial-code-holder',
40524                         disabled: true
40525                     }
40526                 ]
40527             };
40528             
40529             var container = {
40530                 cls: 'roo-select2-container input-group',
40531                 cn: [
40532                     flag_container,
40533                     box
40534                 ]
40535             };
40536             
40537             if (this.fieldLabel.length) {
40538                 var indicator = {
40539                     tag: 'i',
40540                     tooltip: 'This field is required'
40541                 };
40542                 
40543                 var label = {
40544                     tag: 'label',
40545                     'for':  id,
40546                     cls: 'control-label',
40547                     cn: []
40548                 };
40549                 
40550                 var label_text = {
40551                     tag: 'span',
40552                     html: this.fieldLabel
40553                 };
40554                 
40555                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40556                 label.cn = [
40557                     indicator,
40558                     label_text
40559                 ];
40560                 
40561                 if(this.indicatorpos == 'right') {
40562                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40563                     label.cn = [
40564                         label_text,
40565                         indicator
40566                     ];
40567                 }
40568                 
40569                 if(align == 'left') {
40570                     container = {
40571                         tag: 'div',
40572                         cn: [
40573                             container
40574                         ]
40575                     };
40576                     
40577                     if(this.labelWidth > 12){
40578                         label.style = "width: " + this.labelWidth + 'px';
40579                     }
40580                     if(this.labelWidth < 13 && this.labelmd == 0){
40581                         this.labelmd = this.labelWidth;
40582                     }
40583                     if(this.labellg > 0){
40584                         label.cls += ' col-lg-' + this.labellg;
40585                         input.cls += ' col-lg-' + (12 - this.labellg);
40586                     }
40587                     if(this.labelmd > 0){
40588                         label.cls += ' col-md-' + this.labelmd;
40589                         container.cls += ' col-md-' + (12 - this.labelmd);
40590                     }
40591                     if(this.labelsm > 0){
40592                         label.cls += ' col-sm-' + this.labelsm;
40593                         container.cls += ' col-sm-' + (12 - this.labelsm);
40594                     }
40595                     if(this.labelxs > 0){
40596                         label.cls += ' col-xs-' + this.labelxs;
40597                         container.cls += ' col-xs-' + (12 - this.labelxs);
40598                     }
40599                 }
40600             }
40601             
40602             cfg.cn = [
40603                 label,
40604                 container
40605             ];
40606             
40607             var settings = this;
40608             
40609             ['xs','sm','md','lg'].map(function(size){
40610                 if (settings[size]) {
40611                     cfg.cls += ' col-' + size + '-' + settings[size];
40612                 }
40613             });
40614             
40615             this.store = new Roo.data.Store({
40616                 proxy : new Roo.data.MemoryProxy({}),
40617                 reader : new Roo.data.JsonReader({
40618                     fields : [
40619                         {
40620                             'name' : 'name',
40621                             'type' : 'string'
40622                         },
40623                         {
40624                             'name' : 'iso2',
40625                             'type' : 'string'
40626                         },
40627                         {
40628                             'name' : 'dialCode',
40629                             'type' : 'string'
40630                         },
40631                         {
40632                             'name' : 'priority',
40633                             'type' : 'string'
40634                         },
40635                         {
40636                             'name' : 'areaCodes',
40637                             'type' : 'string'
40638                         }
40639                     ]
40640                 })
40641             });
40642             
40643             if(!this.preferedCountries) {
40644                 this.preferedCountries = [
40645                     'hk',
40646                     'gb',
40647                     'us'
40648                 ];
40649             }
40650             
40651             var p = this.preferedCountries.reverse();
40652             
40653             if(p) {
40654                 for (var i = 0; i < p.length; i++) {
40655                     for (var j = 0; j < this.allCountries.length; j++) {
40656                         if(this.allCountries[j].iso2 == p[i]) {
40657                             var t = this.allCountries[j];
40658                             this.allCountries.splice(j,1);
40659                             this.allCountries.unshift(t);
40660                         }
40661                     } 
40662                 }
40663             }
40664             
40665             this.store.proxy.data = {
40666                 success: true,
40667                 data: this.allCountries
40668             };
40669             
40670             return cfg;
40671         },
40672         
40673         initEvents : function()
40674         {
40675             this.createList();
40676             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40677             
40678             this.indicator = this.indicatorEl();
40679             this.flag = this.flagEl();
40680             this.dialCodeHolder = this.dialCodeHolderEl();
40681             
40682             this.trigger = this.el.select('div.flag-box',true).first();
40683             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40684             
40685             var _this = this;
40686             
40687             (function(){
40688                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40689                 _this.list.setWidth(lw);
40690             }).defer(100);
40691             
40692             this.list.on('mouseover', this.onViewOver, this);
40693             this.list.on('mousemove', this.onViewMove, this);
40694             this.inputEl().on("keyup", this.onKeyUp, this);
40695             this.inputEl().on("keypress", this.onKeyPress, this);
40696             
40697             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40698
40699             this.view = new Roo.View(this.list, this.tpl, {
40700                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40701             });
40702             
40703             this.view.on('click', this.onViewClick, this);
40704             this.setValue(this.defaultDialCode);
40705         },
40706         
40707         onTriggerClick : function(e)
40708         {
40709             Roo.log('trigger click');
40710             if(this.disabled){
40711                 return;
40712             }
40713             
40714             if(this.isExpanded()){
40715                 this.collapse();
40716                 this.hasFocus = false;
40717             }else {
40718                 this.store.load({});
40719                 this.hasFocus = true;
40720                 this.expand();
40721             }
40722         },
40723         
40724         isExpanded : function()
40725         {
40726             return this.list.isVisible();
40727         },
40728         
40729         collapse : function()
40730         {
40731             if(!this.isExpanded()){
40732                 return;
40733             }
40734             this.list.hide();
40735             Roo.get(document).un('mousedown', this.collapseIf, this);
40736             Roo.get(document).un('mousewheel', this.collapseIf, this);
40737             this.fireEvent('collapse', this);
40738             this.validate();
40739         },
40740         
40741         expand : function()
40742         {
40743             Roo.log('expand');
40744
40745             if(this.isExpanded() || !this.hasFocus){
40746                 return;
40747             }
40748             
40749             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40750             this.list.setWidth(lw);
40751             
40752             this.list.show();
40753             this.restrictHeight();
40754             
40755             Roo.get(document).on('mousedown', this.collapseIf, this);
40756             Roo.get(document).on('mousewheel', this.collapseIf, this);
40757             
40758             this.fireEvent('expand', this);
40759         },
40760         
40761         restrictHeight : function()
40762         {
40763             this.list.alignTo(this.inputEl(), this.listAlign);
40764             this.list.alignTo(this.inputEl(), this.listAlign);
40765         },
40766         
40767         onViewOver : function(e, t)
40768         {
40769             if(this.inKeyMode){
40770                 return;
40771             }
40772             var item = this.view.findItemFromChild(t);
40773             
40774             if(item){
40775                 var index = this.view.indexOf(item);
40776                 this.select(index, false);
40777             }
40778         },
40779
40780         // private
40781         onViewClick : function(view, doFocus, el, e)
40782         {
40783             var index = this.view.getSelectedIndexes()[0];
40784             
40785             var r = this.store.getAt(index);
40786             
40787             if(r){
40788                 this.onSelect(r, index);
40789             }
40790             if(doFocus !== false && !this.blockFocus){
40791                 this.inputEl().focus();
40792             }
40793         },
40794         
40795         onViewMove : function(e, t)
40796         {
40797             this.inKeyMode = false;
40798         },
40799         
40800         select : function(index, scrollIntoView)
40801         {
40802             this.selectedIndex = index;
40803             this.view.select(index);
40804             if(scrollIntoView !== false){
40805                 var el = this.view.getNode(index);
40806                 if(el){
40807                     this.list.scrollChildIntoView(el, false);
40808                 }
40809             }
40810         },
40811         
40812         createList : function()
40813         {
40814             this.list = Roo.get(document.body).createChild({
40815                 tag: 'ul',
40816                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40817                 style: 'display:none'
40818             });
40819             
40820             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40821         },
40822         
40823         collapseIf : function(e)
40824         {
40825             var in_combo  = e.within(this.el);
40826             var in_list =  e.within(this.list);
40827             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40828             
40829             if (in_combo || in_list || is_list) {
40830                 return;
40831             }
40832             this.collapse();
40833         },
40834         
40835         onSelect : function(record, index)
40836         {
40837             if(this.fireEvent('beforeselect', this, record, index) !== false){
40838                 
40839                 this.setFlagClass(record.data.iso2);
40840                 this.setDialCode(record.data.dialCode);
40841                 this.hasFocus = false;
40842                 this.collapse();
40843                 this.fireEvent('select', this, record, index);
40844             }
40845         },
40846         
40847         flagEl : function()
40848         {
40849             var flag = this.el.select('div.flag',true).first();
40850             if(!flag){
40851                 return false;
40852             }
40853             return flag;
40854         },
40855         
40856         dialCodeHolderEl : function()
40857         {
40858             var d = this.el.select('input.dial-code-holder',true).first();
40859             if(!d){
40860                 return false;
40861             }
40862             return d;
40863         },
40864         
40865         setDialCode : function(v)
40866         {
40867             this.dialCodeHolder.dom.value = '+'+v;
40868         },
40869         
40870         setFlagClass : function(n)
40871         {
40872             this.flag.dom.className = 'flag '+n;
40873         },
40874         
40875         getValue : function()
40876         {
40877             var v = this.inputEl().getValue();
40878             if(this.dialCodeHolder) {
40879                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40880             }
40881             return v;
40882         },
40883         
40884         setValue : function(v)
40885         {
40886             var d = this.getDialCode(v);
40887             
40888             //invalid dial code
40889             if(v.length == 0 || !d || d.length == 0) {
40890                 if(this.rendered){
40891                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40892                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40893                 }
40894                 return;
40895             }
40896             
40897             //valid dial code
40898             this.setFlagClass(this.dialCodeMapping[d].iso2);
40899             this.setDialCode(d);
40900             this.inputEl().dom.value = v.replace('+'+d,'');
40901             this.hiddenEl().dom.value = this.getValue();
40902             
40903             this.validate();
40904         },
40905         
40906         getDialCode : function(v)
40907         {
40908             v = v ||  '';
40909             
40910             if (v.length == 0) {
40911                 return this.dialCodeHolder.dom.value;
40912             }
40913             
40914             var dialCode = "";
40915             if (v.charAt(0) != "+") {
40916                 return false;
40917             }
40918             var numericChars = "";
40919             for (var i = 1; i < v.length; i++) {
40920               var c = v.charAt(i);
40921               if (!isNaN(c)) {
40922                 numericChars += c;
40923                 if (this.dialCodeMapping[numericChars]) {
40924                   dialCode = v.substr(1, i);
40925                 }
40926                 if (numericChars.length == 4) {
40927                   break;
40928                 }
40929               }
40930             }
40931             return dialCode;
40932         },
40933         
40934         reset : function()
40935         {
40936             this.setValue(this.defaultDialCode);
40937             this.validate();
40938         },
40939         
40940         hiddenEl : function()
40941         {
40942             return this.el.select('input.hidden-tel-input',true).first();
40943         },
40944         
40945         // after setting val
40946         onKeyUp : function(e){
40947             this.setValue(this.getValue());
40948         },
40949         
40950         onKeyPress : function(e){
40951             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40952                 e.stopEvent();
40953             }
40954         }
40955         
40956 });
40957 /**
40958  * @class Roo.bootstrap.MoneyField
40959  * @extends Roo.bootstrap.ComboBox
40960  * Bootstrap MoneyField class
40961  * 
40962  * @constructor
40963  * Create a new MoneyField.
40964  * @param {Object} config Configuration options
40965  */
40966
40967 Roo.bootstrap.MoneyField = function(config) {
40968     
40969     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40970     
40971 };
40972
40973 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40974     
40975     /**
40976      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40977      */
40978     allowDecimals : true,
40979     /**
40980      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40981      */
40982     decimalSeparator : ".",
40983     /**
40984      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40985      */
40986     decimalPrecision : 0,
40987     /**
40988      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40989      */
40990     allowNegative : true,
40991     /**
40992      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40993      */
40994     allowZero: true,
40995     /**
40996      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40997      */
40998     minValue : Number.NEGATIVE_INFINITY,
40999     /**
41000      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41001      */
41002     maxValue : Number.MAX_VALUE,
41003     /**
41004      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41005      */
41006     minText : "The minimum value for this field is {0}",
41007     /**
41008      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41009      */
41010     maxText : "The maximum value for this field is {0}",
41011     /**
41012      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41013      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41014      */
41015     nanText : "{0} is not a valid number",
41016     /**
41017      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41018      */
41019     castInt : true,
41020     /**
41021      * @cfg {String} defaults currency of the MoneyField
41022      * value should be in lkey
41023      */
41024     defaultCurrency : false,
41025     /**
41026      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41027      */
41028     thousandsDelimiter : false,
41029     /**
41030      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41031      */
41032     max_length: false,
41033     
41034     inputlg : 9,
41035     inputmd : 9,
41036     inputsm : 9,
41037     inputxs : 6,
41038     
41039     store : false,
41040     
41041     getAutoCreate : function()
41042     {
41043         var align = this.labelAlign || this.parentLabelAlign();
41044         
41045         var id = Roo.id();
41046
41047         var cfg = {
41048             cls: 'form-group',
41049             cn: []
41050         };
41051
41052         var input =  {
41053             tag: 'input',
41054             id : id,
41055             cls : 'form-control roo-money-amount-input',
41056             autocomplete: 'new-password'
41057         };
41058         
41059         var hiddenInput = {
41060             tag: 'input',
41061             type: 'hidden',
41062             id: Roo.id(),
41063             cls: 'hidden-number-input'
41064         };
41065         
41066         if(this.max_length) {
41067             input.maxlength = this.max_length; 
41068         }
41069         
41070         if (this.name) {
41071             hiddenInput.name = this.name;
41072         }
41073
41074         if (this.disabled) {
41075             input.disabled = true;
41076         }
41077
41078         var clg = 12 - this.inputlg;
41079         var cmd = 12 - this.inputmd;
41080         var csm = 12 - this.inputsm;
41081         var cxs = 12 - this.inputxs;
41082         
41083         var container = {
41084             tag : 'div',
41085             cls : 'row roo-money-field',
41086             cn : [
41087                 {
41088                     tag : 'div',
41089                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41090                     cn : [
41091                         {
41092                             tag : 'div',
41093                             cls: 'roo-select2-container input-group',
41094                             cn: [
41095                                 {
41096                                     tag : 'input',
41097                                     cls : 'form-control roo-money-currency-input',
41098                                     autocomplete: 'new-password',
41099                                     readOnly : 1,
41100                                     name : this.currencyName
41101                                 },
41102                                 {
41103                                     tag :'span',
41104                                     cls : 'input-group-addon',
41105                                     cn : [
41106                                         {
41107                                             tag: 'span',
41108                                             cls: 'caret'
41109                                         }
41110                                     ]
41111                                 }
41112                             ]
41113                         }
41114                     ]
41115                 },
41116                 {
41117                     tag : 'div',
41118                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41119                     cn : [
41120                         {
41121                             tag: 'div',
41122                             cls: this.hasFeedback ? 'has-feedback' : '',
41123                             cn: [
41124                                 input
41125                             ]
41126                         }
41127                     ]
41128                 }
41129             ]
41130             
41131         };
41132         
41133         if (this.fieldLabel.length) {
41134             var indicator = {
41135                 tag: 'i',
41136                 tooltip: 'This field is required'
41137             };
41138
41139             var label = {
41140                 tag: 'label',
41141                 'for':  id,
41142                 cls: 'control-label',
41143                 cn: []
41144             };
41145
41146             var label_text = {
41147                 tag: 'span',
41148                 html: this.fieldLabel
41149             };
41150
41151             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41152             label.cn = [
41153                 indicator,
41154                 label_text
41155             ];
41156
41157             if(this.indicatorpos == 'right') {
41158                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41159                 label.cn = [
41160                     label_text,
41161                     indicator
41162                 ];
41163             }
41164
41165             if(align == 'left') {
41166                 container = {
41167                     tag: 'div',
41168                     cn: [
41169                         container
41170                     ]
41171                 };
41172
41173                 if(this.labelWidth > 12){
41174                     label.style = "width: " + this.labelWidth + 'px';
41175                 }
41176                 if(this.labelWidth < 13 && this.labelmd == 0){
41177                     this.labelmd = this.labelWidth;
41178                 }
41179                 if(this.labellg > 0){
41180                     label.cls += ' col-lg-' + this.labellg;
41181                     input.cls += ' col-lg-' + (12 - this.labellg);
41182                 }
41183                 if(this.labelmd > 0){
41184                     label.cls += ' col-md-' + this.labelmd;
41185                     container.cls += ' col-md-' + (12 - this.labelmd);
41186                 }
41187                 if(this.labelsm > 0){
41188                     label.cls += ' col-sm-' + this.labelsm;
41189                     container.cls += ' col-sm-' + (12 - this.labelsm);
41190                 }
41191                 if(this.labelxs > 0){
41192                     label.cls += ' col-xs-' + this.labelxs;
41193                     container.cls += ' col-xs-' + (12 - this.labelxs);
41194                 }
41195             }
41196         }
41197
41198         cfg.cn = [
41199             label,
41200             container,
41201             hiddenInput
41202         ];
41203         
41204         var settings = this;
41205
41206         ['xs','sm','md','lg'].map(function(size){
41207             if (settings[size]) {
41208                 cfg.cls += ' col-' + size + '-' + settings[size];
41209             }
41210         });
41211         
41212         return cfg;
41213     },
41214     
41215     initEvents : function()
41216     {
41217         this.indicator = this.indicatorEl();
41218         
41219         this.initCurrencyEvent();
41220         
41221         this.initNumberEvent();
41222     },
41223     
41224     initCurrencyEvent : function()
41225     {
41226         if (!this.store) {
41227             throw "can not find store for combo";
41228         }
41229         
41230         this.store = Roo.factory(this.store, Roo.data);
41231         this.store.parent = this;
41232         
41233         this.createList();
41234         
41235         this.triggerEl = this.el.select('.input-group-addon', true).first();
41236         
41237         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41238         
41239         var _this = this;
41240         
41241         (function(){
41242             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41243             _this.list.setWidth(lw);
41244         }).defer(100);
41245         
41246         this.list.on('mouseover', this.onViewOver, this);
41247         this.list.on('mousemove', this.onViewMove, this);
41248         this.list.on('scroll', this.onViewScroll, this);
41249         
41250         if(!this.tpl){
41251             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41252         }
41253         
41254         this.view = new Roo.View(this.list, this.tpl, {
41255             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41256         });
41257         
41258         this.view.on('click', this.onViewClick, this);
41259         
41260         this.store.on('beforeload', this.onBeforeLoad, this);
41261         this.store.on('load', this.onLoad, this);
41262         this.store.on('loadexception', this.onLoadException, this);
41263         
41264         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41265             "up" : function(e){
41266                 this.inKeyMode = true;
41267                 this.selectPrev();
41268             },
41269
41270             "down" : function(e){
41271                 if(!this.isExpanded()){
41272                     this.onTriggerClick();
41273                 }else{
41274                     this.inKeyMode = true;
41275                     this.selectNext();
41276                 }
41277             },
41278
41279             "enter" : function(e){
41280                 this.collapse();
41281                 
41282                 if(this.fireEvent("specialkey", this, e)){
41283                     this.onViewClick(false);
41284                 }
41285                 
41286                 return true;
41287             },
41288
41289             "esc" : function(e){
41290                 this.collapse();
41291             },
41292
41293             "tab" : function(e){
41294                 this.collapse();
41295                 
41296                 if(this.fireEvent("specialkey", this, e)){
41297                     this.onViewClick(false);
41298                 }
41299                 
41300                 return true;
41301             },
41302
41303             scope : this,
41304
41305             doRelay : function(foo, bar, hname){
41306                 if(hname == 'down' || this.scope.isExpanded()){
41307                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41308                 }
41309                 return true;
41310             },
41311
41312             forceKeyDown: true
41313         });
41314         
41315         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41316         
41317     },
41318     
41319     initNumberEvent : function(e)
41320     {
41321         this.inputEl().on("keydown" , this.fireKey,  this);
41322         this.inputEl().on("focus", this.onFocus,  this);
41323         this.inputEl().on("blur", this.onBlur,  this);
41324         
41325         this.inputEl().relayEvent('keyup', this);
41326         
41327         if(this.indicator){
41328             this.indicator.addClass('invisible');
41329         }
41330  
41331         this.originalValue = this.getValue();
41332         
41333         if(this.validationEvent == 'keyup'){
41334             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41335             this.inputEl().on('keyup', this.filterValidation, this);
41336         }
41337         else if(this.validationEvent !== false){
41338             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41339         }
41340         
41341         if(this.selectOnFocus){
41342             this.on("focus", this.preFocus, this);
41343             
41344         }
41345         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41346             this.inputEl().on("keypress", this.filterKeys, this);
41347         } else {
41348             this.inputEl().relayEvent('keypress', this);
41349         }
41350         
41351         var allowed = "0123456789";
41352         
41353         if(this.allowDecimals){
41354             allowed += this.decimalSeparator;
41355         }
41356         
41357         if(this.allowNegative){
41358             allowed += "-";
41359         }
41360         
41361         if(this.thousandsDelimiter) {
41362             allowed += ",";
41363         }
41364         
41365         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41366         
41367         var keyPress = function(e){
41368             
41369             var k = e.getKey();
41370             
41371             var c = e.getCharCode();
41372             
41373             if(
41374                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41375                     allowed.indexOf(String.fromCharCode(c)) === -1
41376             ){
41377                 e.stopEvent();
41378                 return;
41379             }
41380             
41381             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41382                 return;
41383             }
41384             
41385             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41386                 e.stopEvent();
41387             }
41388         };
41389         
41390         this.inputEl().on("keypress", keyPress, this);
41391         
41392     },
41393     
41394     onTriggerClick : function(e)
41395     {   
41396         if(this.disabled){
41397             return;
41398         }
41399         
41400         this.page = 0;
41401         this.loadNext = false;
41402         
41403         if(this.isExpanded()){
41404             this.collapse();
41405             return;
41406         }
41407         
41408         this.hasFocus = true;
41409         
41410         if(this.triggerAction == 'all') {
41411             this.doQuery(this.allQuery, true);
41412             return;
41413         }
41414         
41415         this.doQuery(this.getRawValue());
41416     },
41417     
41418     getCurrency : function()
41419     {   
41420         var v = this.currencyEl().getValue();
41421         
41422         return v;
41423     },
41424     
41425     restrictHeight : function()
41426     {
41427         this.list.alignTo(this.currencyEl(), this.listAlign);
41428         this.list.alignTo(this.currencyEl(), this.listAlign);
41429     },
41430     
41431     onViewClick : function(view, doFocus, el, e)
41432     {
41433         var index = this.view.getSelectedIndexes()[0];
41434         
41435         var r = this.store.getAt(index);
41436         
41437         if(r){
41438             this.onSelect(r, index);
41439         }
41440     },
41441     
41442     onSelect : function(record, index){
41443         
41444         if(this.fireEvent('beforeselect', this, record, index) !== false){
41445         
41446             this.setFromCurrencyData(index > -1 ? record.data : false);
41447             
41448             this.collapse();
41449             
41450             this.fireEvent('select', this, record, index);
41451         }
41452     },
41453     
41454     setFromCurrencyData : function(o)
41455     {
41456         var currency = '';
41457         
41458         this.lastCurrency = o;
41459         
41460         if (this.currencyField) {
41461             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41462         } else {
41463             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41464         }
41465         
41466         this.lastSelectionText = currency;
41467         
41468         //setting default currency
41469         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41470             this.setCurrency(this.defaultCurrency);
41471             return;
41472         }
41473         
41474         this.setCurrency(currency);
41475     },
41476     
41477     setFromData : function(o)
41478     {
41479         var c = {};
41480         
41481         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41482         
41483         this.setFromCurrencyData(c);
41484         
41485         var value = '';
41486         
41487         if (this.name) {
41488             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41489         } else {
41490             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41491         }
41492         
41493         this.setValue(value);
41494         
41495     },
41496     
41497     setCurrency : function(v)
41498     {   
41499         this.currencyValue = v;
41500         
41501         if(this.rendered){
41502             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41503             this.validate();
41504         }
41505     },
41506     
41507     setValue : function(v)
41508     {
41509         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41510         
41511         this.value = v;
41512         
41513         if(this.rendered){
41514             
41515             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41516             
41517             this.inputEl().dom.value = (v == '') ? '' :
41518                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41519             
41520             if(!this.allowZero && v === '0') {
41521                 this.hiddenEl().dom.value = '';
41522                 this.inputEl().dom.value = '';
41523             }
41524             
41525             this.validate();
41526         }
41527     },
41528     
41529     getRawValue : function()
41530     {
41531         var v = this.inputEl().getValue();
41532         
41533         return v;
41534     },
41535     
41536     getValue : function()
41537     {
41538         return this.fixPrecision(this.parseValue(this.getRawValue()));
41539     },
41540     
41541     parseValue : function(value)
41542     {
41543         if(this.thousandsDelimiter) {
41544             value += "";
41545             r = new RegExp(",", "g");
41546             value = value.replace(r, "");
41547         }
41548         
41549         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41550         return isNaN(value) ? '' : value;
41551         
41552     },
41553     
41554     fixPrecision : function(value)
41555     {
41556         if(this.thousandsDelimiter) {
41557             value += "";
41558             r = new RegExp(",", "g");
41559             value = value.replace(r, "");
41560         }
41561         
41562         var nan = isNaN(value);
41563         
41564         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41565             return nan ? '' : value;
41566         }
41567         return parseFloat(value).toFixed(this.decimalPrecision);
41568     },
41569     
41570     decimalPrecisionFcn : function(v)
41571     {
41572         return Math.floor(v);
41573     },
41574     
41575     validateValue : function(value)
41576     {
41577         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41578             return false;
41579         }
41580         
41581         var num = this.parseValue(value);
41582         
41583         if(isNaN(num)){
41584             this.markInvalid(String.format(this.nanText, value));
41585             return false;
41586         }
41587         
41588         if(num < this.minValue){
41589             this.markInvalid(String.format(this.minText, this.minValue));
41590             return false;
41591         }
41592         
41593         if(num > this.maxValue){
41594             this.markInvalid(String.format(this.maxText, this.maxValue));
41595             return false;
41596         }
41597         
41598         return true;
41599     },
41600     
41601     validate : function()
41602     {
41603         if(this.disabled || this.allowBlank){
41604             this.markValid();
41605             return true;
41606         }
41607         
41608         var currency = this.getCurrency();
41609         
41610         if(this.validateValue(this.getRawValue()) && currency.length){
41611             this.markValid();
41612             return true;
41613         }
41614         
41615         this.markInvalid();
41616         return false;
41617     },
41618     
41619     getName: function()
41620     {
41621         return this.name;
41622     },
41623     
41624     beforeBlur : function()
41625     {
41626         if(!this.castInt){
41627             return;
41628         }
41629         
41630         var v = this.parseValue(this.getRawValue());
41631         
41632         if(v || v == 0){
41633             this.setValue(v);
41634         }
41635     },
41636     
41637     onBlur : function()
41638     {
41639         this.beforeBlur();
41640         
41641         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41642             //this.el.removeClass(this.focusClass);
41643         }
41644         
41645         this.hasFocus = false;
41646         
41647         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41648             this.validate();
41649         }
41650         
41651         var v = this.getValue();
41652         
41653         if(String(v) !== String(this.startValue)){
41654             this.fireEvent('change', this, v, this.startValue);
41655         }
41656         
41657         this.fireEvent("blur", this);
41658     },
41659     
41660     inputEl : function()
41661     {
41662         return this.el.select('.roo-money-amount-input', true).first();
41663     },
41664     
41665     currencyEl : function()
41666     {
41667         return this.el.select('.roo-money-currency-input', true).first();
41668     },
41669     
41670     hiddenEl : function()
41671     {
41672         return this.el.select('input.hidden-number-input',true).first();
41673     }
41674     
41675 });/**
41676  * @class Roo.bootstrap.BezierSignature
41677  * @extends Roo.bootstrap.Component
41678  * Bootstrap BezierSignature class
41679  * This script refer to:
41680  *    Title: Signature Pad
41681  *    Author: szimek
41682  *    Availability: https://github.com/szimek/signature_pad
41683  *
41684  * @constructor
41685  * Create a new BezierSignature
41686  * @param {Object} config The config object
41687  */
41688
41689 Roo.bootstrap.BezierSignature = function(config){
41690     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41691     this.addEvents({
41692         "resize" : true
41693     });
41694 };
41695
41696 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41697 {
41698      
41699     curve_data: [],
41700     
41701     is_empty: true,
41702     
41703     mouse_btn_down: true,
41704     
41705     /**
41706      * @cfg {int} canvas height
41707      */
41708     canvas_height: '200px',
41709     
41710     /**
41711      * @cfg {float|function} Radius of a single dot.
41712      */ 
41713     dot_size: false,
41714     
41715     /**
41716      * @cfg {float} Minimum width of a line. Defaults to 0.5.
41717      */
41718     min_width: 0.5,
41719     
41720     /**
41721      * @cfg {float} Maximum width of a line. Defaults to 2.5.
41722      */
41723     max_width: 2.5,
41724     
41725     /**
41726      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41727      */
41728     throttle: 16,
41729     
41730     /**
41731      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41732      */
41733     min_distance: 5,
41734     
41735     /**
41736      * @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.
41737      */
41738     bg_color: 'rgba(0, 0, 0, 0)',
41739     
41740     /**
41741      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41742      */
41743     dot_color: 'black',
41744     
41745     /**
41746      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41747      */ 
41748     velocity_filter_weight: 0.7,
41749     
41750     /**
41751      * @cfg {function} Callback when stroke begin. 
41752      */
41753     onBegin: false,
41754     
41755     /**
41756      * @cfg {function} Callback when stroke end.
41757      */
41758     onEnd: false,
41759     
41760     getAutoCreate : function()
41761     {
41762         var cls = 'roo-signature column';
41763         
41764         if(this.cls){
41765             cls += ' ' + this.cls;
41766         }
41767         
41768         var col_sizes = [
41769             'lg',
41770             'md',
41771             'sm',
41772             'xs'
41773         ];
41774         
41775         for(var i = 0; i < col_sizes.length; i++) {
41776             if(this[col_sizes[i]]) {
41777                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41778             }
41779         }
41780         
41781         var cfg = {
41782             tag: 'div',
41783             cls: cls,
41784             cn: [
41785                 {
41786                     tag: 'div',
41787                     cls: 'roo-signature-body',
41788                     cn: [
41789                         {
41790                             tag: 'canvas',
41791                             cls: 'roo-signature-body-canvas',
41792                             height: this.canvas_height,
41793                             width: this.canvas_width
41794                         }
41795                     ]
41796                 },
41797                 {
41798                     tag: 'input',
41799                     type: 'file',
41800                     style: 'display: none'
41801                 }
41802             ]
41803         };
41804         
41805         return cfg;
41806     },
41807     
41808     initEvents: function() 
41809     {
41810         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41811         
41812         var canvas = this.canvasEl();
41813         
41814         // mouse && touch event swapping...
41815         canvas.dom.style.touchAction = 'none';
41816         canvas.dom.style.msTouchAction = 'none';
41817         
41818         this.mouse_btn_down = false;
41819         canvas.on('mousedown', this._handleMouseDown, this);
41820         canvas.on('mousemove', this._handleMouseMove, this);
41821         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41822         
41823         if (window.PointerEvent) {
41824             canvas.on('pointerdown', this._handleMouseDown, this);
41825             canvas.on('pointermove', this._handleMouseMove, this);
41826             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41827         }
41828         
41829         if ('ontouchstart' in window) {
41830             canvas.on('touchstart', this._handleTouchStart, this);
41831             canvas.on('touchmove', this._handleTouchMove, this);
41832             canvas.on('touchend', this._handleTouchEnd, this);
41833         }
41834         
41835         Roo.EventManager.onWindowResize(this.resize, this, true);
41836         
41837         // file input event
41838         this.fileEl().on('change', this.uploadImage, this);
41839         
41840         this.clear();
41841         
41842         this.resize();
41843     },
41844     
41845     resize: function(){
41846         
41847         var canvas = this.canvasEl().dom;
41848         var ctx = this.canvasElCtx();
41849         var img_data = false;
41850         
41851         if(canvas.width > 0) {
41852             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41853         }
41854         // setting canvas width will clean img data
41855         canvas.width = 0;
41856         
41857         var style = window.getComputedStyle ? 
41858             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41859             
41860         var padding_left = parseInt(style.paddingLeft) || 0;
41861         var padding_right = parseInt(style.paddingRight) || 0;
41862         
41863         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41864         
41865         if(img_data) {
41866             ctx.putImageData(img_data, 0, 0);
41867         }
41868     },
41869     
41870     _handleMouseDown: function(e)
41871     {
41872         if (e.browserEvent.which === 1) {
41873             this.mouse_btn_down = true;
41874             this.strokeBegin(e);
41875         }
41876     },
41877     
41878     _handleMouseMove: function (e)
41879     {
41880         if (this.mouse_btn_down) {
41881             this.strokeMoveUpdate(e);
41882         }
41883     },
41884     
41885     _handleMouseUp: function (e)
41886     {
41887         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41888             this.mouse_btn_down = false;
41889             this.strokeEnd(e);
41890         }
41891     },
41892     
41893     _handleTouchStart: function (e) {
41894         
41895         e.preventDefault();
41896         if (e.browserEvent.targetTouches.length === 1) {
41897             // var touch = e.browserEvent.changedTouches[0];
41898             // this.strokeBegin(touch);
41899             
41900              this.strokeBegin(e); // assume e catching the correct xy...
41901         }
41902     },
41903     
41904     _handleTouchMove: function (e) {
41905         e.preventDefault();
41906         // var touch = event.targetTouches[0];
41907         // _this._strokeMoveUpdate(touch);
41908         this.strokeMoveUpdate(e);
41909     },
41910     
41911     _handleTouchEnd: function (e) {
41912         var wasCanvasTouched = e.target === this.canvasEl().dom;
41913         if (wasCanvasTouched) {
41914             e.preventDefault();
41915             // var touch = event.changedTouches[0];
41916             // _this._strokeEnd(touch);
41917             this.strokeEnd(e);
41918         }
41919     },
41920     
41921     reset: function () {
41922         this._lastPoints = [];
41923         this._lastVelocity = 0;
41924         this._lastWidth = (this.min_width + this.max_width) / 2;
41925         this.canvasElCtx().fillStyle = this.dot_color;
41926     },
41927     
41928     strokeMoveUpdate: function(e)
41929     {
41930         this.strokeUpdate(e);
41931         
41932         if (this.throttle) {
41933             this.throttleStroke(this.strokeUpdate, this.throttle);
41934         }
41935         else {
41936             this.strokeUpdate(e);
41937         }
41938     },
41939     
41940     strokeBegin: function(e)
41941     {
41942         var newPointGroup = {
41943             color: this.dot_color,
41944             points: []
41945         };
41946         
41947         if (typeof this.onBegin === 'function') {
41948             this.onBegin(e);
41949         }
41950         
41951         this.curve_data.push(newPointGroup);
41952         this.reset();
41953         this.strokeUpdate(e);
41954     },
41955     
41956     strokeUpdate: function(e)
41957     {
41958         var rect = this.canvasEl().dom.getBoundingClientRect();
41959         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41960         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41961         var lastPoints = lastPointGroup.points;
41962         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41963         var isLastPointTooClose = lastPoint
41964             ? point.distanceTo(lastPoint) <= this.min_distance
41965             : false;
41966         var color = lastPointGroup.color;
41967         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41968             var curve = this.addPoint(point);
41969             if (!lastPoint) {
41970                 this.drawDot({color: color, point: point});
41971             }
41972             else if (curve) {
41973                 this.drawCurve({color: color, curve: curve});
41974             }
41975             lastPoints.push({
41976                 time: point.time,
41977                 x: point.x,
41978                 y: point.y
41979             });
41980         }
41981     },
41982     
41983     strokeEnd: function(e)
41984     {
41985         this.strokeUpdate(e);
41986         if (typeof this.onEnd === 'function') {
41987             this.onEnd(e);
41988         }
41989     },
41990     
41991     addPoint:  function (point) {
41992         var _lastPoints = this._lastPoints;
41993         _lastPoints.push(point);
41994         if (_lastPoints.length > 2) {
41995             if (_lastPoints.length === 3) {
41996                 _lastPoints.unshift(_lastPoints[0]);
41997             }
41998             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41999             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42000             _lastPoints.shift();
42001             return curve;
42002         }
42003         return null;
42004     },
42005     
42006     calculateCurveWidths: function (startPoint, endPoint) {
42007         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42008             (1 - this.velocity_filter_weight) * this._lastVelocity;
42009
42010         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42011         var widths = {
42012             end: newWidth,
42013             start: this._lastWidth
42014         };
42015         
42016         this._lastVelocity = velocity;
42017         this._lastWidth = newWidth;
42018         return widths;
42019     },
42020     
42021     drawDot: function (_a) {
42022         var color = _a.color, point = _a.point;
42023         var ctx = this.canvasElCtx();
42024         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42025         ctx.beginPath();
42026         this.drawCurveSegment(point.x, point.y, width);
42027         ctx.closePath();
42028         ctx.fillStyle = color;
42029         ctx.fill();
42030     },
42031     
42032     drawCurve: function (_a) {
42033         var color = _a.color, curve = _a.curve;
42034         var ctx = this.canvasElCtx();
42035         var widthDelta = curve.endWidth - curve.startWidth;
42036         var drawSteps = Math.floor(curve.length()) * 2;
42037         ctx.beginPath();
42038         ctx.fillStyle = color;
42039         for (var i = 0; i < drawSteps; i += 1) {
42040         var t = i / drawSteps;
42041         var tt = t * t;
42042         var ttt = tt * t;
42043         var u = 1 - t;
42044         var uu = u * u;
42045         var uuu = uu * u;
42046         var x = uuu * curve.startPoint.x;
42047         x += 3 * uu * t * curve.control1.x;
42048         x += 3 * u * tt * curve.control2.x;
42049         x += ttt * curve.endPoint.x;
42050         var y = uuu * curve.startPoint.y;
42051         y += 3 * uu * t * curve.control1.y;
42052         y += 3 * u * tt * curve.control2.y;
42053         y += ttt * curve.endPoint.y;
42054         var width = curve.startWidth + ttt * widthDelta;
42055         this.drawCurveSegment(x, y, width);
42056         }
42057         ctx.closePath();
42058         ctx.fill();
42059     },
42060     
42061     drawCurveSegment: function (x, y, width) {
42062         var ctx = this.canvasElCtx();
42063         ctx.moveTo(x, y);
42064         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42065         this.is_empty = false;
42066     },
42067     
42068     clear: function()
42069     {
42070         var ctx = this.canvasElCtx();
42071         var canvas = this.canvasEl().dom;
42072         ctx.fillStyle = this.bg_color;
42073         ctx.clearRect(0, 0, canvas.width, canvas.height);
42074         ctx.fillRect(0, 0, canvas.width, canvas.height);
42075         this.curve_data = [];
42076         this.reset();
42077         this.is_empty = true;
42078     },
42079     
42080     fileEl: function()
42081     {
42082         return  this.el.select('input',true).first();
42083     },
42084     
42085     canvasEl: function()
42086     {
42087         return this.el.select('canvas',true).first();
42088     },
42089     
42090     canvasElCtx: function()
42091     {
42092         return this.el.select('canvas',true).first().dom.getContext('2d');
42093     },
42094     
42095     getImage: function(type)
42096     {
42097         if(this.is_empty) {
42098             return false;
42099         }
42100         
42101         // encryption ?
42102         return this.canvasEl().dom.toDataURL('image/'+type, 1);
42103     },
42104     
42105     drawFromImage: function(img_src)
42106     {
42107         var img = new Image();
42108         
42109         img.onload = function(){
42110             this.canvasElCtx().drawImage(img, 0, 0);
42111         }.bind(this);
42112         
42113         img.src = img_src;
42114         
42115         this.is_empty = false;
42116     },
42117     
42118     selectImage: function()
42119     {
42120         this.fileEl().dom.click();
42121     },
42122     
42123     uploadImage: function(e)
42124     {
42125         var reader = new FileReader();
42126         
42127         reader.onload = function(e){
42128             var img = new Image();
42129             img.onload = function(){
42130                 this.reset();
42131                 this.canvasElCtx().drawImage(img, 0, 0);
42132             }.bind(this);
42133             img.src = e.target.result;
42134         }.bind(this);
42135         
42136         reader.readAsDataURL(e.target.files[0]);
42137     },
42138     
42139     // Bezier Point Constructor
42140     Point: (function () {
42141         function Point(x, y, time) {
42142             this.x = x;
42143             this.y = y;
42144             this.time = time || Date.now();
42145         }
42146         Point.prototype.distanceTo = function (start) {
42147             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42148         };
42149         Point.prototype.equals = function (other) {
42150             return this.x === other.x && this.y === other.y && this.time === other.time;
42151         };
42152         Point.prototype.velocityFrom = function (start) {
42153             return this.time !== start.time
42154             ? this.distanceTo(start) / (this.time - start.time)
42155             : 0;
42156         };
42157         return Point;
42158     }()),
42159     
42160     
42161     // Bezier Constructor
42162     Bezier: (function () {
42163         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42164             this.startPoint = startPoint;
42165             this.control2 = control2;
42166             this.control1 = control1;
42167             this.endPoint = endPoint;
42168             this.startWidth = startWidth;
42169             this.endWidth = endWidth;
42170         }
42171         Bezier.fromPoints = function (points, widths, scope) {
42172             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42173             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42174             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42175         };
42176         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42177             var dx1 = s1.x - s2.x;
42178             var dy1 = s1.y - s2.y;
42179             var dx2 = s2.x - s3.x;
42180             var dy2 = s2.y - s3.y;
42181             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42182             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42183             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42184             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42185             var dxm = m1.x - m2.x;
42186             var dym = m1.y - m2.y;
42187             var k = l2 / (l1 + l2);
42188             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42189             var tx = s2.x - cm.x;
42190             var ty = s2.y - cm.y;
42191             return {
42192                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42193                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42194             };
42195         };
42196         Bezier.prototype.length = function () {
42197             var steps = 10;
42198             var length = 0;
42199             var px;
42200             var py;
42201             for (var i = 0; i <= steps; i += 1) {
42202                 var t = i / steps;
42203                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42204                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42205                 if (i > 0) {
42206                     var xdiff = cx - px;
42207                     var ydiff = cy - py;
42208                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42209                 }
42210                 px = cx;
42211                 py = cy;
42212             }
42213             return length;
42214         };
42215         Bezier.prototype.point = function (t, start, c1, c2, end) {
42216             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42217             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42218             + (3.0 * c2 * (1.0 - t) * t * t)
42219             + (end * t * t * t);
42220         };
42221         return Bezier;
42222     }()),
42223     
42224     throttleStroke: function(fn, wait) {
42225       if (wait === void 0) { wait = 250; }
42226       var previous = 0;
42227       var timeout = null;
42228       var result;
42229       var storedContext;
42230       var storedArgs;
42231       var later = function () {
42232           previous = Date.now();
42233           timeout = null;
42234           result = fn.apply(storedContext, storedArgs);
42235           if (!timeout) {
42236               storedContext = null;
42237               storedArgs = [];
42238           }
42239       };
42240       return function wrapper() {
42241           var args = [];
42242           for (var _i = 0; _i < arguments.length; _i++) {
42243               args[_i] = arguments[_i];
42244           }
42245           var now = Date.now();
42246           var remaining = wait - (now - previous);
42247           storedContext = this;
42248           storedArgs = args;
42249           if (remaining <= 0 || remaining > wait) {
42250               if (timeout) {
42251                   clearTimeout(timeout);
42252                   timeout = null;
42253               }
42254               previous = now;
42255               result = fn.apply(storedContext, storedArgs);
42256               if (!timeout) {
42257                   storedContext = null;
42258                   storedArgs = [];
42259               }
42260           }
42261           else if (!timeout) {
42262               timeout = window.setTimeout(later, remaining);
42263           }
42264           return result;
42265       };
42266   }
42267   
42268 });
42269
42270  
42271
42272