Roo/bootstrap/Component.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets, function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })(); /*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174     /**
175      * This is really a wrapper for addxtypeChild
176      * it handles stuff relating to flexy:foreach / flexy:if
177      * = some of our projects use a flat rendering of the output, and try and overlay it with dynamic data.
178      *  -- this is a bit of a nightmare... and is even more confusing to debug..
179      *
180      *  
181      *
182      */
183     addxtype  : function(tree,cntr)
184     {
185         var cn = this;
186         
187         cn = Roo.factory(tree);
188         //Roo.log(['addxtype', cn]);
189            
190         cn.parentType = this.xtype; //??
191         cn.parentId = this.id;
192         
193         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
194         if (typeof(cn.container_method) == 'string') {
195             cntr = cn.container_method;
196         }
197         
198         
199         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
200         
201         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
202         
203         var build_from_html =  Roo.XComponent.build_from_html;
204           
205         var is_body  = (tree.xtype == 'Body') ;
206           
207         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
208           
209         var self_cntr_el = Roo.get(this[cntr](false));
210         
211         // do not try and build conditional elements 
212         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
213             return false;
214         }
215         
216         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
217             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
218                 return this.addxtypeChild(tree,cntr, is_body);
219             }
220             
221             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
222                 
223             if(echild){
224                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
225             }
226             
227             Roo.log('skipping render');
228             return cn;
229             
230         }
231         
232         var ret = false;
233         if (!build_from_html) {
234             return false;
235         }
236         
237         // this i think handles overlaying multiple children of the same type
238         // with the sam eelement.. - which might be buggy..
239         while (true) {
240             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
241             
242             if (!echild) {
243                 break;
244             }
245             
246             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
247                 break;
248             }
249             
250             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
251         }
252        
253         return ret;
254     },
255     /**
256      * add a child to this element
257      *   - turn the child.cfg into a child_instance
258      *   - call child_instance.render( this { getContainerMethod()} )
259      *   - loop through the children, and call addxtype.. (reall this) on newly created child.
260      *  
261      */
262     
263     addxtypeChild : function (tree, cntr, is_body)
264     {
265         Roo.debug && Roo.log('addxtypeChild:' + cntr);
266         var cn = this;
267         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
268         
269         
270         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
271                     (typeof(tree['flexy:foreach']) != 'undefined');
272           
273     
274         
275         skip_children = false;
276         // render the element if it's not BODY.
277         if (!is_body) {
278             
279             // if parent was disabled, then do not try and create the children..
280             if(!this[cntr](true)){
281                 tree.items = [];
282                 return tree;
283             }
284            
285             cn = Roo.factory(tree);
286            
287             cn.parentType = this.xtype; //??
288             cn.parentId = this.id;
289             
290             var build_from_html =  Roo.XComponent.build_from_html;
291             
292             
293             // does the container contain child eleemnts with 'xtype' attributes.
294             // that match this xtype..
295             // note - when we render we create these as well..
296             // so we should check to see if body has xtype set.
297             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
298                
299                 var self_cntr_el = Roo.get(this[cntr](false));
300                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
301                 if (echild) { 
302                     //Roo.log(Roo.XComponent.build_from_html);
303                     //Roo.log("got echild:");
304                     //Roo.log(echild);
305                 }
306                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
307                 // and are not displayed -this causes this to use up the wrong element when matching.
308                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
309                 
310                 
311                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
312                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
313                   
314                   
315                   
316                     cn.el = echild;
317                   //  Roo.log("GOT");
318                     //echild.dom.removeAttribute('xtype');
319                 } else {
320                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
321                     Roo.debug && Roo.log(self_cntr_el);
322                     Roo.debug && Roo.log(echild);
323                     Roo.debug && Roo.log(cn);
324                 }
325             }
326            
327             
328            
329             // if object has flexy:if - then it may or may not be rendered.
330             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
331                 // skip a flexy if element.
332                 Roo.debug && Roo.log('skipping render');
333                 Roo.debug && Roo.log(tree);
334                 if (!cn.el) {
335                     Roo.debug && Roo.log('skipping all children');
336                     skip_children = true;
337                 }
338                 
339              } else {
340                  
341                 // actually if flexy:foreach is found, we really want to create 
342                 // multiple copies here...
343                 //Roo.log('render');
344                 //Roo.log(this[cntr]());
345                 // some elements do not have render methods.. like the layouts...
346                 /*
347                 if(this[cntr](true) === false){
348                     cn.items = [];
349                     return cn;
350                 }
351                 */
352                 cn.render && cn.render(this[cntr](true));
353                 
354              }
355             // then add the element..
356         }
357          
358         // handle the kids..
359         
360         var nitems = [];
361         /*
362         if (typeof (tree.menu) != 'undefined') {
363             tree.menu.parentType = cn.xtype;
364             tree.menu.triggerEl = cn.el;
365             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
366             
367         }
368         */
369         if (!tree.items || !tree.items.length) {
370             cn.items = nitems;
371             //Roo.log(["no children", this]);
372             
373             return cn;
374         }
375          
376         var items = tree.items;
377         delete tree.items;
378         
379         //Roo.log(items.length);
380             // add the items..
381         if (!skip_children) {    
382             for(var i =0;i < items.length;i++) {
383               //  Roo.log(['add child', items[i]]);
384                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
385             }
386         }
387         
388         cn.items = nitems;
389         
390         //Roo.log("fire childrenrendered");
391         
392         cn.fireEvent('childrenrendered', this);
393         
394         return cn;
395     },
396     
397     
398     addxtypeChildren: function(child_array)
399     {
400         var nitems = [];
401         if (!child_array || !child_array.length) {
402             this.items = nitems;
403             return this;
404         }
405         for(var i =0;i < child_array.length;i++) {
406         //  Roo.log(['add child', items[i]]);
407           nitems.push(this.addxtype(Roo.apply({}, child_array[i])));
408       }
409         
410         
411     },
412     
413     
414     
415     /**
416      * Set the element that will be used to show or hide
417      */
418     setVisibilityEl : function(el)
419     {
420         this.visibilityEl = el;
421     },
422     
423      /**
424      * Get the element that will be used to show or hide
425      */
426     getVisibilityEl : function()
427     {
428         if (typeof(this.visibilityEl) == 'object') {
429             return this.visibilityEl;
430         }
431         
432         if (typeof(this.visibilityEl) == 'string') {
433             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
434         }
435         
436         return this.getEl();
437     },
438     
439     /**
440      * Show a component - removes 'hidden' class
441      */
442     show : function()
443     {
444         if(!this.getVisibilityEl()){
445             return;
446         }
447          
448         this.getVisibilityEl().removeClass(['hidden','d-none']);
449         
450         this.fireEvent('show', this);
451         
452         
453     },
454     /**
455      * Hide a component - adds 'hidden' class
456      */
457     hide: function()
458     {
459         if(!this.getVisibilityEl()){
460             return;
461         }
462         
463         this.getVisibilityEl().addClass(['hidden','d-none']);
464         
465         this.fireEvent('hide', this);
466         
467     }
468 });
469
470  /*
471  * - LGPL
472  *
473  * Body
474  *
475  */
476
477 /**
478  * @class Roo.bootstrap.Body
479  * @extends Roo.bootstrap.Component
480  * Bootstrap Body class
481  *
482  * @constructor
483  * Create a new body
484  * @param {Object} config The config object
485  */
486
487 Roo.bootstrap.Body = function(config){
488
489     config = config || {};
490
491     Roo.bootstrap.Body.superclass.constructor.call(this, config);
492     this.el = Roo.get(config.el ? config.el : document.body );
493     if (this.cls && this.cls.length) {
494         Roo.get(document.body).addClass(this.cls);
495     }
496 };
497
498 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
499
500     is_body : true,// just to make sure it's constructed?
501
502         autoCreate : {
503         cls: 'container'
504     },
505     onRender : function(ct, position)
506     {
507        /* Roo.log("Roo.bootstrap.Body - onRender");
508         if (this.cls && this.cls.length) {
509             Roo.get(document.body).addClass(this.cls);
510         }
511         // style??? xttr???
512         */
513     }
514
515
516
517
518 });
519 /*
520  * - LGPL
521  *
522  * button group
523  * 
524  */
525
526
527 /**
528  * @class Roo.bootstrap.ButtonGroup
529  * @extends Roo.bootstrap.Component
530  * Bootstrap ButtonGroup class
531  * @cfg {String} size lg | sm | xs (default empty normal)
532  * @cfg {String} align vertical | justified  (default none)
533  * @cfg {String} direction up | down (default down)
534  * @cfg {Boolean} toolbar false | true
535  * @cfg {Boolean} btn true | false
536  * 
537  * 
538  * @constructor
539  * Create a new Input
540  * @param {Object} config The config object
541  */
542
543 Roo.bootstrap.ButtonGroup = function(config){
544     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
545 };
546
547 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
548     
549     size: '',
550     align: '',
551     direction: '',
552     toolbar: false,
553     btn: true,
554
555     getAutoCreate : function(){
556         var cfg = {
557             cls: 'btn-group',
558             html : null
559         };
560         
561         cfg.html = this.html || cfg.html;
562         
563         if (this.toolbar) {
564             cfg = {
565                 cls: 'btn-toolbar',
566                 html: null
567             };
568             
569             return cfg;
570         }
571         
572         if (['vertical','justified'].indexOf(this.align)!==-1) {
573             cfg.cls = 'btn-group-' + this.align;
574             
575             if (this.align == 'justified') {
576                 console.log(this.items);
577             }
578         }
579         
580         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
581             cfg.cls += ' btn-group-' + this.size;
582         }
583         
584         if (this.direction == 'up') {
585             cfg.cls += ' dropup' ;
586         }
587         
588         return cfg;
589     },
590     /**
591      * Add a button to the group (similar to NavItem API.)
592      */
593     addItem : function(cfg)
594     {
595         var cn = new Roo.bootstrap.Button(cfg);
596         //this.register(cn);
597         cn.parentId = this.id;
598         cn.onRender(this.el, null);
599         return cn;
600     }
601    
602 });
603
604  /*
605  * - LGPL
606  *
607  * button
608  * 
609  */
610
611 /**
612  * @class Roo.bootstrap.Button
613  * @extends Roo.bootstrap.Component
614  * Bootstrap Button class
615  * @cfg {String} html The button content
616  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
617  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
618  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
619  * @cfg {String} size ( lg | sm | xs)
620  * @cfg {String} tag ( a | input | submit)
621  * @cfg {String} href empty or href
622  * @cfg {Boolean} disabled default false;
623  * @cfg {Boolean} isClose default false;
624  * @cfg {String} glyphicon depricated - use fa
625  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
626  * @cfg {String} badge text for badge
627  * @cfg {String} theme (default|glow)  
628  * @cfg {Boolean} inverse dark themed version
629  * @cfg {Boolean} toggle is it a slidy toggle button
630  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
631  * @cfg {String} ontext text for on slidy toggle state
632  * @cfg {String} offtext text for off slidy toggle state
633  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
634  * @cfg {Boolean} removeClass remove the standard class..
635  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
636  * 
637  * @constructor
638  * Create a new button
639  * @param {Object} config The config object
640  */
641
642
643 Roo.bootstrap.Button = function(config){
644     Roo.bootstrap.Button.superclass.constructor.call(this, config);
645     this.weightClass = ["btn-default btn-outline-secondary", 
646                        "btn-primary", 
647                        "btn-success", 
648                        "btn-info", 
649                        "btn-warning",
650                        "btn-danger",
651                        "btn-link"
652                       ],  
653     this.addEvents({
654         // raw events
655         /**
656          * @event click
657          * When a butotn is pressed
658          * @param {Roo.bootstrap.Button} btn
659          * @param {Roo.EventObject} e
660          */
661         "click" : true,
662          /**
663          * @event toggle
664          * After the button has been toggles
665          * @param {Roo.bootstrap.Button} btn
666          * @param {Roo.EventObject} e
667          * @param {boolean} pressed (also available as button.pressed)
668          */
669         "toggle" : true
670     });
671 };
672
673 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
674     html: false,
675     active: false,
676     weight: '',
677     badge_weight: '',
678     outline : false,
679     size: '',
680     tag: 'button',
681     href: '',
682     disabled: false,
683     isClose: false,
684     glyphicon: '',
685     fa: '',
686     badge: '',
687     theme: 'default',
688     inverse: false,
689     
690     toggle: false,
691     ontext: 'ON',
692     offtext: 'OFF',
693     defaulton: true,
694     preventDefault: true,
695     removeClass: false,
696     name: false,
697     target: false,
698      
699     pressed : null,
700      
701     
702     getAutoCreate : function(){
703         
704         var cfg = {
705             tag : 'button',
706             cls : 'roo-button',
707             html: ''
708         };
709         
710         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
711             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
712             this.tag = 'button';
713         } else {
714             cfg.tag = this.tag;
715         }
716         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
717         
718         if (this.toggle == true) {
719             cfg={
720                 tag: 'div',
721                 cls: 'slider-frame roo-button',
722                 cn: [
723                     {
724                         tag: 'span',
725                         'data-on-text':'ON',
726                         'data-off-text':'OFF',
727                         cls: 'slider-button',
728                         html: this.offtext
729                     }
730                 ]
731             };
732             
733             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
734                 cfg.cls += ' '+this.weight;
735             }
736             
737             return cfg;
738         }
739         
740         if (this.isClose) {
741             cfg.cls += ' close';
742             
743             cfg["aria-hidden"] = true;
744             
745             cfg.html = "&times;";
746             
747             return cfg;
748         }
749         
750          
751         if (this.theme==='default') {
752             cfg.cls = 'btn roo-button';
753             
754             //if (this.parentType != 'Navbar') {
755             this.weight = this.weight.length ?  this.weight : 'default';
756             //}
757             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
758                 
759                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
760                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
761                 cfg.cls += ' btn-' + outline + weight;
762                 if (this.weight == 'default') {
763                     // BC
764                     cfg.cls += ' btn-' + this.weight;
765                 }
766             }
767         } else if (this.theme==='glow') {
768             
769             cfg.tag = 'a';
770             cfg.cls = 'btn-glow roo-button';
771             
772             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
773                 
774                 cfg.cls += ' ' + this.weight;
775             }
776         }
777    
778         
779         if (this.inverse) {
780             this.cls += ' inverse';
781         }
782         
783         
784         if (this.active || this.pressed === true) {
785             cfg.cls += ' active';
786         }
787         
788         if (this.disabled) {
789             cfg.disabled = 'disabled';
790         }
791         
792         if (this.items) {
793             Roo.log('changing to ul' );
794             cfg.tag = 'ul';
795             this.glyphicon = 'caret';
796             if (Roo.bootstrap.version == 4) {
797                 this.fa = 'caret-down';
798             }
799             
800         }
801         
802         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
803          
804         //gsRoo.log(this.parentType);
805         if (this.parentType === 'Navbar' && !this.parent().bar) {
806             Roo.log('changing to li?');
807             
808             cfg.tag = 'li';
809             
810             cfg.cls = '';
811             cfg.cn =  [{
812                 tag : 'a',
813                 cls : 'roo-button',
814                 html : this.html,
815                 href : this.href || '#'
816             }];
817             if (this.menu) {
818                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
819                 cfg.cls += ' dropdown';
820             }   
821             
822             delete cfg.html;
823             
824         }
825         
826        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
827         
828         if (this.glyphicon) {
829             cfg.html = ' ' + cfg.html;
830             
831             cfg.cn = [
832                 {
833                     tag: 'span',
834                     cls: 'glyphicon glyphicon-' + this.glyphicon
835                 }
836             ];
837         }
838         if (this.fa) {
839             cfg.html = ' ' + cfg.html;
840             
841             cfg.cn = [
842                 {
843                     tag: 'i',
844                     cls: 'fa fas fa-' + this.fa
845                 }
846             ];
847         }
848         
849         if (this.badge) {
850             cfg.html += ' ';
851             
852             cfg.tag = 'a';
853             
854 //            cfg.cls='btn roo-button';
855             
856             cfg.href=this.href;
857             
858             var value = cfg.html;
859             
860             if(this.glyphicon){
861                 value = {
862                     tag: 'span',
863                     cls: 'glyphicon glyphicon-' + this.glyphicon,
864                     html: this.html
865                 };
866             }
867             if(this.fa){
868                 value = {
869                     tag: 'i',
870                     cls: 'fa fas fa-' + this.fa,
871                     html: this.html
872                 };
873             }
874             
875             var bw = this.badge_weight.length ? this.badge_weight :
876                 (this.weight.length ? this.weight : 'secondary');
877             bw = bw == 'default' ? 'secondary' : bw;
878             
879             cfg.cn = [
880                 value,
881                 {
882                     tag: 'span',
883                     cls: 'badge badge-' + bw,
884                     html: this.badge
885                 }
886             ];
887             
888             cfg.html='';
889         }
890         
891         if (this.menu) {
892             cfg.cls += ' dropdown';
893             cfg.html = typeof(cfg.html) != 'undefined' ?
894                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
895         }
896         
897         if (cfg.tag !== 'a' && this.href !== '') {
898             throw "Tag must be a to set href.";
899         } else if (this.href.length > 0) {
900             cfg.href = this.href;
901         }
902         
903         if(this.removeClass){
904             cfg.cls = '';
905         }
906         
907         if(this.target){
908             cfg.target = this.target;
909         }
910         
911         return cfg;
912     },
913     initEvents: function() {
914        // Roo.log('init events?');
915 //        Roo.log(this.el.dom);
916         // add the menu...
917         
918         if (typeof (this.menu) != 'undefined') {
919             this.menu.parentType = this.xtype;
920             this.menu.triggerEl = this.el;
921             this.addxtype(Roo.apply({}, this.menu));
922         }
923
924
925        if (this.el.hasClass('roo-button')) {
926             this.el.on('click', this.onClick, this);
927        } else {
928             this.el.select('.roo-button').on('click', this.onClick, this);
929        }
930        
931        if(this.removeClass){
932            this.el.on('click', this.onClick, this);
933        }
934        
935        this.el.enableDisplayMode();
936         
937     },
938     onClick : function(e)
939     {
940         if (this.disabled) {
941             return;
942         }
943         
944         Roo.log('button on click ');
945         if(this.preventDefault){
946             e.preventDefault();
947         }
948         
949         if (this.pressed === true || this.pressed === false) {
950             this.toggleActive(e);
951         }
952         
953         
954         this.fireEvent('click', this, e);
955     },
956     
957     /**
958      * Enables this button
959      */
960     enable : function()
961     {
962         this.disabled = false;
963         this.el.removeClass('disabled');
964     },
965     
966     /**
967      * Disable this button
968      */
969     disable : function()
970     {
971         this.disabled = true;
972         this.el.addClass('disabled');
973     },
974      /**
975      * sets the active state on/off, 
976      * @param {Boolean} state (optional) Force a particular state
977      */
978     setActive : function(v) {
979         
980         this.el[v ? 'addClass' : 'removeClass']('active');
981         this.pressed = v;
982     },
983      /**
984      * toggles the current active state 
985      */
986     toggleActive : function(e)
987     {
988         this.setActive(!this.pressed);
989         this.fireEvent('toggle', this, e, !this.pressed);
990     },
991      /**
992      * get the current active state
993      * @return {boolean} true if it's active
994      */
995     isActive : function()
996     {
997         return this.el.hasClass('active');
998     },
999     /**
1000      * set the text of the first selected button
1001      */
1002     setText : function(str)
1003     {
1004         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1005     },
1006     /**
1007      * get the text of the first selected button
1008      */
1009     getText : function()
1010     {
1011         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1012     },
1013     
1014     setWeight : function(str)
1015     {
1016         this.el.removeClass(this.weightClass);
1017         this.weight = str;
1018         var outline = this.outline ? 'outline-' : '';
1019         if (str == 'default') {
1020             this.el.addClass('btn-default btn-outline-secondary');        
1021             return;
1022         }
1023         this.el.addClass('btn-' + outline + str);        
1024     }
1025     
1026     
1027 });
1028
1029  /*
1030  * - LGPL
1031  *
1032  * column
1033  * 
1034  */
1035
1036 /**
1037  * @class Roo.bootstrap.Column
1038  * @extends Roo.bootstrap.Component
1039  * Bootstrap Column class
1040  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1041  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1042  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1043  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1044  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1045  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1046  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1047  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1048  *
1049  * 
1050  * @cfg {Boolean} hidden (true|false) hide the element
1051  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1052  * @cfg {String} fa (ban|check|...) font awesome icon
1053  * @cfg {Number} fasize (1|2|....) font awsome size
1054
1055  * @cfg {String} icon (info-sign|check|...) glyphicon name
1056
1057  * @cfg {String} html content of column.
1058  * 
1059  * @constructor
1060  * Create a new Column
1061  * @param {Object} config The config object
1062  */
1063
1064 Roo.bootstrap.Column = function(config){
1065     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1066 };
1067
1068 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1069     
1070     xs: false,
1071     sm: false,
1072     md: false,
1073     lg: false,
1074     xsoff: false,
1075     smoff: false,
1076     mdoff: false,
1077     lgoff: false,
1078     html: '',
1079     offset: 0,
1080     alert: false,
1081     fa: false,
1082     icon : false,
1083     hidden : false,
1084     fasize : 1,
1085     
1086     getAutoCreate : function(){
1087         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1088         
1089         cfg = {
1090             tag: 'div',
1091             cls: 'column'
1092         };
1093         
1094         var settings=this;
1095         ['xs','sm','md','lg'].map(function(size){
1096             //Roo.log( size + ':' + settings[size]);
1097             
1098             if (settings[size+'off'] !== false) {
1099                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1100             }
1101             
1102             if (settings[size] === false) {
1103                 return;
1104             }
1105             
1106             if (!settings[size]) { // 0 = hidden
1107                 cfg.cls += ' hidden-' + size + ' hidden' + size + '-down';;
1108                 return;
1109             }
1110             cfg.cls += ' col-' + size + '-' + settings[size] + (
1111                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1112             );
1113             
1114         });
1115         
1116         if (this.hidden) {
1117             cfg.cls += ' hidden';
1118         }
1119         
1120         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1121             cfg.cls +=' alert alert-' + this.alert;
1122         }
1123         
1124         
1125         if (this.html.length) {
1126             cfg.html = this.html;
1127         }
1128         if (this.fa) {
1129             var fasize = '';
1130             if (this.fasize > 1) {
1131                 fasize = ' fa-' + this.fasize + 'x';
1132             }
1133             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1134             
1135             
1136         }
1137         if (this.icon) {
1138             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1139         }
1140         
1141         return cfg;
1142     }
1143    
1144 });
1145
1146  
1147
1148  /*
1149  * - LGPL
1150  *
1151  * page container.
1152  * 
1153  */
1154
1155
1156 /**
1157  * @class Roo.bootstrap.Container
1158  * @extends Roo.bootstrap.Component
1159  * Bootstrap Container class
1160  * @cfg {Boolean} jumbotron is it a jumbotron element
1161  * @cfg {String} html content of element
1162  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1163  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1164  * @cfg {String} header content of header (for panel)
1165  * @cfg {String} footer content of footer (for panel)
1166  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1167  * @cfg {String} tag (header|aside|section) type of HTML tag.
1168  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1169  * @cfg {String} fa font awesome icon
1170  * @cfg {String} icon (info-sign|check|...) glyphicon name
1171  * @cfg {Boolean} hidden (true|false) hide the element
1172  * @cfg {Boolean} expandable (true|false) default false
1173  * @cfg {Boolean} expanded (true|false) default true
1174  * @cfg {String} rheader contet on the right of header
1175  * @cfg {Boolean} clickable (true|false) default false
1176
1177  *     
1178  * @constructor
1179  * Create a new Container
1180  * @param {Object} config The config object
1181  */
1182
1183 Roo.bootstrap.Container = function(config){
1184     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1185     
1186     this.addEvents({
1187         // raw events
1188          /**
1189          * @event expand
1190          * After the panel has been expand
1191          * 
1192          * @param {Roo.bootstrap.Container} this
1193          */
1194         "expand" : true,
1195         /**
1196          * @event collapse
1197          * After the panel has been collapsed
1198          * 
1199          * @param {Roo.bootstrap.Container} this
1200          */
1201         "collapse" : true,
1202         /**
1203          * @event click
1204          * When a element is chick
1205          * @param {Roo.bootstrap.Container} this
1206          * @param {Roo.EventObject} e
1207          */
1208         "click" : true
1209     });
1210 };
1211
1212 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1213     
1214     jumbotron : false,
1215     well: '',
1216     panel : '',
1217     header: '',
1218     footer : '',
1219     sticky: '',
1220     tag : false,
1221     alert : false,
1222     fa: false,
1223     icon : false,
1224     expandable : false,
1225     rheader : '',
1226     expanded : true,
1227     clickable: false,
1228   
1229      
1230     getChildContainer : function() {
1231         
1232         if(!this.el){
1233             return false;
1234         }
1235         
1236         if (this.panel.length) {
1237             return this.el.select('.panel-body',true).first();
1238         }
1239         
1240         return this.el;
1241     },
1242     
1243     
1244     getAutoCreate : function(){
1245         
1246         var cfg = {
1247             tag : this.tag || 'div',
1248             html : '',
1249             cls : ''
1250         };
1251         if (this.jumbotron) {
1252             cfg.cls = 'jumbotron';
1253         }
1254         
1255         
1256         
1257         // - this is applied by the parent..
1258         //if (this.cls) {
1259         //    cfg.cls = this.cls + '';
1260         //}
1261         
1262         if (this.sticky.length) {
1263             
1264             var bd = Roo.get(document.body);
1265             if (!bd.hasClass('bootstrap-sticky')) {
1266                 bd.addClass('bootstrap-sticky');
1267                 Roo.select('html',true).setStyle('height', '100%');
1268             }
1269              
1270             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1271         }
1272         
1273         
1274         if (this.well.length) {
1275             switch (this.well) {
1276                 case 'lg':
1277                 case 'sm':
1278                     cfg.cls +=' well well-' +this.well;
1279                     break;
1280                 default:
1281                     cfg.cls +=' well';
1282                     break;
1283             }
1284         }
1285         
1286         if (this.hidden) {
1287             cfg.cls += ' hidden';
1288         }
1289         
1290         
1291         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1292             cfg.cls +=' alert alert-' + this.alert;
1293         }
1294         
1295         var body = cfg;
1296         
1297         if (this.panel.length) {
1298             cfg.cls += ' panel panel-' + this.panel;
1299             cfg.cn = [];
1300             if (this.header.length) {
1301                 
1302                 var h = [];
1303                 
1304                 if(this.expandable){
1305                     
1306                     cfg.cls = cfg.cls + ' expandable';
1307                     
1308                     h.push({
1309                         tag: 'i',
1310                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1311                     });
1312                     
1313                 }
1314                 
1315                 h.push(
1316                     {
1317                         tag: 'span',
1318                         cls : 'panel-title',
1319                         html : (this.expandable ? '&nbsp;' : '') + this.header
1320                     },
1321                     {
1322                         tag: 'span',
1323                         cls: 'panel-header-right',
1324                         html: this.rheader
1325                     }
1326                 );
1327                 
1328                 cfg.cn.push({
1329                     cls : 'panel-heading',
1330                     style : this.expandable ? 'cursor: pointer' : '',
1331                     cn : h
1332                 });
1333                 
1334             }
1335             
1336             body = false;
1337             cfg.cn.push({
1338                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1339                 html : this.html
1340             });
1341             
1342             
1343             if (this.footer.length) {
1344                 cfg.cn.push({
1345                     cls : 'panel-footer',
1346                     html : this.footer
1347                     
1348                 });
1349             }
1350             
1351         }
1352         
1353         if (body) {
1354             body.html = this.html || cfg.html;
1355             // prefix with the icons..
1356             if (this.fa) {
1357                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1358             }
1359             if (this.icon) {
1360                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1361             }
1362             
1363             
1364         }
1365         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1366             cfg.cls =  'container';
1367         }
1368         
1369         return cfg;
1370     },
1371     
1372     initEvents: function() 
1373     {
1374         if(this.expandable){
1375             var headerEl = this.headerEl();
1376         
1377             if(headerEl){
1378                 headerEl.on('click', this.onToggleClick, this);
1379             }
1380         }
1381         
1382         if(this.clickable){
1383             this.el.on('click', this.onClick, this);
1384         }
1385         
1386     },
1387     
1388     onToggleClick : function()
1389     {
1390         var headerEl = this.headerEl();
1391         
1392         if(!headerEl){
1393             return;
1394         }
1395         
1396         if(this.expanded){
1397             this.collapse();
1398             return;
1399         }
1400         
1401         this.expand();
1402     },
1403     
1404     expand : function()
1405     {
1406         if(this.fireEvent('expand', this)) {
1407             
1408             this.expanded = true;
1409             
1410             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1411             
1412             this.el.select('.panel-body',true).first().removeClass('hide');
1413             
1414             var toggleEl = this.toggleEl();
1415
1416             if(!toggleEl){
1417                 return;
1418             }
1419
1420             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1421         }
1422         
1423     },
1424     
1425     collapse : function()
1426     {
1427         if(this.fireEvent('collapse', this)) {
1428             
1429             this.expanded = false;
1430             
1431             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1432             this.el.select('.panel-body',true).first().addClass('hide');
1433         
1434             var toggleEl = this.toggleEl();
1435
1436             if(!toggleEl){
1437                 return;
1438             }
1439
1440             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1441         }
1442     },
1443     
1444     toggleEl : function()
1445     {
1446         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1447             return;
1448         }
1449         
1450         return this.el.select('.panel-heading .fa',true).first();
1451     },
1452     
1453     headerEl : function()
1454     {
1455         if(!this.el || !this.panel.length || !this.header.length){
1456             return;
1457         }
1458         
1459         return this.el.select('.panel-heading',true).first()
1460     },
1461     
1462     bodyEl : function()
1463     {
1464         if(!this.el || !this.panel.length){
1465             return;
1466         }
1467         
1468         return this.el.select('.panel-body',true).first()
1469     },
1470     
1471     titleEl : function()
1472     {
1473         if(!this.el || !this.panel.length || !this.header.length){
1474             return;
1475         }
1476         
1477         return this.el.select('.panel-title',true).first();
1478     },
1479     
1480     setTitle : function(v)
1481     {
1482         var titleEl = this.titleEl();
1483         
1484         if(!titleEl){
1485             return;
1486         }
1487         
1488         titleEl.dom.innerHTML = v;
1489     },
1490     
1491     getTitle : function()
1492     {
1493         
1494         var titleEl = this.titleEl();
1495         
1496         if(!titleEl){
1497             return '';
1498         }
1499         
1500         return titleEl.dom.innerHTML;
1501     },
1502     
1503     setRightTitle : function(v)
1504     {
1505         var t = this.el.select('.panel-header-right',true).first();
1506         
1507         if(!t){
1508             return;
1509         }
1510         
1511         t.dom.innerHTML = v;
1512     },
1513     
1514     onClick : function(e)
1515     {
1516         e.preventDefault();
1517         
1518         this.fireEvent('click', this, e);
1519     }
1520 });
1521
1522  /*
1523  * - LGPL
1524  *
1525  * image
1526  * 
1527  */
1528
1529
1530 /**
1531  * @class Roo.bootstrap.Img
1532  * @extends Roo.bootstrap.Component
1533  * Bootstrap Img class
1534  * @cfg {Boolean} imgResponsive false | true
1535  * @cfg {String} border rounded | circle | thumbnail
1536  * @cfg {String} src image source
1537  * @cfg {String} alt image alternative text
1538  * @cfg {String} href a tag href
1539  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1540  * @cfg {String} xsUrl xs image source
1541  * @cfg {String} smUrl sm image source
1542  * @cfg {String} mdUrl md image source
1543  * @cfg {String} lgUrl lg image source
1544  * 
1545  * @constructor
1546  * Create a new Input
1547  * @param {Object} config The config object
1548  */
1549
1550 Roo.bootstrap.Img = function(config){
1551     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1552     
1553     this.addEvents({
1554         // img events
1555         /**
1556          * @event click
1557          * The img click event for the img.
1558          * @param {Roo.EventObject} e
1559          */
1560         "click" : true
1561     });
1562 };
1563
1564 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1565     
1566     imgResponsive: true,
1567     border: '',
1568     src: 'about:blank',
1569     href: false,
1570     target: false,
1571     xsUrl: '',
1572     smUrl: '',
1573     mdUrl: '',
1574     lgUrl: '',
1575
1576     getAutoCreate : function()
1577     {   
1578         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1579             return this.createSingleImg();
1580         }
1581         
1582         var cfg = {
1583             tag: 'div',
1584             cls: 'roo-image-responsive-group',
1585             cn: []
1586         };
1587         var _this = this;
1588         
1589         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1590             
1591             if(!_this[size + 'Url']){
1592                 return;
1593             }
1594             
1595             var img = {
1596                 tag: 'img',
1597                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1598                 html: _this.html || cfg.html,
1599                 src: _this[size + 'Url']
1600             };
1601             
1602             img.cls += ' roo-image-responsive-' + size;
1603             
1604             var s = ['xs', 'sm', 'md', 'lg'];
1605             
1606             s.splice(s.indexOf(size), 1);
1607             
1608             Roo.each(s, function(ss){
1609                 img.cls += ' hidden-' + ss;
1610             });
1611             
1612             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1613                 cfg.cls += ' img-' + _this.border;
1614             }
1615             
1616             if(_this.alt){
1617                 cfg.alt = _this.alt;
1618             }
1619             
1620             if(_this.href){
1621                 var a = {
1622                     tag: 'a',
1623                     href: _this.href,
1624                     cn: [
1625                         img
1626                     ]
1627                 };
1628
1629                 if(this.target){
1630                     a.target = _this.target;
1631                 }
1632             }
1633             
1634             cfg.cn.push((_this.href) ? a : img);
1635             
1636         });
1637         
1638         return cfg;
1639     },
1640     
1641     createSingleImg : function()
1642     {
1643         var cfg = {
1644             tag: 'img',
1645             cls: (this.imgResponsive) ? 'img-responsive' : '',
1646             html : null,
1647             src : 'about:blank'  // just incase src get's set to undefined?!?
1648         };
1649         
1650         cfg.html = this.html || cfg.html;
1651         
1652         cfg.src = this.src || cfg.src;
1653         
1654         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1655             cfg.cls += ' img-' + this.border;
1656         }
1657         
1658         if(this.alt){
1659             cfg.alt = this.alt;
1660         }
1661         
1662         if(this.href){
1663             var a = {
1664                 tag: 'a',
1665                 href: this.href,
1666                 cn: [
1667                     cfg
1668                 ]
1669             };
1670             
1671             if(this.target){
1672                 a.target = this.target;
1673             }
1674             
1675         }
1676         
1677         return (this.href) ? a : cfg;
1678     },
1679     
1680     initEvents: function() 
1681     {
1682         if(!this.href){
1683             this.el.on('click', this.onClick, this);
1684         }
1685         
1686     },
1687     
1688     onClick : function(e)
1689     {
1690         Roo.log('img onclick');
1691         this.fireEvent('click', this, e);
1692     },
1693     /**
1694      * Sets the url of the image - used to update it
1695      * @param {String} url the url of the image
1696      */
1697     
1698     setSrc : function(url)
1699     {
1700         this.src =  url;
1701         
1702         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1703             this.el.dom.src =  url;
1704             return;
1705         }
1706         
1707         this.el.select('img', true).first().dom.src =  url;
1708     }
1709     
1710     
1711    
1712 });
1713
1714  /*
1715  * - LGPL
1716  *
1717  * image
1718  * 
1719  */
1720
1721
1722 /**
1723  * @class Roo.bootstrap.Link
1724  * @extends Roo.bootstrap.Component
1725  * Bootstrap Link Class
1726  * @cfg {String} alt image alternative text
1727  * @cfg {String} href a tag href
1728  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1729  * @cfg {String} html the content of the link.
1730  * @cfg {String} anchor name for the anchor link
1731  * @cfg {String} fa - favicon
1732
1733  * @cfg {Boolean} preventDefault (true | false) default false
1734
1735  * 
1736  * @constructor
1737  * Create a new Input
1738  * @param {Object} config The config object
1739  */
1740
1741 Roo.bootstrap.Link = function(config){
1742     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1743     
1744     this.addEvents({
1745         // img events
1746         /**
1747          * @event click
1748          * The img click event for the img.
1749          * @param {Roo.EventObject} e
1750          */
1751         "click" : true
1752     });
1753 };
1754
1755 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1756     
1757     href: false,
1758     target: false,
1759     preventDefault: false,
1760     anchor : false,
1761     alt : false,
1762     fa: false,
1763
1764
1765     getAutoCreate : function()
1766     {
1767         var html = this.html || '';
1768         
1769         if (this.fa !== false) {
1770             html = '<i class="fa fa-' + this.fa + '"></i>';
1771         }
1772         var cfg = {
1773             tag: 'a'
1774         };
1775         // anchor's do not require html/href...
1776         if (this.anchor === false) {
1777             cfg.html = html;
1778             cfg.href = this.href || '#';
1779         } else {
1780             cfg.name = this.anchor;
1781             if (this.html !== false || this.fa !== false) {
1782                 cfg.html = html;
1783             }
1784             if (this.href !== false) {
1785                 cfg.href = this.href;
1786             }
1787         }
1788         
1789         if(this.alt !== false){
1790             cfg.alt = this.alt;
1791         }
1792         
1793         
1794         if(this.target !== false) {
1795             cfg.target = this.target;
1796         }
1797         
1798         return cfg;
1799     },
1800     
1801     initEvents: function() {
1802         
1803         if(!this.href || this.preventDefault){
1804             this.el.on('click', this.onClick, this);
1805         }
1806     },
1807     
1808     onClick : function(e)
1809     {
1810         if(this.preventDefault){
1811             e.preventDefault();
1812         }
1813         //Roo.log('img onclick');
1814         this.fireEvent('click', this, e);
1815     }
1816    
1817 });
1818
1819  /*
1820  * - LGPL
1821  *
1822  * header
1823  * 
1824  */
1825
1826 /**
1827  * @class Roo.bootstrap.Header
1828  * @extends Roo.bootstrap.Component
1829  * Bootstrap Header class
1830  * @cfg {String} html content of header
1831  * @cfg {Number} level (1|2|3|4|5|6) default 1
1832  * 
1833  * @constructor
1834  * Create a new Header
1835  * @param {Object} config The config object
1836  */
1837
1838
1839 Roo.bootstrap.Header  = function(config){
1840     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1841 };
1842
1843 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1844     
1845     //href : false,
1846     html : false,
1847     level : 1,
1848     
1849     
1850     
1851     getAutoCreate : function(){
1852         
1853         
1854         
1855         var cfg = {
1856             tag: 'h' + (1 *this.level),
1857             html: this.html || ''
1858         } ;
1859         
1860         return cfg;
1861     }
1862    
1863 });
1864
1865  
1866
1867  /*
1868  * Based on:
1869  * Ext JS Library 1.1.1
1870  * Copyright(c) 2006-2007, Ext JS, LLC.
1871  *
1872  * Originally Released Under LGPL - original licence link has changed is not relivant.
1873  *
1874  * Fork - LGPL
1875  * <script type="text/javascript">
1876  */
1877  
1878 /**
1879  * @class Roo.bootstrap.MenuMgr
1880  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1881  * @singleton
1882  */
1883 Roo.bootstrap.MenuMgr = function(){
1884    var menus, active, groups = {}, attached = false, lastShow = new Date();
1885
1886    // private - called when first menu is created
1887    function init(){
1888        menus = {};
1889        active = new Roo.util.MixedCollection();
1890        Roo.get(document).addKeyListener(27, function(){
1891            if(active.length > 0){
1892                hideAll();
1893            }
1894        });
1895    }
1896
1897    // private
1898    function hideAll(){
1899        if(active && active.length > 0){
1900            var c = active.clone();
1901            c.each(function(m){
1902                m.hide();
1903            });
1904        }
1905    }
1906
1907    // private
1908    function onHide(m){
1909        active.remove(m);
1910        if(active.length < 1){
1911            Roo.get(document).un("mouseup", onMouseDown);
1912             
1913            attached = false;
1914        }
1915    }
1916
1917    // private
1918    function onShow(m){
1919        var last = active.last();
1920        lastShow = new Date();
1921        active.add(m);
1922        if(!attached){
1923           Roo.get(document).on("mouseup", onMouseDown);
1924            
1925            attached = true;
1926        }
1927        if(m.parentMenu){
1928           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1929           m.parentMenu.activeChild = m;
1930        }else if(last && last.isVisible()){
1931           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1932        }
1933    }
1934
1935    // private
1936    function onBeforeHide(m){
1937        if(m.activeChild){
1938            m.activeChild.hide();
1939        }
1940        if(m.autoHideTimer){
1941            clearTimeout(m.autoHideTimer);
1942            delete m.autoHideTimer;
1943        }
1944    }
1945
1946    // private
1947    function onBeforeShow(m){
1948        var pm = m.parentMenu;
1949        if(!pm && !m.allowOtherMenus){
1950            hideAll();
1951        }else if(pm && pm.activeChild && active != m){
1952            pm.activeChild.hide();
1953        }
1954    }
1955
1956    // private this should really trigger on mouseup..
1957    function onMouseDown(e){
1958         Roo.log("on Mouse Up");
1959         
1960         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1961             Roo.log("MenuManager hideAll");
1962             hideAll();
1963             e.stopEvent();
1964         }
1965         
1966         
1967    }
1968
1969    // private
1970    function onBeforeCheck(mi, state){
1971        if(state){
1972            var g = groups[mi.group];
1973            for(var i = 0, l = g.length; i < l; i++){
1974                if(g[i] != mi){
1975                    g[i].setChecked(false);
1976                }
1977            }
1978        }
1979    }
1980
1981    return {
1982
1983        /**
1984         * Hides all menus that are currently visible
1985         */
1986        hideAll : function(){
1987             hideAll();  
1988        },
1989
1990        // private
1991        register : function(menu){
1992            if(!menus){
1993                init();
1994            }
1995            menus[menu.id] = menu;
1996            menu.on("beforehide", onBeforeHide);
1997            menu.on("hide", onHide);
1998            menu.on("beforeshow", onBeforeShow);
1999            menu.on("show", onShow);
2000            var g = menu.group;
2001            if(g && menu.events["checkchange"]){
2002                if(!groups[g]){
2003                    groups[g] = [];
2004                }
2005                groups[g].push(menu);
2006                menu.on("checkchange", onCheck);
2007            }
2008        },
2009
2010         /**
2011          * Returns a {@link Roo.menu.Menu} object
2012          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
2013          * be used to generate and return a new Menu instance.
2014          */
2015        get : function(menu){
2016            if(typeof menu == "string"){ // menu id
2017                return menus[menu];
2018            }else if(menu.events){  // menu instance
2019                return menu;
2020            }
2021            /*else if(typeof menu.length == 'number'){ // array of menu items?
2022                return new Roo.bootstrap.Menu({items:menu});
2023            }else{ // otherwise, must be a config
2024                return new Roo.bootstrap.Menu(menu);
2025            }
2026            */
2027            return false;
2028        },
2029
2030        // private
2031        unregister : function(menu){
2032            delete menus[menu.id];
2033            menu.un("beforehide", onBeforeHide);
2034            menu.un("hide", onHide);
2035            menu.un("beforeshow", onBeforeShow);
2036            menu.un("show", onShow);
2037            var g = menu.group;
2038            if(g && menu.events["checkchange"]){
2039                groups[g].remove(menu);
2040                menu.un("checkchange", onCheck);
2041            }
2042        },
2043
2044        // private
2045        registerCheckable : function(menuItem){
2046            var g = menuItem.group;
2047            if(g){
2048                if(!groups[g]){
2049                    groups[g] = [];
2050                }
2051                groups[g].push(menuItem);
2052                menuItem.on("beforecheckchange", onBeforeCheck);
2053            }
2054        },
2055
2056        // private
2057        unregisterCheckable : function(menuItem){
2058            var g = menuItem.group;
2059            if(g){
2060                groups[g].remove(menuItem);
2061                menuItem.un("beforecheckchange", onBeforeCheck);
2062            }
2063        }
2064    };
2065 }();/*
2066  * - LGPL
2067  *
2068  * menu
2069  * 
2070  */
2071
2072 /**
2073  * @class Roo.bootstrap.Menu
2074  * @extends Roo.bootstrap.Component
2075  * Bootstrap Menu class - container for MenuItems
2076  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2077  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2078  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2079  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2080  * 
2081  * @constructor
2082  * Create a new Menu
2083  * @param {Object} config The config object
2084  */
2085
2086
2087 Roo.bootstrap.Menu = function(config){
2088     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2089     if (this.registerMenu && this.type != 'treeview')  {
2090         Roo.bootstrap.MenuMgr.register(this);
2091     }
2092     
2093     
2094     this.addEvents({
2095         /**
2096          * @event beforeshow
2097          * Fires before this menu is displayed (return false to block)
2098          * @param {Roo.menu.Menu} this
2099          */
2100         beforeshow : true,
2101         /**
2102          * @event beforehide
2103          * Fires before this menu is hidden (return false to block)
2104          * @param {Roo.menu.Menu} this
2105          */
2106         beforehide : true,
2107         /**
2108          * @event show
2109          * Fires after this menu is displayed
2110          * @param {Roo.menu.Menu} this
2111          */
2112         show : true,
2113         /**
2114          * @event hide
2115          * Fires after this menu is hidden
2116          * @param {Roo.menu.Menu} this
2117          */
2118         hide : true,
2119         /**
2120          * @event click
2121          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2122          * @param {Roo.menu.Menu} this
2123          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2124          * @param {Roo.EventObject} e
2125          */
2126         click : true,
2127         /**
2128          * @event mouseover
2129          * Fires when the mouse is hovering over this menu
2130          * @param {Roo.menu.Menu} this
2131          * @param {Roo.EventObject} e
2132          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2133          */
2134         mouseover : true,
2135         /**
2136          * @event mouseout
2137          * Fires when the mouse exits this menu
2138          * @param {Roo.menu.Menu} this
2139          * @param {Roo.EventObject} e
2140          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2141          */
2142         mouseout : true,
2143         /**
2144          * @event itemclick
2145          * Fires when a menu item contained in this menu is clicked
2146          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2147          * @param {Roo.EventObject} e
2148          */
2149         itemclick: true
2150     });
2151     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2152 };
2153
2154 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2155     
2156    /// html : false,
2157     //align : '',
2158     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2159     type: false,
2160     /**
2161      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2162      */
2163     registerMenu : true,
2164     
2165     menuItems :false, // stores the menu items..
2166     
2167     hidden:true,
2168         
2169     parentMenu : false,
2170     
2171     stopEvent : true,
2172     
2173     isLink : false,
2174     
2175     getChildContainer : function() {
2176         return this.el;  
2177     },
2178     
2179     getAutoCreate : function(){
2180          
2181         //if (['right'].indexOf(this.align)!==-1) {
2182         //    cfg.cn[1].cls += ' pull-right'
2183         //}
2184         
2185         
2186         var cfg = {
2187             tag : 'ul',
2188             cls : 'dropdown-menu' ,
2189             style : 'z-index:1000'
2190             
2191         };
2192         
2193         if (this.type === 'submenu') {
2194             cfg.cls = 'submenu active';
2195         }
2196         if (this.type === 'treeview') {
2197             cfg.cls = 'treeview-menu';
2198         }
2199         
2200         return cfg;
2201     },
2202     initEvents : function() {
2203         
2204        // Roo.log("ADD event");
2205        // Roo.log(this.triggerEl.dom);
2206         
2207         this.triggerEl.on('click', this.onTriggerClick, this);
2208         
2209         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2210         
2211         
2212         if (this.triggerEl.hasClass('nav-item')) {
2213             // dropdown toggle on the 'a' in BS4?
2214             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2215         } else {
2216             this.triggerEl.addClass('dropdown-toggle');
2217         }
2218         if (Roo.isTouch) {
2219             this.el.on('touchstart'  , this.onTouch, this);
2220         }
2221         this.el.on('click' , this.onClick, this);
2222
2223         this.el.on("mouseover", this.onMouseOver, this);
2224         this.el.on("mouseout", this.onMouseOut, this);
2225         
2226     },
2227     
2228     findTargetItem : function(e)
2229     {
2230         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2231         if(!t){
2232             return false;
2233         }
2234         //Roo.log(t);         Roo.log(t.id);
2235         if(t && t.id){
2236             //Roo.log(this.menuitems);
2237             return this.menuitems.get(t.id);
2238             
2239             //return this.items.get(t.menuItemId);
2240         }
2241         
2242         return false;
2243     },
2244     
2245     onTouch : function(e) 
2246     {
2247         Roo.log("menu.onTouch");
2248         //e.stopEvent(); this make the user popdown broken
2249         this.onClick(e);
2250     },
2251     
2252     onClick : function(e)
2253     {
2254         Roo.log("menu.onClick");
2255         
2256         var t = this.findTargetItem(e);
2257         if(!t || t.isContainer){
2258             return;
2259         }
2260         Roo.log(e);
2261         /*
2262         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2263             if(t == this.activeItem && t.shouldDeactivate(e)){
2264                 this.activeItem.deactivate();
2265                 delete this.activeItem;
2266                 return;
2267             }
2268             if(t.canActivate){
2269                 this.setActiveItem(t, true);
2270             }
2271             return;
2272             
2273             
2274         }
2275         */
2276        
2277         Roo.log('pass click event');
2278         
2279         t.onClick(e);
2280         
2281         this.fireEvent("click", this, t, e);
2282         
2283         var _this = this;
2284         
2285         if(!t.href.length || t.href == '#'){
2286             (function() { _this.hide(); }).defer(100);
2287         }
2288         
2289     },
2290     
2291     onMouseOver : function(e){
2292         var t  = this.findTargetItem(e);
2293         //Roo.log(t);
2294         //if(t){
2295         //    if(t.canActivate && !t.disabled){
2296         //        this.setActiveItem(t, true);
2297         //    }
2298         //}
2299         
2300         this.fireEvent("mouseover", this, e, t);
2301     },
2302     isVisible : function(){
2303         return !this.hidden;
2304     },
2305     onMouseOut : function(e){
2306         var t  = this.findTargetItem(e);
2307         
2308         //if(t ){
2309         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2310         //        this.activeItem.deactivate();
2311         //        delete this.activeItem;
2312         //    }
2313         //}
2314         this.fireEvent("mouseout", this, e, t);
2315     },
2316     
2317     
2318     /**
2319      * Displays this menu relative to another element
2320      * @param {String/HTMLElement/Roo.Element} element The element to align to
2321      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2322      * the element (defaults to this.defaultAlign)
2323      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2324      */
2325     show : function(el, pos, parentMenu)
2326     {
2327         if (false === this.fireEvent("beforeshow", this)) {
2328             Roo.log("show canceled");
2329             return;
2330         }
2331         this.parentMenu = parentMenu;
2332         if(!this.el){
2333             this.render();
2334         }
2335         
2336         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2337     },
2338      /**
2339      * Displays this menu at a specific xy position
2340      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2341      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2342      */
2343     showAt : function(xy, parentMenu, /* private: */_e){
2344         this.parentMenu = parentMenu;
2345         if(!this.el){
2346             this.render();
2347         }
2348         if(_e !== false){
2349             this.fireEvent("beforeshow", this);
2350             //xy = this.el.adjustForConstraints(xy);
2351         }
2352         
2353         //this.el.show();
2354         this.hideMenuItems();
2355         this.hidden = false;
2356         this.triggerEl.addClass('open');
2357         this.el.addClass('show');
2358         
2359         // reassign x when hitting right
2360         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2361             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2362         }
2363         
2364         // reassign y when hitting bottom
2365         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2366             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2367         }
2368         
2369         // but the list may align on trigger left or trigger top... should it be a properity?
2370         
2371         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2372             this.el.setXY(xy);
2373         }
2374         
2375         this.focus();
2376         this.fireEvent("show", this);
2377     },
2378     
2379     focus : function(){
2380         return;
2381         if(!this.hidden){
2382             this.doFocus.defer(50, this);
2383         }
2384     },
2385
2386     doFocus : function(){
2387         if(!this.hidden){
2388             this.focusEl.focus();
2389         }
2390     },
2391
2392     /**
2393      * Hides this menu and optionally all parent menus
2394      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2395      */
2396     hide : function(deep)
2397     {
2398         if (false === this.fireEvent("beforehide", this)) {
2399             Roo.log("hide canceled");
2400             return;
2401         }
2402         this.hideMenuItems();
2403         if(this.el && this.isVisible()){
2404            
2405             if(this.activeItem){
2406                 this.activeItem.deactivate();
2407                 this.activeItem = null;
2408             }
2409             this.triggerEl.removeClass('open');;
2410             this.el.removeClass('show');
2411             this.hidden = true;
2412             this.fireEvent("hide", this);
2413         }
2414         if(deep === true && this.parentMenu){
2415             this.parentMenu.hide(true);
2416         }
2417     },
2418     
2419     onTriggerClick : function(e)
2420     {
2421         Roo.log('trigger click');
2422         
2423         var target = e.getTarget();
2424         
2425         Roo.log(target.nodeName.toLowerCase());
2426         
2427         if(target.nodeName.toLowerCase() === 'i'){
2428             e.preventDefault();
2429         }
2430         
2431     },
2432     
2433     onTriggerPress  : function(e)
2434     {
2435         Roo.log('trigger press');
2436         //Roo.log(e.getTarget());
2437        // Roo.log(this.triggerEl.dom);
2438        
2439         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2440         var pel = Roo.get(e.getTarget());
2441         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2442             Roo.log('is treeview or dropdown?');
2443             return;
2444         }
2445         
2446         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2447             return;
2448         }
2449         
2450         if (this.isVisible()) {
2451             Roo.log('hide');
2452             this.hide();
2453         } else {
2454             Roo.log('show');
2455             this.show(this.triggerEl, '?', false);
2456         }
2457         
2458         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2459             e.stopEvent();
2460         }
2461         
2462     },
2463        
2464     
2465     hideMenuItems : function()
2466     {
2467         Roo.log("hide Menu Items");
2468         if (!this.el) { 
2469             return;
2470         }
2471         
2472         this.el.select('.open',true).each(function(aa) {
2473             
2474             aa.removeClass('open');
2475          
2476         });
2477     },
2478     addxtypeChild : function (tree, cntr) {
2479         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2480           
2481         this.menuitems.add(comp);
2482         return comp;
2483
2484     },
2485     getEl : function()
2486     {
2487         Roo.log(this.el);
2488         return this.el;
2489     },
2490     
2491     clear : function()
2492     {
2493         this.getEl().dom.innerHTML = '';
2494         this.menuitems.clear();
2495     }
2496 });
2497
2498  
2499  /*
2500  * - LGPL
2501  *
2502  * menu item
2503  * 
2504  */
2505
2506
2507 /**
2508  * @class Roo.bootstrap.MenuItem
2509  * @extends Roo.bootstrap.Component
2510  * Bootstrap MenuItem class
2511  * @cfg {String} html the menu label
2512  * @cfg {String} href the link
2513  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2514  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2515  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2516  * @cfg {String} fa favicon to show on left of menu item.
2517  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2518  * 
2519  * 
2520  * @constructor
2521  * Create a new MenuItem
2522  * @param {Object} config The config object
2523  */
2524
2525
2526 Roo.bootstrap.MenuItem = function(config){
2527     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2528     this.addEvents({
2529         // raw events
2530         /**
2531          * @event click
2532          * The raw click event for the entire grid.
2533          * @param {Roo.bootstrap.MenuItem} this
2534          * @param {Roo.EventObject} e
2535          */
2536         "click" : true
2537     });
2538 };
2539
2540 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2541     
2542     href : false,
2543     html : false,
2544     preventDefault: false,
2545     isContainer : false,
2546     active : false,
2547     fa: false,
2548     
2549     getAutoCreate : function(){
2550         
2551         if(this.isContainer){
2552             return {
2553                 tag: 'li',
2554                 cls: 'dropdown-menu-item '
2555             };
2556         }
2557         var ctag = {
2558             tag: 'span',
2559             html: 'Link'
2560         };
2561         
2562         var anc = {
2563             tag : 'a',
2564             cls : 'dropdown-item',
2565             href : '#',
2566             cn : [  ]
2567         };
2568         
2569         if (this.fa !== false) {
2570             anc.cn.push({
2571                 tag : 'i',
2572                 cls : 'fa fa-' + this.fa
2573             });
2574         }
2575         
2576         anc.cn.push(ctag);
2577         
2578         
2579         var cfg= {
2580             tag: 'li',
2581             cls: 'dropdown-menu-item',
2582             cn: [ anc ]
2583         };
2584         if (this.parent().type == 'treeview') {
2585             cfg.cls = 'treeview-menu';
2586         }
2587         if (this.active) {
2588             cfg.cls += ' active';
2589         }
2590         
2591         
2592         
2593         anc.href = this.href || cfg.cn[0].href ;
2594         ctag.html = this.html || cfg.cn[0].html ;
2595         return cfg;
2596     },
2597     
2598     initEvents: function()
2599     {
2600         if (this.parent().type == 'treeview') {
2601             this.el.select('a').on('click', this.onClick, this);
2602         }
2603         
2604         if (this.menu) {
2605             this.menu.parentType = this.xtype;
2606             this.menu.triggerEl = this.el;
2607             this.menu = this.addxtype(Roo.apply({}, this.menu));
2608         }
2609         
2610     },
2611     onClick : function(e)
2612     {
2613         Roo.log('item on click ');
2614         
2615         if(this.preventDefault){
2616             e.preventDefault();
2617         }
2618         //this.parent().hideMenuItems();
2619         
2620         this.fireEvent('click', this, e);
2621     },
2622     getEl : function()
2623     {
2624         return this.el;
2625     } 
2626 });
2627
2628  
2629
2630  /*
2631  * - LGPL
2632  *
2633  * menu separator
2634  * 
2635  */
2636
2637
2638 /**
2639  * @class Roo.bootstrap.MenuSeparator
2640  * @extends Roo.bootstrap.Component
2641  * Bootstrap MenuSeparator class
2642  * 
2643  * @constructor
2644  * Create a new MenuItem
2645  * @param {Object} config The config object
2646  */
2647
2648
2649 Roo.bootstrap.MenuSeparator = function(config){
2650     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2651 };
2652
2653 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2654     
2655     getAutoCreate : function(){
2656         var cfg = {
2657             cls: 'divider',
2658             tag : 'li'
2659         };
2660         
2661         return cfg;
2662     }
2663    
2664 });
2665
2666  
2667
2668  
2669 /*
2670 * Licence: LGPL
2671 */
2672
2673 /**
2674  * @class Roo.bootstrap.Modal
2675  * @extends Roo.bootstrap.Component
2676  * Bootstrap Modal class
2677  * @cfg {String} title Title of dialog
2678  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2679  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2680  * @cfg {Boolean} specificTitle default false
2681  * @cfg {Array} buttons Array of buttons or standard button set..
2682  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2683  * @cfg {Boolean} animate default true
2684  * @cfg {Boolean} allow_close default true
2685  * @cfg {Boolean} fitwindow default false
2686  * @cfg {String} size (sm|lg) default empty
2687  * @cfg {Number} max_width set the max width of modal
2688  *
2689  *
2690  * @constructor
2691  * Create a new Modal Dialog
2692  * @param {Object} config The config object
2693  */
2694
2695 Roo.bootstrap.Modal = function(config){
2696     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2697     this.addEvents({
2698         // raw events
2699         /**
2700          * @event btnclick
2701          * The raw btnclick event for the button
2702          * @param {Roo.EventObject} e
2703          */
2704         "btnclick" : true,
2705         /**
2706          * @event resize
2707          * Fire when dialog resize
2708          * @param {Roo.bootstrap.Modal} this
2709          * @param {Roo.EventObject} e
2710          */
2711         "resize" : true
2712     });
2713     this.buttons = this.buttons || [];
2714
2715     if (this.tmpl) {
2716         this.tmpl = Roo.factory(this.tmpl);
2717     }
2718
2719 };
2720
2721 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2722
2723     title : 'test dialog',
2724
2725     buttons : false,
2726
2727     // set on load...
2728
2729     html: false,
2730
2731     tmp: false,
2732
2733     specificTitle: false,
2734
2735     buttonPosition: 'right',
2736
2737     allow_close : true,
2738
2739     animate : true,
2740
2741     fitwindow: false,
2742     
2743      // private
2744     dialogEl: false,
2745     bodyEl:  false,
2746     footerEl:  false,
2747     titleEl:  false,
2748     closeEl:  false,
2749
2750     size: '',
2751     
2752     max_width: 0,
2753     
2754     max_height: 0,
2755     
2756     fit_content: false,
2757
2758     onRender : function(ct, position)
2759     {
2760         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2761
2762         if(!this.el){
2763             var cfg = Roo.apply({},  this.getAutoCreate());
2764             cfg.id = Roo.id();
2765             //if(!cfg.name){
2766             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2767             //}
2768             //if (!cfg.name.length) {
2769             //    delete cfg.name;
2770            // }
2771             if (this.cls) {
2772                 cfg.cls += ' ' + this.cls;
2773             }
2774             if (this.style) {
2775                 cfg.style = this.style;
2776             }
2777             this.el = Roo.get(document.body).createChild(cfg, position);
2778         }
2779         //var type = this.el.dom.type;
2780
2781
2782         if(this.tabIndex !== undefined){
2783             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2784         }
2785
2786         this.dialogEl = this.el.select('.modal-dialog',true).first();
2787         this.bodyEl = this.el.select('.modal-body',true).first();
2788         this.closeEl = this.el.select('.modal-header .close', true).first();
2789         this.headerEl = this.el.select('.modal-header',true).first();
2790         this.titleEl = this.el.select('.modal-title',true).first();
2791         this.footerEl = this.el.select('.modal-footer',true).first();
2792
2793         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2794         
2795         //this.el.addClass("x-dlg-modal");
2796
2797         if (this.buttons.length) {
2798             Roo.each(this.buttons, function(bb) {
2799                 var b = Roo.apply({}, bb);
2800                 b.xns = b.xns || Roo.bootstrap;
2801                 b.xtype = b.xtype || 'Button';
2802                 if (typeof(b.listeners) == 'undefined') {
2803                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2804                 }
2805
2806                 var btn = Roo.factory(b);
2807
2808                 btn.render(this.getButtonContainer());
2809
2810             },this);
2811         }
2812         // render the children.
2813         var nitems = [];
2814
2815         if(typeof(this.items) != 'undefined'){
2816             var items = this.items;
2817             delete this.items;
2818
2819             for(var i =0;i < items.length;i++) {
2820                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2821             }
2822         }
2823
2824         this.items = nitems;
2825
2826         // where are these used - they used to be body/close/footer
2827
2828
2829         this.initEvents();
2830         //this.el.addClass([this.fieldClass, this.cls]);
2831
2832     },
2833
2834     getAutoCreate : function()
2835     {
2836         var bdy = {
2837                 cls : 'modal-body',
2838                 html : this.html || ''
2839         };
2840
2841         var title = {
2842             tag: 'h4',
2843             cls : 'modal-title',
2844             html : this.title
2845         };
2846
2847         if(this.specificTitle){
2848             title = this.title;
2849
2850         }
2851
2852         var header = [];
2853         if (this.allow_close && Roo.bootstrap.version == 3) {
2854             header.push({
2855                 tag: 'button',
2856                 cls : 'close',
2857                 html : '&times'
2858             });
2859         }
2860
2861         header.push(title);
2862
2863         if (this.allow_close && Roo.bootstrap.version == 4) {
2864             header.push({
2865                 tag: 'button',
2866                 cls : 'close',
2867                 html : '&times'
2868             });
2869         }
2870         
2871         var size = '';
2872
2873         if(this.size.length){
2874             size = 'modal-' + this.size;
2875         }
2876         
2877         var footer = Roo.bootstrap.version == 3 ?
2878             {
2879                 cls : 'modal-footer',
2880                 cn : [
2881                     {
2882                         tag: 'div',
2883                         cls: 'btn-' + this.buttonPosition
2884                     }
2885                 ]
2886
2887             } :
2888             {  // BS4 uses mr-auto on left buttons....
2889                 cls : 'modal-footer'
2890             };
2891
2892             
2893
2894         
2895         
2896         var modal = {
2897             cls: "modal",
2898              cn : [
2899                 {
2900                     cls: "modal-dialog " + size,
2901                     cn : [
2902                         {
2903                             cls : "modal-content",
2904                             cn : [
2905                                 {
2906                                     cls : 'modal-header',
2907                                     cn : header
2908                                 },
2909                                 bdy,
2910                                 footer
2911                             ]
2912
2913                         }
2914                     ]
2915
2916                 }
2917             ]
2918         };
2919
2920         if(this.animate){
2921             modal.cls += ' fade';
2922         }
2923
2924         return modal;
2925
2926     },
2927     getChildContainer : function() {
2928
2929          return this.bodyEl;
2930
2931     },
2932     getButtonContainer : function() {
2933         
2934          return Roo.bootstrap.version == 4 ?
2935             this.el.select('.modal-footer',true).first()
2936             : this.el.select('.modal-footer div',true).first();
2937
2938     },
2939     initEvents : function()
2940     {
2941         if (this.allow_close) {
2942             this.closeEl.on('click', this.hide, this);
2943         }
2944         Roo.EventManager.onWindowResize(this.resize, this, true);
2945
2946
2947     },
2948   
2949
2950     resize : function()
2951     {
2952         this.maskEl.setSize(
2953             Roo.lib.Dom.getViewWidth(true),
2954             Roo.lib.Dom.getViewHeight(true)
2955         );
2956         
2957         if (this.fitwindow) {
2958             
2959            
2960             this.setSize(
2961                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2962                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
2963             );
2964             return;
2965         }
2966         
2967         if(this.max_width !== 0) {
2968             
2969             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2970             
2971             if(this.height) {
2972                 this.setSize(w, this.height);
2973                 return;
2974             }
2975             
2976             if(this.max_height) {
2977                 this.setSize(w,Math.min(
2978                     this.max_height,
2979                     Roo.lib.Dom.getViewportHeight(true) - 60
2980                 ));
2981                 
2982                 return;
2983             }
2984             
2985             if(!this.fit_content) {
2986                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2987                 return;
2988             }
2989             
2990             this.setSize(w, Math.min(
2991                 60 +
2992                 this.headerEl.getHeight() + 
2993                 this.footerEl.getHeight() + 
2994                 this.getChildHeight(this.bodyEl.dom.childNodes),
2995                 Roo.lib.Dom.getViewportHeight(true) - 60)
2996             );
2997         }
2998         
2999     },
3000
3001     setSize : function(w,h)
3002     {
3003         if (!w && !h) {
3004             return;
3005         }
3006         
3007         this.resizeTo(w,h);
3008     },
3009
3010     show : function() {
3011
3012         if (!this.rendered) {
3013             this.render();
3014         }
3015
3016         //this.el.setStyle('display', 'block');
3017         this.el.removeClass('hideing');
3018         this.el.dom.style.display='block';
3019         
3020         Roo.get(document.body).addClass('modal-open');
3021  
3022         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
3023             
3024             (function(){
3025                 this.el.addClass('show');
3026                 this.el.addClass('in');
3027             }).defer(50, this);
3028         }else{
3029             this.el.addClass('show');
3030             this.el.addClass('in');
3031         }
3032
3033         // not sure how we can show data in here..
3034         //if (this.tmpl) {
3035         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3036         //}
3037
3038         Roo.get(document.body).addClass("x-body-masked");
3039         
3040         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3041         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3042         this.maskEl.dom.style.display = 'block';
3043         this.maskEl.addClass('show');
3044         
3045         
3046         this.resize();
3047         
3048         this.fireEvent('show', this);
3049
3050         // set zindex here - otherwise it appears to be ignored...
3051         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3052
3053         (function () {
3054             this.items.forEach( function(e) {
3055                 e.layout ? e.layout() : false;
3056
3057             });
3058         }).defer(100,this);
3059
3060     },
3061     hide : function()
3062     {
3063         if(this.fireEvent("beforehide", this) !== false){
3064             
3065             this.maskEl.removeClass('show');
3066             
3067             this.maskEl.dom.style.display = '';
3068             Roo.get(document.body).removeClass("x-body-masked");
3069             this.el.removeClass('in');
3070             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3071
3072             if(this.animate){ // why
3073                 this.el.addClass('hideing');
3074                 this.el.removeClass('show');
3075                 (function(){
3076                     if (!this.el.hasClass('hideing')) {
3077                         return; // it's been shown again...
3078                     }
3079                     
3080                     this.el.dom.style.display='';
3081
3082                     Roo.get(document.body).removeClass('modal-open');
3083                     this.el.removeClass('hideing');
3084                 }).defer(150,this);
3085                 
3086             }else{
3087                 this.el.removeClass('show');
3088                 this.el.dom.style.display='';
3089                 Roo.get(document.body).removeClass('modal-open');
3090
3091             }
3092             this.fireEvent('hide', this);
3093         }
3094     },
3095     isVisible : function()
3096     {
3097         
3098         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3099         
3100     },
3101
3102     addButton : function(str, cb)
3103     {
3104
3105
3106         var b = Roo.apply({}, { html : str } );
3107         b.xns = b.xns || Roo.bootstrap;
3108         b.xtype = b.xtype || 'Button';
3109         if (typeof(b.listeners) == 'undefined') {
3110             b.listeners = { click : cb.createDelegate(this)  };
3111         }
3112
3113         var btn = Roo.factory(b);
3114
3115         btn.render(this.getButtonContainer());
3116
3117         return btn;
3118
3119     },
3120
3121     setDefaultButton : function(btn)
3122     {
3123         //this.el.select('.modal-footer').()
3124     },
3125
3126     resizeTo: function(w,h)
3127     {
3128         this.dialogEl.setWidth(w);
3129         
3130         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3131
3132         this.bodyEl.setHeight(h - diff);
3133         
3134         this.fireEvent('resize', this);
3135     },
3136     
3137     setContentSize  : function(w, h)
3138     {
3139
3140     },
3141     onButtonClick: function(btn,e)
3142     {
3143         //Roo.log([a,b,c]);
3144         this.fireEvent('btnclick', btn.name, e);
3145     },
3146      /**
3147      * Set the title of the Dialog
3148      * @param {String} str new Title
3149      */
3150     setTitle: function(str) {
3151         this.titleEl.dom.innerHTML = str;
3152     },
3153     /**
3154      * Set the body of the Dialog
3155      * @param {String} str new Title
3156      */
3157     setBody: function(str) {
3158         this.bodyEl.dom.innerHTML = str;
3159     },
3160     /**
3161      * Set the body of the Dialog using the template
3162      * @param {Obj} data - apply this data to the template and replace the body contents.
3163      */
3164     applyBody: function(obj)
3165     {
3166         if (!this.tmpl) {
3167             Roo.log("Error - using apply Body without a template");
3168             //code
3169         }
3170         this.tmpl.overwrite(this.bodyEl, obj);
3171     },
3172     
3173     getChildHeight : function(child_nodes)
3174     {
3175         if(
3176             !child_nodes ||
3177             child_nodes.length == 0
3178         ) {
3179             return;
3180         }
3181         
3182         var child_height = 0;
3183         
3184         for(var i = 0; i < child_nodes.length; i++) {
3185             
3186             /*
3187             * for modal with tabs...
3188             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3189                 
3190                 var layout_childs = child_nodes[i].childNodes;
3191                 
3192                 for(var j = 0; j < layout_childs.length; j++) {
3193                     
3194                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3195                         
3196                         var layout_body_childs = layout_childs[j].childNodes;
3197                         
3198                         for(var k = 0; k < layout_body_childs.length; k++) {
3199                             
3200                             if(layout_body_childs[k].classList.contains('navbar')) {
3201                                 child_height += layout_body_childs[k].offsetHeight;
3202                                 continue;
3203                             }
3204                             
3205                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3206                                 
3207                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3208                                 
3209                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3210                                     
3211                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3212                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3213                                         continue;
3214                                     }
3215                                     
3216                                 }
3217                                 
3218                             }
3219                             
3220                         }
3221                     }
3222                 }
3223                 continue;
3224             }
3225             */
3226             
3227             child_height += child_nodes[i].offsetHeight;
3228             // Roo.log(child_nodes[i].offsetHeight);
3229         }
3230         
3231         return child_height;
3232     }
3233
3234 });
3235
3236
3237 Roo.apply(Roo.bootstrap.Modal,  {
3238     /**
3239          * Button config that displays a single OK button
3240          * @type Object
3241          */
3242         OK :  [{
3243             name : 'ok',
3244             weight : 'primary',
3245             html : 'OK'
3246         }],
3247         /**
3248          * Button config that displays Yes and No buttons
3249          * @type Object
3250          */
3251         YESNO : [
3252             {
3253                 name  : 'no',
3254                 html : 'No'
3255             },
3256             {
3257                 name  :'yes',
3258                 weight : 'primary',
3259                 html : 'Yes'
3260             }
3261         ],
3262
3263         /**
3264          * Button config that displays OK and Cancel buttons
3265          * @type Object
3266          */
3267         OKCANCEL : [
3268             {
3269                name : 'cancel',
3270                 html : 'Cancel'
3271             },
3272             {
3273                 name : 'ok',
3274                 weight : 'primary',
3275                 html : 'OK'
3276             }
3277         ],
3278         /**
3279          * Button config that displays Yes, No and Cancel buttons
3280          * @type Object
3281          */
3282         YESNOCANCEL : [
3283             {
3284                 name : 'yes',
3285                 weight : 'primary',
3286                 html : 'Yes'
3287             },
3288             {
3289                 name : 'no',
3290                 html : 'No'
3291             },
3292             {
3293                 name : 'cancel',
3294                 html : 'Cancel'
3295             }
3296         ],
3297         
3298         zIndex : 10001
3299 });
3300 /*
3301  * - LGPL
3302  *
3303  * messagebox - can be used as a replace
3304  * 
3305  */
3306 /**
3307  * @class Roo.MessageBox
3308  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3309  * Example usage:
3310  *<pre><code>
3311 // Basic alert:
3312 Roo.Msg.alert('Status', 'Changes saved successfully.');
3313
3314 // Prompt for user data:
3315 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3316     if (btn == 'ok'){
3317         // process text value...
3318     }
3319 });
3320
3321 // Show a dialog using config options:
3322 Roo.Msg.show({
3323    title:'Save Changes?',
3324    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3325    buttons: Roo.Msg.YESNOCANCEL,
3326    fn: processResult,
3327    animEl: 'elId'
3328 });
3329 </code></pre>
3330  * @singleton
3331  */
3332 Roo.bootstrap.MessageBox = function(){
3333     var dlg, opt, mask, waitTimer;
3334     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3335     var buttons, activeTextEl, bwidth;
3336
3337     
3338     // private
3339     var handleButton = function(button){
3340         dlg.hide();
3341         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3342     };
3343
3344     // private
3345     var handleHide = function(){
3346         if(opt && opt.cls){
3347             dlg.el.removeClass(opt.cls);
3348         }
3349         //if(waitTimer){
3350         //    Roo.TaskMgr.stop(waitTimer);
3351         //    waitTimer = null;
3352         //}
3353     };
3354
3355     // private
3356     var updateButtons = function(b){
3357         var width = 0;
3358         if(!b){
3359             buttons["ok"].hide();
3360             buttons["cancel"].hide();
3361             buttons["yes"].hide();
3362             buttons["no"].hide();
3363             dlg.footerEl.hide();
3364             
3365             return width;
3366         }
3367         dlg.footerEl.show();
3368         for(var k in buttons){
3369             if(typeof buttons[k] != "function"){
3370                 if(b[k]){
3371                     buttons[k].show();
3372                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3373                     width += buttons[k].el.getWidth()+15;
3374                 }else{
3375                     buttons[k].hide();
3376                 }
3377             }
3378         }
3379         return width;
3380     };
3381
3382     // private
3383     var handleEsc = function(d, k, e){
3384         if(opt && opt.closable !== false){
3385             dlg.hide();
3386         }
3387         if(e){
3388             e.stopEvent();
3389         }
3390     };
3391
3392     return {
3393         /**
3394          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3395          * @return {Roo.BasicDialog} The BasicDialog element
3396          */
3397         getDialog : function(){
3398            if(!dlg){
3399                 dlg = new Roo.bootstrap.Modal( {
3400                     //draggable: true,
3401                     //resizable:false,
3402                     //constraintoviewport:false,
3403                     //fixedcenter:true,
3404                     //collapsible : false,
3405                     //shim:true,
3406                     //modal: true,
3407                 //    width: 'auto',
3408                   //  height:100,
3409                     //buttonAlign:"center",
3410                     closeClick : function(){
3411                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3412                             handleButton("no");
3413                         }else{
3414                             handleButton("cancel");
3415                         }
3416                     }
3417                 });
3418                 dlg.render();
3419                 dlg.on("hide", handleHide);
3420                 mask = dlg.mask;
3421                 //dlg.addKeyListener(27, handleEsc);
3422                 buttons = {};
3423                 this.buttons = buttons;
3424                 var bt = this.buttonText;
3425                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3426                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3427                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3428                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3429                 //Roo.log(buttons);
3430                 bodyEl = dlg.bodyEl.createChild({
3431
3432                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3433                         '<textarea class="roo-mb-textarea"></textarea>' +
3434                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3435                 });
3436                 msgEl = bodyEl.dom.firstChild;
3437                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3438                 textboxEl.enableDisplayMode();
3439                 textboxEl.addKeyListener([10,13], function(){
3440                     if(dlg.isVisible() && opt && opt.buttons){
3441                         if(opt.buttons.ok){
3442                             handleButton("ok");
3443                         }else if(opt.buttons.yes){
3444                             handleButton("yes");
3445                         }
3446                     }
3447                 });
3448                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3449                 textareaEl.enableDisplayMode();
3450                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3451                 progressEl.enableDisplayMode();
3452                 
3453                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3454                 var pf = progressEl.dom.firstChild;
3455                 if (pf) {
3456                     pp = Roo.get(pf.firstChild);
3457                     pp.setHeight(pf.offsetHeight);
3458                 }
3459                 
3460             }
3461             return dlg;
3462         },
3463
3464         /**
3465          * Updates the message box body text
3466          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3467          * the XHTML-compliant non-breaking space character '&amp;#160;')
3468          * @return {Roo.MessageBox} This message box
3469          */
3470         updateText : function(text)
3471         {
3472             if(!dlg.isVisible() && !opt.width){
3473                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3474                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3475             }
3476             msgEl.innerHTML = text || '&#160;';
3477       
3478             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3479             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3480             var w = Math.max(
3481                     Math.min(opt.width || cw , this.maxWidth), 
3482                     Math.max(opt.minWidth || this.minWidth, bwidth)
3483             );
3484             if(opt.prompt){
3485                 activeTextEl.setWidth(w);
3486             }
3487             if(dlg.isVisible()){
3488                 dlg.fixedcenter = false;
3489             }
3490             // to big, make it scroll. = But as usual stupid IE does not support
3491             // !important..
3492             
3493             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3494                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3495                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3496             } else {
3497                 bodyEl.dom.style.height = '';
3498                 bodyEl.dom.style.overflowY = '';
3499             }
3500             if (cw > w) {
3501                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3502             } else {
3503                 bodyEl.dom.style.overflowX = '';
3504             }
3505             
3506             dlg.setContentSize(w, bodyEl.getHeight());
3507             if(dlg.isVisible()){
3508                 dlg.fixedcenter = true;
3509             }
3510             return this;
3511         },
3512
3513         /**
3514          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3515          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3516          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3517          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3518          * @return {Roo.MessageBox} This message box
3519          */
3520         updateProgress : function(value, text){
3521             if(text){
3522                 this.updateText(text);
3523             }
3524             
3525             if (pp) { // weird bug on my firefox - for some reason this is not defined
3526                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3527                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3528             }
3529             return this;
3530         },        
3531
3532         /**
3533          * Returns true if the message box is currently displayed
3534          * @return {Boolean} True if the message box is visible, else false
3535          */
3536         isVisible : function(){
3537             return dlg && dlg.isVisible();  
3538         },
3539
3540         /**
3541          * Hides the message box if it is displayed
3542          */
3543         hide : function(){
3544             if(this.isVisible()){
3545                 dlg.hide();
3546             }  
3547         },
3548
3549         /**
3550          * Displays a new message box, or reinitializes an existing message box, based on the config options
3551          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3552          * The following config object properties are supported:
3553          * <pre>
3554 Property    Type             Description
3555 ----------  ---------------  ------------------------------------------------------------------------------------
3556 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3557                                    closes (defaults to undefined)
3558 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3559                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3560 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3561                                    progress and wait dialogs will ignore this property and always hide the
3562                                    close button as they can only be closed programmatically.
3563 cls               String           A custom CSS class to apply to the message box element
3564 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3565                                    displayed (defaults to 75)
3566 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3567                                    function will be btn (the name of the button that was clicked, if applicable,
3568                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3569                                    Progress and wait dialogs will ignore this option since they do not respond to
3570                                    user actions and can only be closed programmatically, so any required function
3571                                    should be called by the same code after it closes the dialog.
3572 icon              String           A CSS class that provides a background image to be used as an icon for
3573                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3574 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3575 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3576 modal             Boolean          False to allow user interaction with the page while the message box is
3577                                    displayed (defaults to true)
3578 msg               String           A string that will replace the existing message box body text (defaults
3579                                    to the XHTML-compliant non-breaking space character '&#160;')
3580 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3581 progress          Boolean          True to display a progress bar (defaults to false)
3582 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3583 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3584 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3585 title             String           The title text
3586 value             String           The string value to set into the active textbox element if displayed
3587 wait              Boolean          True to display a progress bar (defaults to false)
3588 width             Number           The width of the dialog in pixels
3589 </pre>
3590          *
3591          * Example usage:
3592          * <pre><code>
3593 Roo.Msg.show({
3594    title: 'Address',
3595    msg: 'Please enter your address:',
3596    width: 300,
3597    buttons: Roo.MessageBox.OKCANCEL,
3598    multiline: true,
3599    fn: saveAddress,
3600    animEl: 'addAddressBtn'
3601 });
3602 </code></pre>
3603          * @param {Object} config Configuration options
3604          * @return {Roo.MessageBox} This message box
3605          */
3606         show : function(options)
3607         {
3608             
3609             // this causes nightmares if you show one dialog after another
3610             // especially on callbacks..
3611              
3612             if(this.isVisible()){
3613                 
3614                 this.hide();
3615                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3616                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3617                 Roo.log("New Dialog Message:" +  options.msg )
3618                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3619                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3620                 
3621             }
3622             var d = this.getDialog();
3623             opt = options;
3624             d.setTitle(opt.title || "&#160;");
3625             d.closeEl.setDisplayed(opt.closable !== false);
3626             activeTextEl = textboxEl;
3627             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3628             if(opt.prompt){
3629                 if(opt.multiline){
3630                     textboxEl.hide();
3631                     textareaEl.show();
3632                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3633                         opt.multiline : this.defaultTextHeight);
3634                     activeTextEl = textareaEl;
3635                 }else{
3636                     textboxEl.show();
3637                     textareaEl.hide();
3638                 }
3639             }else{
3640                 textboxEl.hide();
3641                 textareaEl.hide();
3642             }
3643             progressEl.setDisplayed(opt.progress === true);
3644             if (opt.progress) {
3645                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3646             }
3647             this.updateProgress(0);
3648             activeTextEl.dom.value = opt.value || "";
3649             if(opt.prompt){
3650                 dlg.setDefaultButton(activeTextEl);
3651             }else{
3652                 var bs = opt.buttons;
3653                 var db = null;
3654                 if(bs && bs.ok){
3655                     db = buttons["ok"];
3656                 }else if(bs && bs.yes){
3657                     db = buttons["yes"];
3658                 }
3659                 dlg.setDefaultButton(db);
3660             }
3661             bwidth = updateButtons(opt.buttons);
3662             this.updateText(opt.msg);
3663             if(opt.cls){
3664                 d.el.addClass(opt.cls);
3665             }
3666             d.proxyDrag = opt.proxyDrag === true;
3667             d.modal = opt.modal !== false;
3668             d.mask = opt.modal !== false ? mask : false;
3669             if(!d.isVisible()){
3670                 // force it to the end of the z-index stack so it gets a cursor in FF
3671                 document.body.appendChild(dlg.el.dom);
3672                 d.animateTarget = null;
3673                 d.show(options.animEl);
3674             }
3675             return this;
3676         },
3677
3678         /**
3679          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3680          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3681          * and closing the message box when the process is complete.
3682          * @param {String} title The title bar text
3683          * @param {String} msg The message box body text
3684          * @return {Roo.MessageBox} This message box
3685          */
3686         progress : function(title, msg){
3687             this.show({
3688                 title : title,
3689                 msg : msg,
3690                 buttons: false,
3691                 progress:true,
3692                 closable:false,
3693                 minWidth: this.minProgressWidth,
3694                 modal : true
3695             });
3696             return this;
3697         },
3698
3699         /**
3700          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3701          * If a callback function is passed it will be called after the user clicks the button, and the
3702          * id of the button that was clicked will be passed as the only parameter to the callback
3703          * (could also be the top-right close button).
3704          * @param {String} title The title bar text
3705          * @param {String} msg The message box body text
3706          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3707          * @param {Object} scope (optional) The scope of the callback function
3708          * @return {Roo.MessageBox} This message box
3709          */
3710         alert : function(title, msg, fn, scope)
3711         {
3712             this.show({
3713                 title : title,
3714                 msg : msg,
3715                 buttons: this.OK,
3716                 fn: fn,
3717                 closable : false,
3718                 scope : scope,
3719                 modal : true
3720             });
3721             return this;
3722         },
3723
3724         /**
3725          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3726          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3727          * You are responsible for closing the message box when the process is complete.
3728          * @param {String} msg The message box body text
3729          * @param {String} title (optional) The title bar text
3730          * @return {Roo.MessageBox} This message box
3731          */
3732         wait : function(msg, title){
3733             this.show({
3734                 title : title,
3735                 msg : msg,
3736                 buttons: false,
3737                 closable:false,
3738                 progress:true,
3739                 modal:true,
3740                 width:300,
3741                 wait:true
3742             });
3743             waitTimer = Roo.TaskMgr.start({
3744                 run: function(i){
3745                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3746                 },
3747                 interval: 1000
3748             });
3749             return this;
3750         },
3751
3752         /**
3753          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3754          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3755          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3756          * @param {String} title The title bar text
3757          * @param {String} msg The message box body text
3758          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3759          * @param {Object} scope (optional) The scope of the callback function
3760          * @return {Roo.MessageBox} This message box
3761          */
3762         confirm : function(title, msg, fn, scope){
3763             this.show({
3764                 title : title,
3765                 msg : msg,
3766                 buttons: this.YESNO,
3767                 fn: fn,
3768                 scope : scope,
3769                 modal : true
3770             });
3771             return this;
3772         },
3773
3774         /**
3775          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3776          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3777          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3778          * (could also be the top-right close button) and the text that was entered will be passed as the two
3779          * parameters to the callback.
3780          * @param {String} title The title bar text
3781          * @param {String} msg The message box body text
3782          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3783          * @param {Object} scope (optional) The scope of the callback function
3784          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3785          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3786          * @return {Roo.MessageBox} This message box
3787          */
3788         prompt : function(title, msg, fn, scope, multiline){
3789             this.show({
3790                 title : title,
3791                 msg : msg,
3792                 buttons: this.OKCANCEL,
3793                 fn: fn,
3794                 minWidth:250,
3795                 scope : scope,
3796                 prompt:true,
3797                 multiline: multiline,
3798                 modal : true
3799             });
3800             return this;
3801         },
3802
3803         /**
3804          * Button config that displays a single OK button
3805          * @type Object
3806          */
3807         OK : {ok:true},
3808         /**
3809          * Button config that displays Yes and No buttons
3810          * @type Object
3811          */
3812         YESNO : {yes:true, no:true},
3813         /**
3814          * Button config that displays OK and Cancel buttons
3815          * @type Object
3816          */
3817         OKCANCEL : {ok:true, cancel:true},
3818         /**
3819          * Button config that displays Yes, No and Cancel buttons
3820          * @type Object
3821          */
3822         YESNOCANCEL : {yes:true, no:true, cancel:true},
3823
3824         /**
3825          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3826          * @type Number
3827          */
3828         defaultTextHeight : 75,
3829         /**
3830          * The maximum width in pixels of the message box (defaults to 600)
3831          * @type Number
3832          */
3833         maxWidth : 600,
3834         /**
3835          * The minimum width in pixels of the message box (defaults to 100)
3836          * @type Number
3837          */
3838         minWidth : 100,
3839         /**
3840          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3841          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3842          * @type Number
3843          */
3844         minProgressWidth : 250,
3845         /**
3846          * An object containing the default button text strings that can be overriden for localized language support.
3847          * Supported properties are: ok, cancel, yes and no.
3848          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3849          * @type Object
3850          */
3851         buttonText : {
3852             ok : "OK",
3853             cancel : "Cancel",
3854             yes : "Yes",
3855             no : "No"
3856         }
3857     };
3858 }();
3859
3860 /**
3861  * Shorthand for {@link Roo.MessageBox}
3862  */
3863 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3864 Roo.Msg = Roo.Msg || Roo.MessageBox;
3865 /*
3866  * - LGPL
3867  *
3868  * navbar
3869  * 
3870  */
3871
3872 /**
3873  * @class Roo.bootstrap.Navbar
3874  * @extends Roo.bootstrap.Component
3875  * Bootstrap Navbar class
3876
3877  * @constructor
3878  * Create a new Navbar
3879  * @param {Object} config The config object
3880  */
3881
3882
3883 Roo.bootstrap.Navbar = function(config){
3884     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3885     this.addEvents({
3886         // raw events
3887         /**
3888          * @event beforetoggle
3889          * Fire before toggle the menu
3890          * @param {Roo.EventObject} e
3891          */
3892         "beforetoggle" : true
3893     });
3894 };
3895
3896 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3897     
3898     
3899    
3900     // private
3901     navItems : false,
3902     loadMask : false,
3903     
3904     
3905     getAutoCreate : function(){
3906         
3907         
3908         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3909         
3910     },
3911     
3912     initEvents :function ()
3913     {
3914         //Roo.log(this.el.select('.navbar-toggle',true));
3915         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
3916         
3917         var mark = {
3918             tag: "div",
3919             cls:"x-dlg-mask"
3920         };
3921         
3922         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3923         
3924         var size = this.el.getSize();
3925         this.maskEl.setSize(size.width, size.height);
3926         this.maskEl.enableDisplayMode("block");
3927         this.maskEl.hide();
3928         
3929         if(this.loadMask){
3930             this.maskEl.show();
3931         }
3932     },
3933     
3934     
3935     getChildContainer : function()
3936     {
3937         if (this.el && this.el.select('.collapse').getCount()) {
3938             return this.el.select('.collapse',true).first();
3939         }
3940         
3941         return this.el;
3942     },
3943     
3944     mask : function()
3945     {
3946         this.maskEl.show();
3947     },
3948     
3949     unmask : function()
3950     {
3951         this.maskEl.hide();
3952     },
3953     onToggle : function()
3954     {
3955         
3956         if(this.fireEvent('beforetoggle', this) === false){
3957             return;
3958         }
3959         var ce = this.el.select('.navbar-collapse',true).first();
3960       
3961         if (!ce.hasClass('show')) {
3962            this.expand();
3963         } else {
3964             this.collapse();
3965         }
3966         
3967         
3968     
3969     },
3970     /**
3971      * Expand the navbar pulldown 
3972      */
3973     expand : function ()
3974     {
3975        
3976         var ce = this.el.select('.navbar-collapse',true).first();
3977         if (ce.hasClass('collapsing')) {
3978             return;
3979         }
3980         ce.dom.style.height = '';
3981                // show it...
3982         ce.addClass('in'); // old...
3983         ce.removeClass('collapse');
3984         ce.addClass('show');
3985         var h = ce.getHeight();
3986         Roo.log(h);
3987         ce.removeClass('show');
3988         // at this point we should be able to see it..
3989         ce.addClass('collapsing');
3990         
3991         ce.setHeight(0); // resize it ...
3992         ce.on('transitionend', function() {
3993             //Roo.log('done transition');
3994             ce.removeClass('collapsing');
3995             ce.addClass('show');
3996             ce.removeClass('collapse');
3997
3998             ce.dom.style.height = '';
3999         }, this, { single: true} );
4000         ce.setHeight(h);
4001         ce.dom.scrollTop = 0;
4002     },
4003     /**
4004      * Collapse the navbar pulldown 
4005      */
4006     collapse : function()
4007     {
4008          var ce = this.el.select('.navbar-collapse',true).first();
4009        
4010         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
4011             // it's collapsed or collapsing..
4012             return;
4013         }
4014         ce.removeClass('in'); // old...
4015         ce.setHeight(ce.getHeight());
4016         ce.removeClass('show');
4017         ce.addClass('collapsing');
4018         
4019         ce.on('transitionend', function() {
4020             ce.dom.style.height = '';
4021             ce.removeClass('collapsing');
4022             ce.addClass('collapse');
4023         }, this, { single: true} );
4024         ce.setHeight(0);
4025     }
4026     
4027     
4028     
4029 });
4030
4031
4032
4033  
4034
4035  /*
4036  * - LGPL
4037  *
4038  * navbar
4039  * 
4040  */
4041
4042 /**
4043  * @class Roo.bootstrap.NavSimplebar
4044  * @extends Roo.bootstrap.Navbar
4045  * Bootstrap Sidebar class
4046  *
4047  * @cfg {Boolean} inverse is inverted color
4048  * 
4049  * @cfg {String} type (nav | pills | tabs)
4050  * @cfg {Boolean} arrangement stacked | justified
4051  * @cfg {String} align (left | right) alignment
4052  * 
4053  * @cfg {Boolean} main (true|false) main nav bar? default false
4054  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4055  * 
4056  * @cfg {String} tag (header|footer|nav|div) default is nav 
4057
4058  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4059  * 
4060  * 
4061  * @constructor
4062  * Create a new Sidebar
4063  * @param {Object} config The config object
4064  */
4065
4066
4067 Roo.bootstrap.NavSimplebar = function(config){
4068     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4069 };
4070
4071 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4072     
4073     inverse: false,
4074     
4075     type: false,
4076     arrangement: '',
4077     align : false,
4078     
4079     weight : 'light',
4080     
4081     main : false,
4082     
4083     
4084     tag : false,
4085     
4086     
4087     getAutoCreate : function(){
4088         
4089         
4090         var cfg = {
4091             tag : this.tag || 'div',
4092             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4093         };
4094         if (['light','white'].indexOf(this.weight) > -1) {
4095             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4096         }
4097         cfg.cls += ' bg-' + this.weight;
4098         
4099         if (this.inverse) {
4100             cfg.cls += ' navbar-inverse';
4101             
4102         }
4103         
4104         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4105         
4106         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4107             return cfg;
4108         }
4109         
4110         
4111     
4112         
4113         cfg.cn = [
4114             {
4115                 cls: 'nav nav-' + this.xtype,
4116                 tag : 'ul'
4117             }
4118         ];
4119         
4120          
4121         this.type = this.type || 'nav';
4122         if (['tabs','pills'].indexOf(this.type) != -1) {
4123             cfg.cn[0].cls += ' nav-' + this.type
4124         
4125         
4126         } else {
4127             if (this.type!=='nav') {
4128                 Roo.log('nav type must be nav/tabs/pills')
4129             }
4130             cfg.cn[0].cls += ' navbar-nav'
4131         }
4132         
4133         
4134         
4135         
4136         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4137             cfg.cn[0].cls += ' nav-' + this.arrangement;
4138         }
4139         
4140         
4141         if (this.align === 'right') {
4142             cfg.cn[0].cls += ' navbar-right';
4143         }
4144         
4145         
4146         
4147         
4148         return cfg;
4149     
4150         
4151     }
4152     
4153     
4154     
4155 });
4156
4157
4158
4159  
4160
4161  
4162        /*
4163  * - LGPL
4164  *
4165  * navbar
4166  * navbar-fixed-top
4167  * navbar-expand-md  fixed-top 
4168  */
4169
4170 /**
4171  * @class Roo.bootstrap.NavHeaderbar
4172  * @extends Roo.bootstrap.NavSimplebar
4173  * Bootstrap Sidebar class
4174  *
4175  * @cfg {String} brand what is brand
4176  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4177  * @cfg {String} brand_href href of the brand
4178  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4179  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4180  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4181  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4182  * 
4183  * @constructor
4184  * Create a new Sidebar
4185  * @param {Object} config The config object
4186  */
4187
4188
4189 Roo.bootstrap.NavHeaderbar = function(config){
4190     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4191       
4192 };
4193
4194 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4195     
4196     position: '',
4197     brand: '',
4198     brand_href: false,
4199     srButton : true,
4200     autohide : false,
4201     desktopCenter : false,
4202    
4203     
4204     getAutoCreate : function(){
4205         
4206         var   cfg = {
4207             tag: this.nav || 'nav',
4208             cls: 'navbar navbar-expand-md',
4209             role: 'navigation',
4210             cn: []
4211         };
4212         
4213         var cn = cfg.cn;
4214         if (this.desktopCenter) {
4215             cn.push({cls : 'container', cn : []});
4216             cn = cn[0].cn;
4217         }
4218         
4219         if(this.srButton){
4220             var btn = {
4221                 tag: 'button',
4222                 type: 'button',
4223                 cls: 'navbar-toggle navbar-toggler',
4224                 'data-toggle': 'collapse',
4225                 cn: [
4226                     {
4227                         tag: 'span',
4228                         cls: 'sr-only',
4229                         html: 'Toggle navigation'
4230                     },
4231                     {
4232                         tag: 'span',
4233                         cls: 'icon-bar navbar-toggler-icon'
4234                     },
4235                     {
4236                         tag: 'span',
4237                         cls: 'icon-bar'
4238                     },
4239                     {
4240                         tag: 'span',
4241                         cls: 'icon-bar'
4242                     }
4243                 ]
4244             };
4245             
4246             cn.push( Roo.bootstrap.version == 4 ? btn : {
4247                 tag: 'div',
4248                 cls: 'navbar-header',
4249                 cn: [
4250                     btn
4251                 ]
4252             });
4253         }
4254         
4255         cn.push({
4256             tag: 'div',
4257             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4258             cn : []
4259         });
4260         
4261         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4262         
4263         if (['light','white'].indexOf(this.weight) > -1) {
4264             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4265         }
4266         cfg.cls += ' bg-' + this.weight;
4267         
4268         
4269         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4270             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4271             
4272             // tag can override this..
4273             
4274             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4275         }
4276         
4277         if (this.brand !== '') {
4278             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4279             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4280                 tag: 'a',
4281                 href: this.brand_href ? this.brand_href : '#',
4282                 cls: 'navbar-brand',
4283                 cn: [
4284                 this.brand
4285                 ]
4286             });
4287         }
4288         
4289         if(this.main){
4290             cfg.cls += ' main-nav';
4291         }
4292         
4293         
4294         return cfg;
4295
4296         
4297     },
4298     getHeaderChildContainer : function()
4299     {
4300         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4301             return this.el.select('.navbar-header',true).first();
4302         }
4303         
4304         return this.getChildContainer();
4305     },
4306     
4307     
4308     initEvents : function()
4309     {
4310         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4311         
4312         if (this.autohide) {
4313             
4314             var prevScroll = 0;
4315             var ft = this.el;
4316             
4317             Roo.get(document).on('scroll',function(e) {
4318                 var ns = Roo.get(document).getScroll().top;
4319                 var os = prevScroll;
4320                 prevScroll = ns;
4321                 
4322                 if(ns > os){
4323                     ft.removeClass('slideDown');
4324                     ft.addClass('slideUp');
4325                     return;
4326                 }
4327                 ft.removeClass('slideUp');
4328                 ft.addClass('slideDown');
4329                  
4330               
4331           },this);
4332         }
4333     }    
4334     
4335 });
4336
4337
4338
4339  
4340
4341  /*
4342  * - LGPL
4343  *
4344  * navbar
4345  * 
4346  */
4347
4348 /**
4349  * @class Roo.bootstrap.NavSidebar
4350  * @extends Roo.bootstrap.Navbar
4351  * Bootstrap Sidebar class
4352  * 
4353  * @constructor
4354  * Create a new Sidebar
4355  * @param {Object} config The config object
4356  */
4357
4358
4359 Roo.bootstrap.NavSidebar = function(config){
4360     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4361 };
4362
4363 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4364     
4365     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4366     
4367     getAutoCreate : function(){
4368         
4369         
4370         return  {
4371             tag: 'div',
4372             cls: 'sidebar sidebar-nav'
4373         };
4374     
4375         
4376     }
4377     
4378     
4379     
4380 });
4381
4382
4383
4384  
4385
4386  /*
4387  * - LGPL
4388  *
4389  * nav group
4390  * 
4391  */
4392
4393 /**
4394  * @class Roo.bootstrap.NavGroup
4395  * @extends Roo.bootstrap.Component
4396  * Bootstrap NavGroup class
4397  * @cfg {String} align (left|right)
4398  * @cfg {Boolean} inverse
4399  * @cfg {String} type (nav|pills|tab) default nav
4400  * @cfg {String} navId - reference Id for navbar.
4401
4402  * 
4403  * @constructor
4404  * Create a new nav group
4405  * @param {Object} config The config object
4406  */
4407
4408 Roo.bootstrap.NavGroup = function(config){
4409     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4410     this.navItems = [];
4411    
4412     Roo.bootstrap.NavGroup.register(this);
4413      this.addEvents({
4414         /**
4415              * @event changed
4416              * Fires when the active item changes
4417              * @param {Roo.bootstrap.NavGroup} this
4418              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4419              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4420          */
4421         'changed': true
4422      });
4423     
4424 };
4425
4426 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4427     
4428     align: '',
4429     inverse: false,
4430     form: false,
4431     type: 'nav',
4432     navId : '',
4433     // private
4434     
4435     navItems : false, 
4436     
4437     getAutoCreate : function()
4438     {
4439         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4440         
4441         cfg = {
4442             tag : 'ul',
4443             cls: 'nav' 
4444         };
4445         if (Roo.bootstrap.version == 4) {
4446             if (['tabs','pills'].indexOf(this.type) != -1) {
4447                 cfg.cls += ' nav-' + this.type; 
4448             } else {
4449                 // trying to remove so header bar can right align top?
4450                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
4451                     // do not use on header bar... 
4452                     cfg.cls += ' navbar-nav';
4453                 }
4454             }
4455             
4456         } else {
4457             if (['tabs','pills'].indexOf(this.type) != -1) {
4458                 cfg.cls += ' nav-' + this.type
4459             } else {
4460                 if (this.type !== 'nav') {
4461                     Roo.log('nav type must be nav/tabs/pills')
4462                 }
4463                 cfg.cls += ' navbar-nav'
4464             }
4465         }
4466         
4467         if (this.parent() && this.parent().sidebar) {
4468             cfg = {
4469                 tag: 'ul',
4470                 cls: 'dashboard-menu sidebar-menu'
4471             };
4472             
4473             return cfg;
4474         }
4475         
4476         if (this.form === true) {
4477             cfg = {
4478                 tag: 'form',
4479                 cls: 'navbar-form form-inline'
4480             };
4481             //nav navbar-right ml-md-auto
4482             if (this.align === 'right') {
4483                 cfg.cls += ' navbar-right ml-md-auto';
4484             } else {
4485                 cfg.cls += ' navbar-left';
4486             }
4487         }
4488         
4489         if (this.align === 'right') {
4490             cfg.cls += ' navbar-right ml-md-auto';
4491         } else {
4492             cfg.cls += ' mr-auto';
4493         }
4494         
4495         if (this.inverse) {
4496             cfg.cls += ' navbar-inverse';
4497             
4498         }
4499         
4500         
4501         return cfg;
4502     },
4503     /**
4504     * sets the active Navigation item
4505     * @param {Roo.bootstrap.NavItem} the new current navitem
4506     */
4507     setActiveItem : function(item)
4508     {
4509         var prev = false;
4510         Roo.each(this.navItems, function(v){
4511             if (v == item) {
4512                 return ;
4513             }
4514             if (v.isActive()) {
4515                 v.setActive(false, true);
4516                 prev = v;
4517                 
4518             }
4519             
4520         });
4521
4522         item.setActive(true, true);
4523         this.fireEvent('changed', this, item, prev);
4524         
4525         
4526     },
4527     /**
4528     * gets the active Navigation item
4529     * @return {Roo.bootstrap.NavItem} the current navitem
4530     */
4531     getActive : function()
4532     {
4533         
4534         var prev = false;
4535         Roo.each(this.navItems, function(v){
4536             
4537             if (v.isActive()) {
4538                 prev = v;
4539                 
4540             }
4541             
4542         });
4543         return prev;
4544     },
4545     
4546     indexOfNav : function()
4547     {
4548         
4549         var prev = false;
4550         Roo.each(this.navItems, function(v,i){
4551             
4552             if (v.isActive()) {
4553                 prev = i;
4554                 
4555             }
4556             
4557         });
4558         return prev;
4559     },
4560     /**
4561     * adds a Navigation item
4562     * @param {Roo.bootstrap.NavItem} the navitem to add
4563     */
4564     addItem : function(cfg)
4565     {
4566         if (this.form && Roo.bootstrap.version == 4) {
4567             cfg.tag = 'div';
4568         }
4569         var cn = new Roo.bootstrap.NavItem(cfg);
4570         this.register(cn);
4571         cn.parentId = this.id;
4572         cn.onRender(this.el, null);
4573         return cn;
4574     },
4575     /**
4576     * register a Navigation item
4577     * @param {Roo.bootstrap.NavItem} the navitem to add
4578     */
4579     register : function(item)
4580     {
4581         this.navItems.push( item);
4582         item.navId = this.navId;
4583     
4584     },
4585     
4586     /**
4587     * clear all the Navigation item
4588     */
4589    
4590     clearAll : function()
4591     {
4592         this.navItems = [];
4593         this.el.dom.innerHTML = '';
4594     },
4595     
4596     getNavItem: function(tabId)
4597     {
4598         var ret = false;
4599         Roo.each(this.navItems, function(e) {
4600             if (e.tabId == tabId) {
4601                ret =  e;
4602                return false;
4603             }
4604             return true;
4605             
4606         });
4607         return ret;
4608     },
4609     
4610     setActiveNext : function()
4611     {
4612         var i = this.indexOfNav(this.getActive());
4613         if (i > this.navItems.length) {
4614             return;
4615         }
4616         this.setActiveItem(this.navItems[i+1]);
4617     },
4618     setActivePrev : function()
4619     {
4620         var i = this.indexOfNav(this.getActive());
4621         if (i  < 1) {
4622             return;
4623         }
4624         this.setActiveItem(this.navItems[i-1]);
4625     },
4626     clearWasActive : function(except) {
4627         Roo.each(this.navItems, function(e) {
4628             if (e.tabId != except.tabId && e.was_active) {
4629                e.was_active = false;
4630                return false;
4631             }
4632             return true;
4633             
4634         });
4635     },
4636     getWasActive : function ()
4637     {
4638         var r = false;
4639         Roo.each(this.navItems, function(e) {
4640             if (e.was_active) {
4641                r = e;
4642                return false;
4643             }
4644             return true;
4645             
4646         });
4647         return r;
4648     }
4649     
4650     
4651 });
4652
4653  
4654 Roo.apply(Roo.bootstrap.NavGroup, {
4655     
4656     groups: {},
4657      /**
4658     * register a Navigation Group
4659     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4660     */
4661     register : function(navgrp)
4662     {
4663         this.groups[navgrp.navId] = navgrp;
4664         
4665     },
4666     /**
4667     * fetch a Navigation Group based on the navigation ID
4668     * @param {string} the navgroup to add
4669     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4670     */
4671     get: function(navId) {
4672         if (typeof(this.groups[navId]) == 'undefined') {
4673             return false;
4674             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4675         }
4676         return this.groups[navId] ;
4677     }
4678     
4679     
4680     
4681 });
4682
4683  /*
4684  * - LGPL
4685  *
4686  * row
4687  * 
4688  */
4689
4690 /**
4691  * @class Roo.bootstrap.NavItem
4692  * @extends Roo.bootstrap.Component
4693  * Bootstrap Navbar.NavItem class
4694  * @cfg {String} href  link to
4695  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4696
4697  * @cfg {String} html content of button
4698  * @cfg {String} badge text inside badge
4699  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4700  * @cfg {String} glyphicon DEPRICATED - use fa
4701  * @cfg {String} icon DEPRICATED - use fa
4702  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4703  * @cfg {Boolean} active Is item active
4704  * @cfg {Boolean} disabled Is item disabled
4705  
4706  * @cfg {Boolean} preventDefault (true | false) default false
4707  * @cfg {String} tabId the tab that this item activates.
4708  * @cfg {String} tagtype (a|span) render as a href or span?
4709  * @cfg {Boolean} animateRef (true|false) link to element default false  
4710   
4711  * @constructor
4712  * Create a new Navbar Item
4713  * @param {Object} config The config object
4714  */
4715 Roo.bootstrap.NavItem = function(config){
4716     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4717     this.addEvents({
4718         // raw events
4719         /**
4720          * @event click
4721          * The raw click event for the entire grid.
4722          * @param {Roo.EventObject} e
4723          */
4724         "click" : true,
4725          /**
4726             * @event changed
4727             * Fires when the active item active state changes
4728             * @param {Roo.bootstrap.NavItem} this
4729             * @param {boolean} state the new state
4730              
4731          */
4732         'changed': true,
4733         /**
4734             * @event scrollto
4735             * Fires when scroll to element
4736             * @param {Roo.bootstrap.NavItem} this
4737             * @param {Object} options
4738             * @param {Roo.EventObject} e
4739              
4740          */
4741         'scrollto': true
4742     });
4743    
4744 };
4745
4746 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4747     
4748     href: false,
4749     html: '',
4750     badge: '',
4751     icon: false,
4752     fa : false,
4753     glyphicon: false,
4754     active: false,
4755     preventDefault : false,
4756     tabId : false,
4757     tagtype : 'a',
4758     tag: 'li',
4759     disabled : false,
4760     animateRef : false,
4761     was_active : false,
4762     button_weight : '',
4763     button_outline : false,
4764     
4765     navLink: false,
4766     
4767     getAutoCreate : function(){
4768          
4769         var cfg = {
4770             tag: this.tag,
4771             cls: 'nav-item'
4772         };
4773         
4774         if (this.active) {
4775             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4776         }
4777         if (this.disabled) {
4778             cfg.cls += ' disabled';
4779         }
4780         
4781         // BS4 only?
4782         if (this.button_weight.length) {
4783             cfg.tag = this.href ? 'a' : 'button';
4784             cfg.html = this.html || '';
4785             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4786             if (this.href) {
4787                 cfg.href = this.href;
4788             }
4789             if (this.fa) {
4790                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4791             }
4792             
4793             // menu .. should add dropdown-menu class - so no need for carat..
4794             
4795             if (this.badge !== '') {
4796                  
4797                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4798             }
4799             return cfg;
4800         }
4801         
4802         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4803             cfg.cn = [
4804                 {
4805                     tag: this.tagtype,
4806                     href : this.href || "#",
4807                     html: this.html || ''
4808                 }
4809             ];
4810             if (this.tagtype == 'a') {
4811                 cfg.cn[0].cls = 'nav-link';
4812             }
4813             if (this.icon) {
4814                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4815             }
4816             if (this.fa) {
4817                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4818             }
4819             if(this.glyphicon) {
4820                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4821             }
4822             
4823             if (this.menu) {
4824                 
4825                 cfg.cn[0].html += " <span class='caret'></span>";
4826              
4827             }
4828             
4829             if (this.badge !== '') {
4830                  
4831                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4832             }
4833         }
4834         
4835         
4836         
4837         return cfg;
4838     },
4839     onRender : function(ct, position)
4840     {
4841        // Roo.log("Call onRender: " + this.xtype);
4842         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4843             this.tag = 'div';
4844         }
4845         
4846         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4847         this.navLink = this.el.select('.nav-link',true).first();
4848         return ret;
4849     },
4850       
4851     
4852     initEvents: function() 
4853     {
4854         if (typeof (this.menu) != 'undefined') {
4855             this.menu.parentType = this.xtype;
4856             this.menu.triggerEl = this.el;
4857             this.menu = this.addxtype(Roo.apply({}, this.menu));
4858         }
4859         
4860         this.el.select('a',true).on('click', this.onClick, this);
4861         
4862         if(this.tagtype == 'span'){
4863             this.el.select('span',true).on('click', this.onClick, this);
4864         }
4865        
4866         // at this point parent should be available..
4867         this.parent().register(this);
4868     },
4869     
4870     onClick : function(e)
4871     {
4872         if (e.getTarget('.dropdown-menu-item')) {
4873             // did you click on a menu itemm.... - then don't trigger onclick..
4874             return;
4875         }
4876         
4877         if(
4878                 this.preventDefault || 
4879                 this.href == '#' 
4880         ){
4881             Roo.log("NavItem - prevent Default?");
4882             e.preventDefault();
4883         }
4884         
4885         if (this.disabled) {
4886             return;
4887         }
4888         
4889         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4890         if (tg && tg.transition) {
4891             Roo.log("waiting for the transitionend");
4892             return;
4893         }
4894         
4895         
4896         
4897         //Roo.log("fire event clicked");
4898         if(this.fireEvent('click', this, e) === false){
4899             return;
4900         };
4901         
4902         if(this.tagtype == 'span'){
4903             return;
4904         }
4905         
4906         //Roo.log(this.href);
4907         var ael = this.el.select('a',true).first();
4908         //Roo.log(ael);
4909         
4910         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4911             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4912             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4913                 return; // ignore... - it's a 'hash' to another page.
4914             }
4915             Roo.log("NavItem - prevent Default?");
4916             e.preventDefault();
4917             this.scrollToElement(e);
4918         }
4919         
4920         
4921         var p =  this.parent();
4922    
4923         if (['tabs','pills'].indexOf(p.type)!==-1) {
4924             if (typeof(p.setActiveItem) !== 'undefined') {
4925                 p.setActiveItem(this);
4926             }
4927         }
4928         
4929         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4930         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4931             // remove the collapsed menu expand...
4932             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
4933         }
4934     },
4935     
4936     isActive: function () {
4937         return this.active
4938     },
4939     setActive : function(state, fire, is_was_active)
4940     {
4941         if (this.active && !state && this.navId) {
4942             this.was_active = true;
4943             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4944             if (nv) {
4945                 nv.clearWasActive(this);
4946             }
4947             
4948         }
4949         this.active = state;
4950         
4951         if (!state ) {
4952             this.el.removeClass('active');
4953             this.navLink ? this.navLink.removeClass('active') : false;
4954         } else if (!this.el.hasClass('active')) {
4955             
4956             this.el.addClass('active');
4957             if (Roo.bootstrap.version == 4 && this.navLink ) {
4958                 this.navLink.addClass('active');
4959             }
4960             
4961         }
4962         if (fire) {
4963             this.fireEvent('changed', this, state);
4964         }
4965         
4966         // show a panel if it's registered and related..
4967         
4968         if (!this.navId || !this.tabId || !state || is_was_active) {
4969             return;
4970         }
4971         
4972         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4973         if (!tg) {
4974             return;
4975         }
4976         var pan = tg.getPanelByName(this.tabId);
4977         if (!pan) {
4978             return;
4979         }
4980         // if we can not flip to new panel - go back to old nav highlight..
4981         if (false == tg.showPanel(pan)) {
4982             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4983             if (nv) {
4984                 var onav = nv.getWasActive();
4985                 if (onav) {
4986                     onav.setActive(true, false, true);
4987                 }
4988             }
4989             
4990         }
4991         
4992         
4993         
4994     },
4995      // this should not be here...
4996     setDisabled : function(state)
4997     {
4998         this.disabled = state;
4999         if (!state ) {
5000             this.el.removeClass('disabled');
5001         } else if (!this.el.hasClass('disabled')) {
5002             this.el.addClass('disabled');
5003         }
5004         
5005     },
5006     
5007     /**
5008      * Fetch the element to display the tooltip on.
5009      * @return {Roo.Element} defaults to this.el
5010      */
5011     tooltipEl : function()
5012     {
5013         return this.el.select('' + this.tagtype + '', true).first();
5014     },
5015     
5016     scrollToElement : function(e)
5017     {
5018         var c = document.body;
5019         
5020         /*
5021          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5022          */
5023         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5024             c = document.documentElement;
5025         }
5026         
5027         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5028         
5029         if(!target){
5030             return;
5031         }
5032
5033         var o = target.calcOffsetsTo(c);
5034         
5035         var options = {
5036             target : target,
5037             value : o[1]
5038         };
5039         
5040         this.fireEvent('scrollto', this, options, e);
5041         
5042         Roo.get(c).scrollTo('top', options.value, true);
5043         
5044         return;
5045     }
5046 });
5047  
5048
5049  /*
5050  * - LGPL
5051  *
5052  * sidebar item
5053  *
5054  *  li
5055  *    <span> icon </span>
5056  *    <span> text </span>
5057  *    <span>badge </span>
5058  */
5059
5060 /**
5061  * @class Roo.bootstrap.NavSidebarItem
5062  * @extends Roo.bootstrap.NavItem
5063  * Bootstrap Navbar.NavSidebarItem class
5064  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5065  * {Boolean} open is the menu open
5066  * {Boolean} buttonView use button as the tigger el rather that a (default false)
5067  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5068  * {String} buttonSize (sm|md|lg)the extra classes for the button
5069  * {Boolean} showArrow show arrow next to the text (default true)
5070  * @constructor
5071  * Create a new Navbar Button
5072  * @param {Object} config The config object
5073  */
5074 Roo.bootstrap.NavSidebarItem = function(config){
5075     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5076     this.addEvents({
5077         // raw events
5078         /**
5079          * @event click
5080          * The raw click event for the entire grid.
5081          * @param {Roo.EventObject} e
5082          */
5083         "click" : true,
5084          /**
5085             * @event changed
5086             * Fires when the active item active state changes
5087             * @param {Roo.bootstrap.NavSidebarItem} this
5088             * @param {boolean} state the new state
5089              
5090          */
5091         'changed': true
5092     });
5093    
5094 };
5095
5096 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5097     
5098     badgeWeight : 'default',
5099     
5100     open: false,
5101     
5102     buttonView : false,
5103     
5104     buttonWeight : 'default',
5105     
5106     buttonSize : 'md',
5107     
5108     showArrow : true,
5109     
5110     getAutoCreate : function(){
5111         
5112         
5113         var a = {
5114                 tag: 'a',
5115                 href : this.href || '#',
5116                 cls: '',
5117                 html : '',
5118                 cn : []
5119         };
5120         
5121         if(this.buttonView){
5122             a = {
5123                 tag: 'button',
5124                 href : this.href || '#',
5125                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5126                 html : this.html,
5127                 cn : []
5128             };
5129         }
5130         
5131         var cfg = {
5132             tag: 'li',
5133             cls: '',
5134             cn: [ a ]
5135         };
5136         
5137         if (this.active) {
5138             cfg.cls += ' active';
5139         }
5140         
5141         if (this.disabled) {
5142             cfg.cls += ' disabled';
5143         }
5144         if (this.open) {
5145             cfg.cls += ' open x-open';
5146         }
5147         // left icon..
5148         if (this.glyphicon || this.icon) {
5149             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5150             a.cn.push({ tag : 'i', cls : c }) ;
5151         }
5152         
5153         if(!this.buttonView){
5154             var span = {
5155                 tag: 'span',
5156                 html : this.html || ''
5157             };
5158
5159             a.cn.push(span);
5160             
5161         }
5162         
5163         if (this.badge !== '') {
5164             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5165         }
5166         
5167         if (this.menu) {
5168             
5169             if(this.showArrow){
5170                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5171             }
5172             
5173             a.cls += ' dropdown-toggle treeview' ;
5174         }
5175         
5176         return cfg;
5177     },
5178     
5179     initEvents : function()
5180     { 
5181         if (typeof (this.menu) != 'undefined') {
5182             this.menu.parentType = this.xtype;
5183             this.menu.triggerEl = this.el;
5184             this.menu = this.addxtype(Roo.apply({}, this.menu));
5185         }
5186         
5187         this.el.on('click', this.onClick, this);
5188         
5189         if(this.badge !== ''){
5190             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5191         }
5192         
5193     },
5194     
5195     onClick : function(e)
5196     {
5197         if(this.disabled){
5198             e.preventDefault();
5199             return;
5200         }
5201         
5202         if(this.preventDefault){
5203             e.preventDefault();
5204         }
5205         
5206         this.fireEvent('click', this, e);
5207     },
5208     
5209     disable : function()
5210     {
5211         this.setDisabled(true);
5212     },
5213     
5214     enable : function()
5215     {
5216         this.setDisabled(false);
5217     },
5218     
5219     setDisabled : function(state)
5220     {
5221         if(this.disabled == state){
5222             return;
5223         }
5224         
5225         this.disabled = state;
5226         
5227         if (state) {
5228             this.el.addClass('disabled');
5229             return;
5230         }
5231         
5232         this.el.removeClass('disabled');
5233         
5234         return;
5235     },
5236     
5237     setActive : function(state)
5238     {
5239         if(this.active == state){
5240             return;
5241         }
5242         
5243         this.active = state;
5244         
5245         if (state) {
5246             this.el.addClass('active');
5247             return;
5248         }
5249         
5250         this.el.removeClass('active');
5251         
5252         return;
5253     },
5254     
5255     isActive: function () 
5256     {
5257         return this.active;
5258     },
5259     
5260     setBadge : function(str)
5261     {
5262         if(!this.badgeEl){
5263             return;
5264         }
5265         
5266         this.badgeEl.dom.innerHTML = str;
5267     }
5268     
5269    
5270      
5271  
5272 });
5273  
5274
5275  /*
5276  * - LGPL
5277  *
5278  * row
5279  * 
5280  */
5281
5282 /**
5283  * @class Roo.bootstrap.Row
5284  * @extends Roo.bootstrap.Component
5285  * Bootstrap Row class (contains columns...)
5286  * 
5287  * @constructor
5288  * Create a new Row
5289  * @param {Object} config The config object
5290  */
5291
5292 Roo.bootstrap.Row = function(config){
5293     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5294 };
5295
5296 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5297     
5298     getAutoCreate : function(){
5299        return {
5300             cls: 'row clearfix'
5301        };
5302     }
5303     
5304     
5305 });
5306
5307  
5308
5309  /*
5310  * - LGPL
5311  *
5312  * element
5313  * 
5314  */
5315
5316 /**
5317  * @class Roo.bootstrap.Element
5318  * @extends Roo.bootstrap.Component
5319  * Bootstrap Element class
5320  * @cfg {String} html contents of the element
5321  * @cfg {String} tag tag of the element
5322  * @cfg {String} cls class of the element
5323  * @cfg {Boolean} preventDefault (true|false) default false
5324  * @cfg {Boolean} clickable (true|false) default false
5325  * 
5326  * @constructor
5327  * Create a new Element
5328  * @param {Object} config The config object
5329  */
5330
5331 Roo.bootstrap.Element = function(config){
5332     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5333     
5334     this.addEvents({
5335         // raw events
5336         /**
5337          * @event click
5338          * When a element is chick
5339          * @param {Roo.bootstrap.Element} this
5340          * @param {Roo.EventObject} e
5341          */
5342         "click" : true
5343     });
5344 };
5345
5346 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5347     
5348     tag: 'div',
5349     cls: '',
5350     html: '',
5351     preventDefault: false, 
5352     clickable: false,
5353     
5354     getAutoCreate : function(){
5355         
5356         var cfg = {
5357             tag: this.tag,
5358             // cls: this.cls, double assign in parent class Component.js :: onRender
5359             html: this.html
5360         };
5361         
5362         return cfg;
5363     },
5364     
5365     initEvents: function() 
5366     {
5367         Roo.bootstrap.Element.superclass.initEvents.call(this);
5368         
5369         if(this.clickable){
5370             this.el.on('click', this.onClick, this);
5371         }
5372         
5373     },
5374     
5375     onClick : function(e)
5376     {
5377         if(this.preventDefault){
5378             e.preventDefault();
5379         }
5380         
5381         this.fireEvent('click', this, e);
5382     },
5383     
5384     getValue : function()
5385     {
5386         return this.el.dom.innerHTML;
5387     },
5388     
5389     setValue : function(value)
5390     {
5391         this.el.dom.innerHTML = value;
5392     }
5393    
5394 });
5395
5396  
5397
5398  /*
5399  * - LGPL
5400  *
5401  * pagination
5402  * 
5403  */
5404
5405 /**
5406  * @class Roo.bootstrap.Pagination
5407  * @extends Roo.bootstrap.Component
5408  * Bootstrap Pagination class
5409  * @cfg {String} size xs | sm | md | lg
5410  * @cfg {Boolean} inverse false | true
5411  * 
5412  * @constructor
5413  * Create a new Pagination
5414  * @param {Object} config The config object
5415  */
5416
5417 Roo.bootstrap.Pagination = function(config){
5418     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5419 };
5420
5421 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5422     
5423     cls: false,
5424     size: false,
5425     inverse: false,
5426     
5427     getAutoCreate : function(){
5428         var cfg = {
5429             tag: 'ul',
5430                 cls: 'pagination'
5431         };
5432         if (this.inverse) {
5433             cfg.cls += ' inverse';
5434         }
5435         if (this.html) {
5436             cfg.html=this.html;
5437         }
5438         if (this.cls) {
5439             cfg.cls += " " + this.cls;
5440         }
5441         return cfg;
5442     }
5443    
5444 });
5445
5446  
5447
5448  /*
5449  * - LGPL
5450  *
5451  * Pagination item
5452  * 
5453  */
5454
5455
5456 /**
5457  * @class Roo.bootstrap.PaginationItem
5458  * @extends Roo.bootstrap.Component
5459  * Bootstrap PaginationItem class
5460  * @cfg {String} html text
5461  * @cfg {String} href the link
5462  * @cfg {Boolean} preventDefault (true | false) default true
5463  * @cfg {Boolean} active (true | false) default false
5464  * @cfg {Boolean} disabled default false
5465  * 
5466  * 
5467  * @constructor
5468  * Create a new PaginationItem
5469  * @param {Object} config The config object
5470  */
5471
5472
5473 Roo.bootstrap.PaginationItem = function(config){
5474     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5475     this.addEvents({
5476         // raw events
5477         /**
5478          * @event click
5479          * The raw click event for the entire grid.
5480          * @param {Roo.EventObject} e
5481          */
5482         "click" : true
5483     });
5484 };
5485
5486 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5487     
5488     href : false,
5489     html : false,
5490     preventDefault: true,
5491     active : false,
5492     cls : false,
5493     disabled: false,
5494     
5495     getAutoCreate : function(){
5496         var cfg= {
5497             tag: 'li',
5498             cn: [
5499                 {
5500                     tag : 'a',
5501                     href : this.href ? this.href : '#',
5502                     html : this.html ? this.html : ''
5503                 }
5504             ]
5505         };
5506         
5507         if(this.cls){
5508             cfg.cls = this.cls;
5509         }
5510         
5511         if(this.disabled){
5512             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5513         }
5514         
5515         if(this.active){
5516             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5517         }
5518         
5519         return cfg;
5520     },
5521     
5522     initEvents: function() {
5523         
5524         this.el.on('click', this.onClick, this);
5525         
5526     },
5527     onClick : function(e)
5528     {
5529         Roo.log('PaginationItem on click ');
5530         if(this.preventDefault){
5531             e.preventDefault();
5532         }
5533         
5534         if(this.disabled){
5535             return;
5536         }
5537         
5538         this.fireEvent('click', this, e);
5539     }
5540    
5541 });
5542
5543  
5544
5545  /*
5546  * - LGPL
5547  *
5548  * slider
5549  * 
5550  */
5551
5552
5553 /**
5554  * @class Roo.bootstrap.Slider
5555  * @extends Roo.bootstrap.Component
5556  * Bootstrap Slider class
5557  *    
5558  * @constructor
5559  * Create a new Slider
5560  * @param {Object} config The config object
5561  */
5562
5563 Roo.bootstrap.Slider = function(config){
5564     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5565 };
5566
5567 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5568     
5569     getAutoCreate : function(){
5570         
5571         var cfg = {
5572             tag: 'div',
5573             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5574             cn: [
5575                 {
5576                     tag: 'a',
5577                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5578                 }
5579             ]
5580         };
5581         
5582         return cfg;
5583     }
5584    
5585 });
5586
5587  /*
5588  * Based on:
5589  * Ext JS Library 1.1.1
5590  * Copyright(c) 2006-2007, Ext JS, LLC.
5591  *
5592  * Originally Released Under LGPL - original licence link has changed is not relivant.
5593  *
5594  * Fork - LGPL
5595  * <script type="text/javascript">
5596  */
5597  
5598
5599 /**
5600  * @class Roo.grid.ColumnModel
5601  * @extends Roo.util.Observable
5602  * This is the default implementation of a ColumnModel used by the Grid. It defines
5603  * the columns in the grid.
5604  * <br>Usage:<br>
5605  <pre><code>
5606  var colModel = new Roo.grid.ColumnModel([
5607         {header: "Ticker", width: 60, sortable: true, locked: true},
5608         {header: "Company Name", width: 150, sortable: true},
5609         {header: "Market Cap.", width: 100, sortable: true},
5610         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5611         {header: "Employees", width: 100, sortable: true, resizable: false}
5612  ]);
5613  </code></pre>
5614  * <p>
5615  
5616  * The config options listed for this class are options which may appear in each
5617  * individual column definition.
5618  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5619  * @constructor
5620  * @param {Object} config An Array of column config objects. See this class's
5621  * config objects for details.
5622 */
5623 Roo.grid.ColumnModel = function(config){
5624         /**
5625      * The config passed into the constructor
5626      */
5627     this.config = config;
5628     this.lookup = {};
5629
5630     // if no id, create one
5631     // if the column does not have a dataIndex mapping,
5632     // map it to the order it is in the config
5633     for(var i = 0, len = config.length; i < len; i++){
5634         var c = config[i];
5635         if(typeof c.dataIndex == "undefined"){
5636             c.dataIndex = i;
5637         }
5638         if(typeof c.renderer == "string"){
5639             c.renderer = Roo.util.Format[c.renderer];
5640         }
5641         if(typeof c.id == "undefined"){
5642             c.id = Roo.id();
5643         }
5644         if(c.editor && c.editor.xtype){
5645             c.editor  = Roo.factory(c.editor, Roo.grid);
5646         }
5647         if(c.editor && c.editor.isFormField){
5648             c.editor = new Roo.grid.GridEditor(c.editor);
5649         }
5650         this.lookup[c.id] = c;
5651     }
5652
5653     /**
5654      * The width of columns which have no width specified (defaults to 100)
5655      * @type Number
5656      */
5657     this.defaultWidth = 100;
5658
5659     /**
5660      * Default sortable of columns which have no sortable specified (defaults to false)
5661      * @type Boolean
5662      */
5663     this.defaultSortable = false;
5664
5665     this.addEvents({
5666         /**
5667              * @event widthchange
5668              * Fires when the width of a column changes.
5669              * @param {ColumnModel} this
5670              * @param {Number} columnIndex The column index
5671              * @param {Number} newWidth The new width
5672              */
5673             "widthchange": true,
5674         /**
5675              * @event headerchange
5676              * Fires when the text of a header changes.
5677              * @param {ColumnModel} this
5678              * @param {Number} columnIndex The column index
5679              * @param {Number} newText The new header text
5680              */
5681             "headerchange": true,
5682         /**
5683              * @event hiddenchange
5684              * Fires when a column is hidden or "unhidden".
5685              * @param {ColumnModel} this
5686              * @param {Number} columnIndex The column index
5687              * @param {Boolean} hidden true if hidden, false otherwise
5688              */
5689             "hiddenchange": true,
5690             /**
5691          * @event columnmoved
5692          * Fires when a column is moved.
5693          * @param {ColumnModel} this
5694          * @param {Number} oldIndex
5695          * @param {Number} newIndex
5696          */
5697         "columnmoved" : true,
5698         /**
5699          * @event columlockchange
5700          * Fires when a column's locked state is changed
5701          * @param {ColumnModel} this
5702          * @param {Number} colIndex
5703          * @param {Boolean} locked true if locked
5704          */
5705         "columnlockchange" : true
5706     });
5707     Roo.grid.ColumnModel.superclass.constructor.call(this);
5708 };
5709 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5710     /**
5711      * @cfg {String} header The header text to display in the Grid view.
5712      */
5713     /**
5714      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5715      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5716      * specified, the column's index is used as an index into the Record's data Array.
5717      */
5718     /**
5719      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5720      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5721      */
5722     /**
5723      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5724      * Defaults to the value of the {@link #defaultSortable} property.
5725      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5726      */
5727     /**
5728      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5729      */
5730     /**
5731      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5732      */
5733     /**
5734      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5735      */
5736     /**
5737      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5738      */
5739     /**
5740      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5741      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5742      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5743      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5744      */
5745        /**
5746      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5747      */
5748     /**
5749      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5750      */
5751     /**
5752      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5753      */
5754     /**
5755      * @cfg {String} cursor (Optional)
5756      */
5757     /**
5758      * @cfg {String} tooltip (Optional)
5759      */
5760     /**
5761      * @cfg {Number} xs (Optional)
5762      */
5763     /**
5764      * @cfg {Number} sm (Optional)
5765      */
5766     /**
5767      * @cfg {Number} md (Optional)
5768      */
5769     /**
5770      * @cfg {Number} lg (Optional)
5771      */
5772     /**
5773      * Returns the id of the column at the specified index.
5774      * @param {Number} index The column index
5775      * @return {String} the id
5776      */
5777     getColumnId : function(index){
5778         return this.config[index].id;
5779     },
5780
5781     /**
5782      * Returns the column for a specified id.
5783      * @param {String} id The column id
5784      * @return {Object} the column
5785      */
5786     getColumnById : function(id){
5787         return this.lookup[id];
5788     },
5789
5790     
5791     /**
5792      * Returns the column for a specified dataIndex.
5793      * @param {String} dataIndex The column dataIndex
5794      * @return {Object|Boolean} the column or false if not found
5795      */
5796     getColumnByDataIndex: function(dataIndex){
5797         var index = this.findColumnIndex(dataIndex);
5798         return index > -1 ? this.config[index] : false;
5799     },
5800     
5801     /**
5802      * Returns the index for a specified column id.
5803      * @param {String} id The column id
5804      * @return {Number} the index, or -1 if not found
5805      */
5806     getIndexById : function(id){
5807         for(var i = 0, len = this.config.length; i < len; i++){
5808             if(this.config[i].id == id){
5809                 return i;
5810             }
5811         }
5812         return -1;
5813     },
5814     
5815     /**
5816      * Returns the index for a specified column dataIndex.
5817      * @param {String} dataIndex The column dataIndex
5818      * @return {Number} the index, or -1 if not found
5819      */
5820     
5821     findColumnIndex : function(dataIndex){
5822         for(var i = 0, len = this.config.length; i < len; i++){
5823             if(this.config[i].dataIndex == dataIndex){
5824                 return i;
5825             }
5826         }
5827         return -1;
5828     },
5829     
5830     
5831     moveColumn : function(oldIndex, newIndex){
5832         var c = this.config[oldIndex];
5833         this.config.splice(oldIndex, 1);
5834         this.config.splice(newIndex, 0, c);
5835         this.dataMap = null;
5836         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5837     },
5838
5839     isLocked : function(colIndex){
5840         return this.config[colIndex].locked === true;
5841     },
5842
5843     setLocked : function(colIndex, value, suppressEvent){
5844         if(this.isLocked(colIndex) == value){
5845             return;
5846         }
5847         this.config[colIndex].locked = value;
5848         if(!suppressEvent){
5849             this.fireEvent("columnlockchange", this, colIndex, value);
5850         }
5851     },
5852
5853     getTotalLockedWidth : function(){
5854         var totalWidth = 0;
5855         for(var i = 0; i < this.config.length; i++){
5856             if(this.isLocked(i) && !this.isHidden(i)){
5857                 this.totalWidth += this.getColumnWidth(i);
5858             }
5859         }
5860         return totalWidth;
5861     },
5862
5863     getLockedCount : function(){
5864         for(var i = 0, len = this.config.length; i < len; i++){
5865             if(!this.isLocked(i)){
5866                 return i;
5867             }
5868         }
5869         
5870         return this.config.length;
5871     },
5872
5873     /**
5874      * Returns the number of columns.
5875      * @return {Number}
5876      */
5877     getColumnCount : function(visibleOnly){
5878         if(visibleOnly === true){
5879             var c = 0;
5880             for(var i = 0, len = this.config.length; i < len; i++){
5881                 if(!this.isHidden(i)){
5882                     c++;
5883                 }
5884             }
5885             return c;
5886         }
5887         return this.config.length;
5888     },
5889
5890     /**
5891      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5892      * @param {Function} fn
5893      * @param {Object} scope (optional)
5894      * @return {Array} result
5895      */
5896     getColumnsBy : function(fn, scope){
5897         var r = [];
5898         for(var i = 0, len = this.config.length; i < len; i++){
5899             var c = this.config[i];
5900             if(fn.call(scope||this, c, i) === true){
5901                 r[r.length] = c;
5902             }
5903         }
5904         return r;
5905     },
5906
5907     /**
5908      * Returns true if the specified column is sortable.
5909      * @param {Number} col The column index
5910      * @return {Boolean}
5911      */
5912     isSortable : function(col){
5913         if(typeof this.config[col].sortable == "undefined"){
5914             return this.defaultSortable;
5915         }
5916         return this.config[col].sortable;
5917     },
5918
5919     /**
5920      * Returns the rendering (formatting) function defined for the column.
5921      * @param {Number} col The column index.
5922      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5923      */
5924     getRenderer : function(col){
5925         if(!this.config[col].renderer){
5926             return Roo.grid.ColumnModel.defaultRenderer;
5927         }
5928         return this.config[col].renderer;
5929     },
5930
5931     /**
5932      * Sets the rendering (formatting) function for a column.
5933      * @param {Number} col The column index
5934      * @param {Function} fn The function to use to process the cell's raw data
5935      * to return HTML markup for the grid view. The render function is called with
5936      * the following parameters:<ul>
5937      * <li>Data value.</li>
5938      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5939      * <li>css A CSS style string to apply to the table cell.</li>
5940      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5941      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5942      * <li>Row index</li>
5943      * <li>Column index</li>
5944      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5945      */
5946     setRenderer : function(col, fn){
5947         this.config[col].renderer = fn;
5948     },
5949
5950     /**
5951      * Returns the width for the specified column.
5952      * @param {Number} col The column index
5953      * @return {Number}
5954      */
5955     getColumnWidth : function(col){
5956         return this.config[col].width * 1 || this.defaultWidth;
5957     },
5958
5959     /**
5960      * Sets the width for a column.
5961      * @param {Number} col The column index
5962      * @param {Number} width The new width
5963      */
5964     setColumnWidth : function(col, width, suppressEvent){
5965         this.config[col].width = width;
5966         this.totalWidth = null;
5967         if(!suppressEvent){
5968              this.fireEvent("widthchange", this, col, width);
5969         }
5970     },
5971
5972     /**
5973      * Returns the total width of all columns.
5974      * @param {Boolean} includeHidden True to include hidden column widths
5975      * @return {Number}
5976      */
5977     getTotalWidth : function(includeHidden){
5978         if(!this.totalWidth){
5979             this.totalWidth = 0;
5980             for(var i = 0, len = this.config.length; i < len; i++){
5981                 if(includeHidden || !this.isHidden(i)){
5982                     this.totalWidth += this.getColumnWidth(i);
5983                 }
5984             }
5985         }
5986         return this.totalWidth;
5987     },
5988
5989     /**
5990      * Returns the header for the specified column.
5991      * @param {Number} col The column index
5992      * @return {String}
5993      */
5994     getColumnHeader : function(col){
5995         return this.config[col].header;
5996     },
5997
5998     /**
5999      * Sets the header for a column.
6000      * @param {Number} col The column index
6001      * @param {String} header The new header
6002      */
6003     setColumnHeader : function(col, header){
6004         this.config[col].header = header;
6005         this.fireEvent("headerchange", this, col, header);
6006     },
6007
6008     /**
6009      * Returns the tooltip for the specified column.
6010      * @param {Number} col The column index
6011      * @return {String}
6012      */
6013     getColumnTooltip : function(col){
6014             return this.config[col].tooltip;
6015     },
6016     /**
6017      * Sets the tooltip for a column.
6018      * @param {Number} col The column index
6019      * @param {String} tooltip The new tooltip
6020      */
6021     setColumnTooltip : function(col, tooltip){
6022             this.config[col].tooltip = tooltip;
6023     },
6024
6025     /**
6026      * Returns the dataIndex for the specified column.
6027      * @param {Number} col The column index
6028      * @return {Number}
6029      */
6030     getDataIndex : function(col){
6031         return this.config[col].dataIndex;
6032     },
6033
6034     /**
6035      * Sets the dataIndex for a column.
6036      * @param {Number} col The column index
6037      * @param {Number} dataIndex The new dataIndex
6038      */
6039     setDataIndex : function(col, dataIndex){
6040         this.config[col].dataIndex = dataIndex;
6041     },
6042
6043     
6044     
6045     /**
6046      * Returns true if the cell is editable.
6047      * @param {Number} colIndex The column index
6048      * @param {Number} rowIndex The row index - this is nto actually used..?
6049      * @return {Boolean}
6050      */
6051     isCellEditable : function(colIndex, rowIndex){
6052         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6053     },
6054
6055     /**
6056      * Returns the editor defined for the cell/column.
6057      * return false or null to disable editing.
6058      * @param {Number} colIndex The column index
6059      * @param {Number} rowIndex The row index
6060      * @return {Object}
6061      */
6062     getCellEditor : function(colIndex, rowIndex){
6063         return this.config[colIndex].editor;
6064     },
6065
6066     /**
6067      * Sets if a column is editable.
6068      * @param {Number} col The column index
6069      * @param {Boolean} editable True if the column is editable
6070      */
6071     setEditable : function(col, editable){
6072         this.config[col].editable = editable;
6073     },
6074
6075
6076     /**
6077      * Returns true if the column is hidden.
6078      * @param {Number} colIndex The column index
6079      * @return {Boolean}
6080      */
6081     isHidden : function(colIndex){
6082         return this.config[colIndex].hidden;
6083     },
6084
6085
6086     /**
6087      * Returns true if the column width cannot be changed
6088      */
6089     isFixed : function(colIndex){
6090         return this.config[colIndex].fixed;
6091     },
6092
6093     /**
6094      * Returns true if the column can be resized
6095      * @return {Boolean}
6096      */
6097     isResizable : function(colIndex){
6098         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6099     },
6100     /**
6101      * Sets if a column is hidden.
6102      * @param {Number} colIndex The column index
6103      * @param {Boolean} hidden True if the column is hidden
6104      */
6105     setHidden : function(colIndex, hidden){
6106         this.config[colIndex].hidden = hidden;
6107         this.totalWidth = null;
6108         this.fireEvent("hiddenchange", this, colIndex, hidden);
6109     },
6110
6111     /**
6112      * Sets the editor for a column.
6113      * @param {Number} col The column index
6114      * @param {Object} editor The editor object
6115      */
6116     setEditor : function(col, editor){
6117         this.config[col].editor = editor;
6118     }
6119 });
6120
6121 Roo.grid.ColumnModel.defaultRenderer = function(value)
6122 {
6123     if(typeof value == "object") {
6124         return value;
6125     }
6126         if(typeof value == "string" && value.length < 1){
6127             return "&#160;";
6128         }
6129     
6130         return String.format("{0}", value);
6131 };
6132
6133 // Alias for backwards compatibility
6134 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6135 /*
6136  * Based on:
6137  * Ext JS Library 1.1.1
6138  * Copyright(c) 2006-2007, Ext JS, LLC.
6139  *
6140  * Originally Released Under LGPL - original licence link has changed is not relivant.
6141  *
6142  * Fork - LGPL
6143  * <script type="text/javascript">
6144  */
6145  
6146 /**
6147  * @class Roo.LoadMask
6148  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6149  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6150  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6151  * element's UpdateManager load indicator and will be destroyed after the initial load.
6152  * @constructor
6153  * Create a new LoadMask
6154  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6155  * @param {Object} config The config object
6156  */
6157 Roo.LoadMask = function(el, config){
6158     this.el = Roo.get(el);
6159     Roo.apply(this, config);
6160     if(this.store){
6161         this.store.on('beforeload', this.onBeforeLoad, this);
6162         this.store.on('load', this.onLoad, this);
6163         this.store.on('loadexception', this.onLoadException, this);
6164         this.removeMask = false;
6165     }else{
6166         var um = this.el.getUpdateManager();
6167         um.showLoadIndicator = false; // disable the default indicator
6168         um.on('beforeupdate', this.onBeforeLoad, this);
6169         um.on('update', this.onLoad, this);
6170         um.on('failure', this.onLoad, this);
6171         this.removeMask = true;
6172     }
6173 };
6174
6175 Roo.LoadMask.prototype = {
6176     /**
6177      * @cfg {Boolean} removeMask
6178      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6179      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6180      */
6181     /**
6182      * @cfg {String} msg
6183      * The text to display in a centered loading message box (defaults to 'Loading...')
6184      */
6185     msg : 'Loading...',
6186     /**
6187      * @cfg {String} msgCls
6188      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6189      */
6190     msgCls : 'x-mask-loading',
6191
6192     /**
6193      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6194      * @type Boolean
6195      */
6196     disabled: false,
6197
6198     /**
6199      * Disables the mask to prevent it from being displayed
6200      */
6201     disable : function(){
6202        this.disabled = true;
6203     },
6204
6205     /**
6206      * Enables the mask so that it can be displayed
6207      */
6208     enable : function(){
6209         this.disabled = false;
6210     },
6211     
6212     onLoadException : function()
6213     {
6214         Roo.log(arguments);
6215         
6216         if (typeof(arguments[3]) != 'undefined') {
6217             Roo.MessageBox.alert("Error loading",arguments[3]);
6218         } 
6219         /*
6220         try {
6221             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6222                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6223             }   
6224         } catch(e) {
6225             
6226         }
6227         */
6228     
6229         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6230     },
6231     // private
6232     onLoad : function()
6233     {
6234         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6235     },
6236
6237     // private
6238     onBeforeLoad : function(){
6239         if(!this.disabled){
6240             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6241         }
6242     },
6243
6244     // private
6245     destroy : function(){
6246         if(this.store){
6247             this.store.un('beforeload', this.onBeforeLoad, this);
6248             this.store.un('load', this.onLoad, this);
6249             this.store.un('loadexception', this.onLoadException, this);
6250         }else{
6251             var um = this.el.getUpdateManager();
6252             um.un('beforeupdate', this.onBeforeLoad, this);
6253             um.un('update', this.onLoad, this);
6254             um.un('failure', this.onLoad, this);
6255         }
6256     }
6257 };/*
6258  * - LGPL
6259  *
6260  * table
6261  * 
6262  */
6263
6264 /**
6265  * @class Roo.bootstrap.Table
6266  * @extends Roo.bootstrap.Component
6267  * Bootstrap Table class
6268  * @cfg {String} cls table class
6269  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6270  * @cfg {String} bgcolor Specifies the background color for a table
6271  * @cfg {Number} border Specifies whether the table cells should have borders or not
6272  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6273  * @cfg {Number} cellspacing Specifies the space between cells
6274  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6275  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6276  * @cfg {String} sortable Specifies that the table should be sortable
6277  * @cfg {String} summary Specifies a summary of the content of a table
6278  * @cfg {Number} width Specifies the width of a table
6279  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6280  * 
6281  * @cfg {boolean} striped Should the rows be alternative striped
6282  * @cfg {boolean} bordered Add borders to the table
6283  * @cfg {boolean} hover Add hover highlighting
6284  * @cfg {boolean} condensed Format condensed
6285  * @cfg {boolean} responsive Format condensed
6286  * @cfg {Boolean} loadMask (true|false) default false
6287  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6288  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6289  * @cfg {Boolean} rowSelection (true|false) default false
6290  * @cfg {Boolean} cellSelection (true|false) default false
6291  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6292  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6293  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6294  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6295  
6296  * 
6297  * @constructor
6298  * Create a new Table
6299  * @param {Object} config The config object
6300  */
6301
6302 Roo.bootstrap.Table = function(config){
6303     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6304     
6305   
6306     
6307     // BC...
6308     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6309     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6310     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6311     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6312     
6313     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6314     if (this.sm) {
6315         this.sm.grid = this;
6316         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6317         this.sm = this.selModel;
6318         this.sm.xmodule = this.xmodule || false;
6319     }
6320     
6321     if (this.cm && typeof(this.cm.config) == 'undefined') {
6322         this.colModel = new Roo.grid.ColumnModel(this.cm);
6323         this.cm = this.colModel;
6324         this.cm.xmodule = this.xmodule || false;
6325     }
6326     if (this.store) {
6327         this.store= Roo.factory(this.store, Roo.data);
6328         this.ds = this.store;
6329         this.ds.xmodule = this.xmodule || false;
6330          
6331     }
6332     if (this.footer && this.store) {
6333         this.footer.dataSource = this.ds;
6334         this.footer = Roo.factory(this.footer);
6335     }
6336     
6337     /** @private */
6338     this.addEvents({
6339         /**
6340          * @event cellclick
6341          * Fires when a cell is clicked
6342          * @param {Roo.bootstrap.Table} this
6343          * @param {Roo.Element} el
6344          * @param {Number} rowIndex
6345          * @param {Number} columnIndex
6346          * @param {Roo.EventObject} e
6347          */
6348         "cellclick" : true,
6349         /**
6350          * @event celldblclick
6351          * Fires when a cell is double clicked
6352          * @param {Roo.bootstrap.Table} this
6353          * @param {Roo.Element} el
6354          * @param {Number} rowIndex
6355          * @param {Number} columnIndex
6356          * @param {Roo.EventObject} e
6357          */
6358         "celldblclick" : true,
6359         /**
6360          * @event rowclick
6361          * Fires when a row is clicked
6362          * @param {Roo.bootstrap.Table} this
6363          * @param {Roo.Element} el
6364          * @param {Number} rowIndex
6365          * @param {Roo.EventObject} e
6366          */
6367         "rowclick" : true,
6368         /**
6369          * @event rowdblclick
6370          * Fires when a row is double clicked
6371          * @param {Roo.bootstrap.Table} this
6372          * @param {Roo.Element} el
6373          * @param {Number} rowIndex
6374          * @param {Roo.EventObject} e
6375          */
6376         "rowdblclick" : true,
6377         /**
6378          * @event mouseover
6379          * Fires when a mouseover occur
6380          * @param {Roo.bootstrap.Table} this
6381          * @param {Roo.Element} el
6382          * @param {Number} rowIndex
6383          * @param {Number} columnIndex
6384          * @param {Roo.EventObject} e
6385          */
6386         "mouseover" : true,
6387         /**
6388          * @event mouseout
6389          * Fires when a mouseout occur
6390          * @param {Roo.bootstrap.Table} this
6391          * @param {Roo.Element} el
6392          * @param {Number} rowIndex
6393          * @param {Number} columnIndex
6394          * @param {Roo.EventObject} e
6395          */
6396         "mouseout" : true,
6397         /**
6398          * @event rowclass
6399          * Fires when a row is rendered, so you can change add a style to it.
6400          * @param {Roo.bootstrap.Table} this
6401          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6402          */
6403         'rowclass' : true,
6404           /**
6405          * @event rowsrendered
6406          * Fires when all the  rows have been rendered
6407          * @param {Roo.bootstrap.Table} this
6408          */
6409         'rowsrendered' : true,
6410         /**
6411          * @event contextmenu
6412          * The raw contextmenu event for the entire grid.
6413          * @param {Roo.EventObject} e
6414          */
6415         "contextmenu" : true,
6416         /**
6417          * @event rowcontextmenu
6418          * Fires when a row is right clicked
6419          * @param {Roo.bootstrap.Table} this
6420          * @param {Number} rowIndex
6421          * @param {Roo.EventObject} e
6422          */
6423         "rowcontextmenu" : true,
6424         /**
6425          * @event cellcontextmenu
6426          * Fires when a cell is right clicked
6427          * @param {Roo.bootstrap.Table} this
6428          * @param {Number} rowIndex
6429          * @param {Number} cellIndex
6430          * @param {Roo.EventObject} e
6431          */
6432          "cellcontextmenu" : true,
6433          /**
6434          * @event headercontextmenu
6435          * Fires when a header is right clicked
6436          * @param {Roo.bootstrap.Table} this
6437          * @param {Number} columnIndex
6438          * @param {Roo.EventObject} e
6439          */
6440         "headercontextmenu" : true
6441     });
6442 };
6443
6444 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6445     
6446     cls: false,
6447     align: false,
6448     bgcolor: false,
6449     border: false,
6450     cellpadding: false,
6451     cellspacing: false,
6452     frame: false,
6453     rules: false,
6454     sortable: false,
6455     summary: false,
6456     width: false,
6457     striped : false,
6458     scrollBody : false,
6459     bordered: false,
6460     hover:  false,
6461     condensed : false,
6462     responsive : false,
6463     sm : false,
6464     cm : false,
6465     store : false,
6466     loadMask : false,
6467     footerShow : true,
6468     headerShow : true,
6469   
6470     rowSelection : false,
6471     cellSelection : false,
6472     layout : false,
6473     
6474     // Roo.Element - the tbody
6475     mainBody: false,
6476     // Roo.Element - thead element
6477     mainHead: false,
6478     
6479     container: false, // used by gridpanel...
6480     
6481     lazyLoad : false,
6482     
6483     CSS : Roo.util.CSS,
6484     
6485     auto_hide_footer : false,
6486     
6487     getAutoCreate : function()
6488     {
6489         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6490         
6491         cfg = {
6492             tag: 'table',
6493             cls : 'table',
6494             cn : []
6495         };
6496         if (this.scrollBody) {
6497             cfg.cls += ' table-body-fixed';
6498         }    
6499         if (this.striped) {
6500             cfg.cls += ' table-striped';
6501         }
6502         
6503         if (this.hover) {
6504             cfg.cls += ' table-hover';
6505         }
6506         if (this.bordered) {
6507             cfg.cls += ' table-bordered';
6508         }
6509         if (this.condensed) {
6510             cfg.cls += ' table-condensed';
6511         }
6512         if (this.responsive) {
6513             cfg.cls += ' table-responsive';
6514         }
6515         
6516         if (this.cls) {
6517             cfg.cls+=  ' ' +this.cls;
6518         }
6519         
6520         // this lot should be simplifed...
6521         var _t = this;
6522         var cp = [
6523             'align',
6524             'bgcolor',
6525             'border',
6526             'cellpadding',
6527             'cellspacing',
6528             'frame',
6529             'rules',
6530             'sortable',
6531             'summary',
6532             'width'
6533         ].forEach(function(k) {
6534             if (_t[k]) {
6535                 cfg[k] = _t[k];
6536             }
6537         });
6538         
6539         
6540         if (this.layout) {
6541             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6542         }
6543         
6544         if(this.store || this.cm){
6545             if(this.headerShow){
6546                 cfg.cn.push(this.renderHeader());
6547             }
6548             
6549             cfg.cn.push(this.renderBody());
6550             
6551             if(this.footerShow){
6552                 cfg.cn.push(this.renderFooter());
6553             }
6554             // where does this come from?
6555             //cfg.cls+=  ' TableGrid';
6556         }
6557         
6558         return { cn : [ cfg ] };
6559     },
6560     
6561     initEvents : function()
6562     {   
6563         if(!this.store || !this.cm){
6564             return;
6565         }
6566         if (this.selModel) {
6567             this.selModel.initEvents();
6568         }
6569         
6570         
6571         //Roo.log('initEvents with ds!!!!');
6572         
6573         this.mainBody = this.el.select('tbody', true).first();
6574         this.mainHead = this.el.select('thead', true).first();
6575         this.mainFoot = this.el.select('tfoot', true).first();
6576         
6577         
6578         
6579         var _this = this;
6580         
6581         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6582             e.on('click', _this.sort, _this);
6583         });
6584         
6585         this.mainBody.on("click", this.onClick, this);
6586         this.mainBody.on("dblclick", this.onDblClick, this);
6587         
6588         // why is this done????? = it breaks dialogs??
6589         //this.parent().el.setStyle('position', 'relative');
6590         
6591         
6592         if (this.footer) {
6593             this.footer.parentId = this.id;
6594             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6595             
6596             if(this.lazyLoad){
6597                 this.el.select('tfoot tr td').first().addClass('hide');
6598             }
6599         } 
6600         
6601         if(this.loadMask) {
6602             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6603         }
6604         
6605         this.store.on('load', this.onLoad, this);
6606         this.store.on('beforeload', this.onBeforeLoad, this);
6607         this.store.on('update', this.onUpdate, this);
6608         this.store.on('add', this.onAdd, this);
6609         this.store.on("clear", this.clear, this);
6610         
6611         this.el.on("contextmenu", this.onContextMenu, this);
6612         
6613         this.mainBody.on('scroll', this.onBodyScroll, this);
6614         
6615         this.cm.on("headerchange", this.onHeaderChange, this);
6616         
6617         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6618         
6619     },
6620     
6621     onContextMenu : function(e, t)
6622     {
6623         this.processEvent("contextmenu", e);
6624     },
6625     
6626     processEvent : function(name, e)
6627     {
6628         if (name != 'touchstart' ) {
6629             this.fireEvent(name, e);    
6630         }
6631         
6632         var t = e.getTarget();
6633         
6634         var cell = Roo.get(t);
6635         
6636         if(!cell){
6637             return;
6638         }
6639         
6640         if(cell.findParent('tfoot', false, true)){
6641             return;
6642         }
6643         
6644         if(cell.findParent('thead', false, true)){
6645             
6646             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6647                 cell = Roo.get(t).findParent('th', false, true);
6648                 if (!cell) {
6649                     Roo.log("failed to find th in thead?");
6650                     Roo.log(e.getTarget());
6651                     return;
6652                 }
6653             }
6654             
6655             var cellIndex = cell.dom.cellIndex;
6656             
6657             var ename = name == 'touchstart' ? 'click' : name;
6658             this.fireEvent("header" + ename, this, cellIndex, e);
6659             
6660             return;
6661         }
6662         
6663         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6664             cell = Roo.get(t).findParent('td', false, true);
6665             if (!cell) {
6666                 Roo.log("failed to find th in tbody?");
6667                 Roo.log(e.getTarget());
6668                 return;
6669             }
6670         }
6671         
6672         var row = cell.findParent('tr', false, true);
6673         var cellIndex = cell.dom.cellIndex;
6674         var rowIndex = row.dom.rowIndex - 1;
6675         
6676         if(row !== false){
6677             
6678             this.fireEvent("row" + name, this, rowIndex, e);
6679             
6680             if(cell !== false){
6681             
6682                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6683             }
6684         }
6685         
6686     },
6687     
6688     onMouseover : function(e, el)
6689     {
6690         var cell = Roo.get(el);
6691         
6692         if(!cell){
6693             return;
6694         }
6695         
6696         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6697             cell = cell.findParent('td', false, true);
6698         }
6699         
6700         var row = cell.findParent('tr', false, true);
6701         var cellIndex = cell.dom.cellIndex;
6702         var rowIndex = row.dom.rowIndex - 1; // start from 0
6703         
6704         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6705         
6706     },
6707     
6708     onMouseout : function(e, el)
6709     {
6710         var cell = Roo.get(el);
6711         
6712         if(!cell){
6713             return;
6714         }
6715         
6716         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6717             cell = cell.findParent('td', false, true);
6718         }
6719         
6720         var row = cell.findParent('tr', false, true);
6721         var cellIndex = cell.dom.cellIndex;
6722         var rowIndex = row.dom.rowIndex - 1; // start from 0
6723         
6724         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6725         
6726     },
6727     
6728     onClick : function(e, el)
6729     {
6730         var cell = Roo.get(el);
6731         
6732         if(!cell || (!this.cellSelection && !this.rowSelection)){
6733             return;
6734         }
6735         
6736         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6737             cell = cell.findParent('td', false, true);
6738         }
6739         
6740         if(!cell || typeof(cell) == 'undefined'){
6741             return;
6742         }
6743         
6744         var row = cell.findParent('tr', false, true);
6745         
6746         if(!row || typeof(row) == 'undefined'){
6747             return;
6748         }
6749         
6750         var cellIndex = cell.dom.cellIndex;
6751         var rowIndex = this.getRowIndex(row);
6752         
6753         // why??? - should these not be based on SelectionModel?
6754         if(this.cellSelection){
6755             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6756         }
6757         
6758         if(this.rowSelection){
6759             this.fireEvent('rowclick', this, row, rowIndex, e);
6760         }
6761         
6762         
6763     },
6764         
6765     onDblClick : function(e,el)
6766     {
6767         var cell = Roo.get(el);
6768         
6769         if(!cell || (!this.cellSelection && !this.rowSelection)){
6770             return;
6771         }
6772         
6773         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6774             cell = cell.findParent('td', false, true);
6775         }
6776         
6777         if(!cell || typeof(cell) == 'undefined'){
6778             return;
6779         }
6780         
6781         var row = cell.findParent('tr', false, true);
6782         
6783         if(!row || typeof(row) == 'undefined'){
6784             return;
6785         }
6786         
6787         var cellIndex = cell.dom.cellIndex;
6788         var rowIndex = this.getRowIndex(row);
6789         
6790         if(this.cellSelection){
6791             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6792         }
6793         
6794         if(this.rowSelection){
6795             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6796         }
6797     },
6798     
6799     sort : function(e,el)
6800     {
6801         var col = Roo.get(el);
6802         
6803         if(!col.hasClass('sortable')){
6804             return;
6805         }
6806         
6807         var sort = col.attr('sort');
6808         var dir = 'ASC';
6809         
6810         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6811             dir = 'DESC';
6812         }
6813         
6814         this.store.sortInfo = {field : sort, direction : dir};
6815         
6816         if (this.footer) {
6817             Roo.log("calling footer first");
6818             this.footer.onClick('first');
6819         } else {
6820         
6821             this.store.load({ params : { start : 0 } });
6822         }
6823     },
6824     
6825     renderHeader : function()
6826     {
6827         var header = {
6828             tag: 'thead',
6829             cn : []
6830         };
6831         
6832         var cm = this.cm;
6833         this.totalWidth = 0;
6834         
6835         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6836             
6837             var config = cm.config[i];
6838             
6839             var c = {
6840                 tag: 'th',
6841                 cls : 'x-hcol-' + i,
6842                 style : '',
6843                 html: cm.getColumnHeader(i)
6844             };
6845             
6846             var hh = '';
6847             
6848             if(typeof(config.sortable) != 'undefined' && config.sortable){
6849                 c.cls = 'sortable';
6850                 c.html = '<i class="glyphicon"></i>' + c.html;
6851             }
6852             
6853             // could use BS4 hidden-..-down 
6854             
6855             if(typeof(config.lgHeader) != 'undefined'){
6856                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
6857             }
6858             
6859             if(typeof(config.mdHeader) != 'undefined'){
6860                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6861             }
6862             
6863             if(typeof(config.smHeader) != 'undefined'){
6864                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6865             }
6866             
6867             if(typeof(config.xsHeader) != 'undefined'){
6868                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6869             }
6870             
6871             if(hh.length){
6872                 c.html = hh;
6873             }
6874             
6875             if(typeof(config.tooltip) != 'undefined'){
6876                 c.tooltip = config.tooltip;
6877             }
6878             
6879             if(typeof(config.colspan) != 'undefined'){
6880                 c.colspan = config.colspan;
6881             }
6882             
6883             if(typeof(config.hidden) != 'undefined' && config.hidden){
6884                 c.style += ' display:none;';
6885             }
6886             
6887             if(typeof(config.dataIndex) != 'undefined'){
6888                 c.sort = config.dataIndex;
6889             }
6890             
6891            
6892             
6893             if(typeof(config.align) != 'undefined' && config.align.length){
6894                 c.style += ' text-align:' + config.align + ';';
6895             }
6896             
6897             if(typeof(config.width) != 'undefined'){
6898                 c.style += ' width:' + config.width + 'px;';
6899                 this.totalWidth += config.width;
6900             } else {
6901                 this.totalWidth += 100; // assume minimum of 100 per column?
6902             }
6903             
6904             if(typeof(config.cls) != 'undefined'){
6905                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6906             }
6907             
6908             ['xs','sm','md','lg'].map(function(size){
6909                 
6910                 if(typeof(config[size]) == 'undefined'){
6911                     return;
6912                 }
6913                  
6914                 if (!config[size]) { // 0 = hidden
6915                     // BS 4 '0' is treated as hide that column and below.
6916                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
6917                     return;
6918                 }
6919                 
6920                 c.cls += ' col-' + size + '-' + config[size] + (
6921                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
6922                 );
6923                 
6924                 
6925             });
6926             
6927             header.cn.push(c)
6928         }
6929         
6930         return header;
6931     },
6932     
6933     renderBody : function()
6934     {
6935         var body = {
6936             tag: 'tbody',
6937             cn : [
6938                 {
6939                     tag: 'tr',
6940                     cn : [
6941                         {
6942                             tag : 'td',
6943                             colspan :  this.cm.getColumnCount()
6944                         }
6945                     ]
6946                 }
6947             ]
6948         };
6949         
6950         return body;
6951     },
6952     
6953     renderFooter : function()
6954     {
6955         var footer = {
6956             tag: 'tfoot',
6957             cn : [
6958                 {
6959                     tag: 'tr',
6960                     cn : [
6961                         {
6962                             tag : 'td',
6963                             colspan :  this.cm.getColumnCount()
6964                         }
6965                     ]
6966                 }
6967             ]
6968         };
6969         
6970         return footer;
6971     },
6972     
6973     
6974     
6975     onLoad : function()
6976     {
6977 //        Roo.log('ds onload');
6978         this.clear();
6979         
6980         var _this = this;
6981         var cm = this.cm;
6982         var ds = this.store;
6983         
6984         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6985             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6986             if (_this.store.sortInfo) {
6987                     
6988                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6989                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6990                 }
6991                 
6992                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6993                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6994                 }
6995             }
6996         });
6997         
6998         var tbody =  this.mainBody;
6999               
7000         if(ds.getCount() > 0){
7001             ds.data.each(function(d,rowIndex){
7002                 var row =  this.renderRow(cm, ds, rowIndex);
7003                 
7004                 tbody.createChild(row);
7005                 
7006                 var _this = this;
7007                 
7008                 if(row.cellObjects.length){
7009                     Roo.each(row.cellObjects, function(r){
7010                         _this.renderCellObject(r);
7011                     })
7012                 }
7013                 
7014             }, this);
7015         }
7016         
7017         var tfoot = this.el.select('tfoot', true).first();
7018         
7019         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7020             
7021             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7022             
7023             var total = this.ds.getTotalCount();
7024             
7025             if(this.footer.pageSize < total){
7026                 this.mainFoot.show();
7027             }
7028         }
7029         
7030         Roo.each(this.el.select('tbody td', true).elements, function(e){
7031             e.on('mouseover', _this.onMouseover, _this);
7032         });
7033         
7034         Roo.each(this.el.select('tbody td', true).elements, function(e){
7035             e.on('mouseout', _this.onMouseout, _this);
7036         });
7037         this.fireEvent('rowsrendered', this);
7038         
7039         this.autoSize();
7040     },
7041     
7042     
7043     onUpdate : function(ds,record)
7044     {
7045         this.refreshRow(record);
7046         this.autoSize();
7047     },
7048     
7049     onRemove : function(ds, record, index, isUpdate){
7050         if(isUpdate !== true){
7051             this.fireEvent("beforerowremoved", this, index, record);
7052         }
7053         var bt = this.mainBody.dom;
7054         
7055         var rows = this.el.select('tbody > tr', true).elements;
7056         
7057         if(typeof(rows[index]) != 'undefined'){
7058             bt.removeChild(rows[index].dom);
7059         }
7060         
7061 //        if(bt.rows[index]){
7062 //            bt.removeChild(bt.rows[index]);
7063 //        }
7064         
7065         if(isUpdate !== true){
7066             //this.stripeRows(index);
7067             //this.syncRowHeights(index, index);
7068             //this.layout();
7069             this.fireEvent("rowremoved", this, index, record);
7070         }
7071     },
7072     
7073     onAdd : function(ds, records, rowIndex)
7074     {
7075         //Roo.log('on Add called');
7076         // - note this does not handle multiple adding very well..
7077         var bt = this.mainBody.dom;
7078         for (var i =0 ; i < records.length;i++) {
7079             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7080             //Roo.log(records[i]);
7081             //Roo.log(this.store.getAt(rowIndex+i));
7082             this.insertRow(this.store, rowIndex + i, false);
7083             return;
7084         }
7085         
7086     },
7087     
7088     
7089     refreshRow : function(record){
7090         var ds = this.store, index;
7091         if(typeof record == 'number'){
7092             index = record;
7093             record = ds.getAt(index);
7094         }else{
7095             index = ds.indexOf(record);
7096         }
7097         this.insertRow(ds, index, true);
7098         this.autoSize();
7099         this.onRemove(ds, record, index+1, true);
7100         this.autoSize();
7101         //this.syncRowHeights(index, index);
7102         //this.layout();
7103         this.fireEvent("rowupdated", this, index, record);
7104     },
7105     
7106     insertRow : function(dm, rowIndex, isUpdate){
7107         
7108         if(!isUpdate){
7109             this.fireEvent("beforerowsinserted", this, rowIndex);
7110         }
7111             //var s = this.getScrollState();
7112         var row = this.renderRow(this.cm, this.store, rowIndex);
7113         // insert before rowIndex..
7114         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7115         
7116         var _this = this;
7117                 
7118         if(row.cellObjects.length){
7119             Roo.each(row.cellObjects, function(r){
7120                 _this.renderCellObject(r);
7121             })
7122         }
7123             
7124         if(!isUpdate){
7125             this.fireEvent("rowsinserted", this, rowIndex);
7126             //this.syncRowHeights(firstRow, lastRow);
7127             //this.stripeRows(firstRow);
7128             //this.layout();
7129         }
7130         
7131     },
7132     
7133     
7134     getRowDom : function(rowIndex)
7135     {
7136         var rows = this.el.select('tbody > tr', true).elements;
7137         
7138         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7139         
7140     },
7141     // returns the object tree for a tr..
7142   
7143     
7144     renderRow : function(cm, ds, rowIndex) 
7145     {
7146         var d = ds.getAt(rowIndex);
7147         
7148         var row = {
7149             tag : 'tr',
7150             cls : 'x-row-' + rowIndex,
7151             cn : []
7152         };
7153             
7154         var cellObjects = [];
7155         
7156         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7157             var config = cm.config[i];
7158             
7159             var renderer = cm.getRenderer(i);
7160             var value = '';
7161             var id = false;
7162             
7163             if(typeof(renderer) !== 'undefined'){
7164                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7165             }
7166             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7167             // and are rendered into the cells after the row is rendered - using the id for the element.
7168             
7169             if(typeof(value) === 'object'){
7170                 id = Roo.id();
7171                 cellObjects.push({
7172                     container : id,
7173                     cfg : value 
7174                 })
7175             }
7176             
7177             var rowcfg = {
7178                 record: d,
7179                 rowIndex : rowIndex,
7180                 colIndex : i,
7181                 rowClass : ''
7182             };
7183
7184             this.fireEvent('rowclass', this, rowcfg);
7185             
7186             var td = {
7187                 tag: 'td',
7188                 cls : rowcfg.rowClass + ' x-col-' + i,
7189                 style: '',
7190                 html: (typeof(value) === 'object') ? '' : value
7191             };
7192             
7193             if (id) {
7194                 td.id = id;
7195             }
7196             
7197             if(typeof(config.colspan) != 'undefined'){
7198                 td.colspan = config.colspan;
7199             }
7200             
7201             if(typeof(config.hidden) != 'undefined' && config.hidden){
7202                 td.style += ' display:none;';
7203             }
7204             
7205             if(typeof(config.align) != 'undefined' && config.align.length){
7206                 td.style += ' text-align:' + config.align + ';';
7207             }
7208             if(typeof(config.valign) != 'undefined' && config.valign.length){
7209                 td.style += ' vertical-align:' + config.valign + ';';
7210             }
7211             
7212             if(typeof(config.width) != 'undefined'){
7213                 td.style += ' width:' +  config.width + 'px;';
7214             }
7215             
7216             if(typeof(config.cursor) != 'undefined'){
7217                 td.style += ' cursor:' +  config.cursor + ';';
7218             }
7219             
7220             if(typeof(config.cls) != 'undefined'){
7221                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7222             }
7223             
7224             ['xs','sm','md','lg'].map(function(size){
7225                 
7226                 if(typeof(config[size]) == 'undefined'){
7227                     return;
7228                 }
7229                 
7230                 
7231                   
7232                 if (!config[size]) { // 0 = hidden
7233                     // BS 4 '0' is treated as hide that column and below.
7234                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7235                     return;
7236                 }
7237                 
7238                 td.cls += ' col-' + size + '-' + config[size] + (
7239                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
7240                 );
7241                  
7242
7243             });
7244             
7245             row.cn.push(td);
7246            
7247         }
7248         
7249         row.cellObjects = cellObjects;
7250         
7251         return row;
7252           
7253     },
7254     
7255     
7256     
7257     onBeforeLoad : function()
7258     {
7259         
7260     },
7261      /**
7262      * Remove all rows
7263      */
7264     clear : function()
7265     {
7266         this.el.select('tbody', true).first().dom.innerHTML = '';
7267     },
7268     /**
7269      * Show or hide a row.
7270      * @param {Number} rowIndex to show or hide
7271      * @param {Boolean} state hide
7272      */
7273     setRowVisibility : function(rowIndex, state)
7274     {
7275         var bt = this.mainBody.dom;
7276         
7277         var rows = this.el.select('tbody > tr', true).elements;
7278         
7279         if(typeof(rows[rowIndex]) == 'undefined'){
7280             return;
7281         }
7282         rows[rowIndex].dom.style.display = state ? '' : 'none';
7283     },
7284     
7285     
7286     getSelectionModel : function(){
7287         if(!this.selModel){
7288             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7289         }
7290         return this.selModel;
7291     },
7292     /*
7293      * Render the Roo.bootstrap object from renderder
7294      */
7295     renderCellObject : function(r)
7296     {
7297         var _this = this;
7298         
7299         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7300         
7301         var t = r.cfg.render(r.container);
7302         
7303         if(r.cfg.cn){
7304             Roo.each(r.cfg.cn, function(c){
7305                 var child = {
7306                     container: t.getChildContainer(),
7307                     cfg: c
7308                 };
7309                 _this.renderCellObject(child);
7310             })
7311         }
7312     },
7313     
7314     getRowIndex : function(row)
7315     {
7316         var rowIndex = -1;
7317         
7318         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7319             if(el != row){
7320                 return;
7321             }
7322             
7323             rowIndex = index;
7324         });
7325         
7326         return rowIndex;
7327     },
7328      /**
7329      * Returns the grid's underlying element = used by panel.Grid
7330      * @return {Element} The element
7331      */
7332     getGridEl : function(){
7333         return this.el;
7334     },
7335      /**
7336      * Forces a resize - used by panel.Grid
7337      * @return {Element} The element
7338      */
7339     autoSize : function()
7340     {
7341         //var ctr = Roo.get(this.container.dom.parentElement);
7342         var ctr = Roo.get(this.el.dom);
7343         
7344         var thd = this.getGridEl().select('thead',true).first();
7345         var tbd = this.getGridEl().select('tbody', true).first();
7346         var tfd = this.getGridEl().select('tfoot', true).first();
7347         
7348         var cw = ctr.getWidth();
7349         
7350         if (tbd) {
7351             
7352             tbd.setSize(ctr.getWidth(),
7353                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7354             );
7355             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7356             cw -= barsize;
7357         }
7358         cw = Math.max(cw, this.totalWidth);
7359         this.getGridEl().select('tr',true).setWidth(cw);
7360         // resize 'expandable coloumn?
7361         
7362         return; // we doe not have a view in this design..
7363         
7364     },
7365     onBodyScroll: function()
7366     {
7367         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7368         if(this.mainHead){
7369             this.mainHead.setStyle({
7370                 'position' : 'relative',
7371                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7372             });
7373         }
7374         
7375         if(this.lazyLoad){
7376             
7377             var scrollHeight = this.mainBody.dom.scrollHeight;
7378             
7379             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7380             
7381             var height = this.mainBody.getHeight();
7382             
7383             if(scrollHeight - height == scrollTop) {
7384                 
7385                 var total = this.ds.getTotalCount();
7386                 
7387                 if(this.footer.cursor + this.footer.pageSize < total){
7388                     
7389                     this.footer.ds.load({
7390                         params : {
7391                             start : this.footer.cursor + this.footer.pageSize,
7392                             limit : this.footer.pageSize
7393                         },
7394                         add : true
7395                     });
7396                 }
7397             }
7398             
7399         }
7400     },
7401     
7402     onHeaderChange : function()
7403     {
7404         var header = this.renderHeader();
7405         var table = this.el.select('table', true).first();
7406         
7407         this.mainHead.remove();
7408         this.mainHead = table.createChild(header, this.mainBody, false);
7409     },
7410     
7411     onHiddenChange : function(colModel, colIndex, hidden)
7412     {
7413         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7414         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7415         
7416         this.CSS.updateRule(thSelector, "display", "");
7417         this.CSS.updateRule(tdSelector, "display", "");
7418         
7419         if(hidden){
7420             this.CSS.updateRule(thSelector, "display", "none");
7421             this.CSS.updateRule(tdSelector, "display", "none");
7422         }
7423         
7424         this.onHeaderChange();
7425         this.onLoad();
7426     },
7427     
7428     setColumnWidth: function(col_index, width)
7429     {
7430         // width = "md-2 xs-2..."
7431         if(!this.colModel.config[col_index]) {
7432             return;
7433         }
7434         
7435         var w = width.split(" ");
7436         
7437         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7438         
7439         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7440         
7441         
7442         for(var j = 0; j < w.length; j++) {
7443             
7444             if(!w[j]) {
7445                 continue;
7446             }
7447             
7448             var size_cls = w[j].split("-");
7449             
7450             if(!Number.isInteger(size_cls[1] * 1)) {
7451                 continue;
7452             }
7453             
7454             if(!this.colModel.config[col_index][size_cls[0]]) {
7455                 continue;
7456             }
7457             
7458             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7459                 continue;
7460             }
7461             
7462             h_row[0].classList.replace(
7463                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7464                 "col-"+size_cls[0]+"-"+size_cls[1]
7465             );
7466             
7467             for(var i = 0; i < rows.length; i++) {
7468                 
7469                 var size_cls = w[j].split("-");
7470                 
7471                 if(!Number.isInteger(size_cls[1] * 1)) {
7472                     continue;
7473                 }
7474                 
7475                 if(!this.colModel.config[col_index][size_cls[0]]) {
7476                     continue;
7477                 }
7478                 
7479                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7480                     continue;
7481                 }
7482                 
7483                 rows[i].classList.replace(
7484                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7485                     "col-"+size_cls[0]+"-"+size_cls[1]
7486                 );
7487             }
7488             
7489             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7490         }
7491     }
7492 });
7493
7494  
7495
7496  /*
7497  * - LGPL
7498  *
7499  * table cell
7500  * 
7501  */
7502
7503 /**
7504  * @class Roo.bootstrap.TableCell
7505  * @extends Roo.bootstrap.Component
7506  * Bootstrap TableCell class
7507  * @cfg {String} html cell contain text
7508  * @cfg {String} cls cell class
7509  * @cfg {String} tag cell tag (td|th) default td
7510  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7511  * @cfg {String} align Aligns the content in a cell
7512  * @cfg {String} axis Categorizes cells
7513  * @cfg {String} bgcolor Specifies the background color of a cell
7514  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7515  * @cfg {Number} colspan Specifies the number of columns a cell should span
7516  * @cfg {String} headers Specifies one or more header cells a cell is related to
7517  * @cfg {Number} height Sets the height of a cell
7518  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7519  * @cfg {Number} rowspan Sets the number of rows a cell should span
7520  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7521  * @cfg {String} valign Vertical aligns the content in a cell
7522  * @cfg {Number} width Specifies the width of a cell
7523  * 
7524  * @constructor
7525  * Create a new TableCell
7526  * @param {Object} config The config object
7527  */
7528
7529 Roo.bootstrap.TableCell = function(config){
7530     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7531 };
7532
7533 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7534     
7535     html: false,
7536     cls: false,
7537     tag: false,
7538     abbr: false,
7539     align: false,
7540     axis: false,
7541     bgcolor: false,
7542     charoff: false,
7543     colspan: false,
7544     headers: false,
7545     height: false,
7546     nowrap: false,
7547     rowspan: false,
7548     scope: false,
7549     valign: false,
7550     width: false,
7551     
7552     
7553     getAutoCreate : function(){
7554         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7555         
7556         cfg = {
7557             tag: 'td'
7558         };
7559         
7560         if(this.tag){
7561             cfg.tag = this.tag;
7562         }
7563         
7564         if (this.html) {
7565             cfg.html=this.html
7566         }
7567         if (this.cls) {
7568             cfg.cls=this.cls
7569         }
7570         if (this.abbr) {
7571             cfg.abbr=this.abbr
7572         }
7573         if (this.align) {
7574             cfg.align=this.align
7575         }
7576         if (this.axis) {
7577             cfg.axis=this.axis
7578         }
7579         if (this.bgcolor) {
7580             cfg.bgcolor=this.bgcolor
7581         }
7582         if (this.charoff) {
7583             cfg.charoff=this.charoff
7584         }
7585         if (this.colspan) {
7586             cfg.colspan=this.colspan
7587         }
7588         if (this.headers) {
7589             cfg.headers=this.headers
7590         }
7591         if (this.height) {
7592             cfg.height=this.height
7593         }
7594         if (this.nowrap) {
7595             cfg.nowrap=this.nowrap
7596         }
7597         if (this.rowspan) {
7598             cfg.rowspan=this.rowspan
7599         }
7600         if (this.scope) {
7601             cfg.scope=this.scope
7602         }
7603         if (this.valign) {
7604             cfg.valign=this.valign
7605         }
7606         if (this.width) {
7607             cfg.width=this.width
7608         }
7609         
7610         
7611         return cfg;
7612     }
7613    
7614 });
7615
7616  
7617
7618  /*
7619  * - LGPL
7620  *
7621  * table row
7622  * 
7623  */
7624
7625 /**
7626  * @class Roo.bootstrap.TableRow
7627  * @extends Roo.bootstrap.Component
7628  * Bootstrap TableRow class
7629  * @cfg {String} cls row class
7630  * @cfg {String} align Aligns the content in a table row
7631  * @cfg {String} bgcolor Specifies a background color for a table row
7632  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7633  * @cfg {String} valign Vertical aligns the content in a table row
7634  * 
7635  * @constructor
7636  * Create a new TableRow
7637  * @param {Object} config The config object
7638  */
7639
7640 Roo.bootstrap.TableRow = function(config){
7641     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7642 };
7643
7644 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7645     
7646     cls: false,
7647     align: false,
7648     bgcolor: false,
7649     charoff: false,
7650     valign: false,
7651     
7652     getAutoCreate : function(){
7653         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7654         
7655         cfg = {
7656             tag: 'tr'
7657         };
7658             
7659         if(this.cls){
7660             cfg.cls = this.cls;
7661         }
7662         if(this.align){
7663             cfg.align = this.align;
7664         }
7665         if(this.bgcolor){
7666             cfg.bgcolor = this.bgcolor;
7667         }
7668         if(this.charoff){
7669             cfg.charoff = this.charoff;
7670         }
7671         if(this.valign){
7672             cfg.valign = this.valign;
7673         }
7674         
7675         return cfg;
7676     }
7677    
7678 });
7679
7680  
7681
7682  /*
7683  * - LGPL
7684  *
7685  * table body
7686  * 
7687  */
7688
7689 /**
7690  * @class Roo.bootstrap.TableBody
7691  * @extends Roo.bootstrap.Component
7692  * Bootstrap TableBody class
7693  * @cfg {String} cls element class
7694  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7695  * @cfg {String} align Aligns the content inside the element
7696  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7697  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7698  * 
7699  * @constructor
7700  * Create a new TableBody
7701  * @param {Object} config The config object
7702  */
7703
7704 Roo.bootstrap.TableBody = function(config){
7705     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7706 };
7707
7708 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7709     
7710     cls: false,
7711     tag: false,
7712     align: false,
7713     charoff: false,
7714     valign: false,
7715     
7716     getAutoCreate : function(){
7717         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7718         
7719         cfg = {
7720             tag: 'tbody'
7721         };
7722             
7723         if (this.cls) {
7724             cfg.cls=this.cls
7725         }
7726         if(this.tag){
7727             cfg.tag = this.tag;
7728         }
7729         
7730         if(this.align){
7731             cfg.align = this.align;
7732         }
7733         if(this.charoff){
7734             cfg.charoff = this.charoff;
7735         }
7736         if(this.valign){
7737             cfg.valign = this.valign;
7738         }
7739         
7740         return cfg;
7741     }
7742     
7743     
7744 //    initEvents : function()
7745 //    {
7746 //        
7747 //        if(!this.store){
7748 //            return;
7749 //        }
7750 //        
7751 //        this.store = Roo.factory(this.store, Roo.data);
7752 //        this.store.on('load', this.onLoad, this);
7753 //        
7754 //        this.store.load();
7755 //        
7756 //    },
7757 //    
7758 //    onLoad: function () 
7759 //    {   
7760 //        this.fireEvent('load', this);
7761 //    }
7762 //    
7763 //   
7764 });
7765
7766  
7767
7768  /*
7769  * Based on:
7770  * Ext JS Library 1.1.1
7771  * Copyright(c) 2006-2007, Ext JS, LLC.
7772  *
7773  * Originally Released Under LGPL - original licence link has changed is not relivant.
7774  *
7775  * Fork - LGPL
7776  * <script type="text/javascript">
7777  */
7778
7779 // as we use this in bootstrap.
7780 Roo.namespace('Roo.form');
7781  /**
7782  * @class Roo.form.Action
7783  * Internal Class used to handle form actions
7784  * @constructor
7785  * @param {Roo.form.BasicForm} el The form element or its id
7786  * @param {Object} config Configuration options
7787  */
7788
7789  
7790  
7791 // define the action interface
7792 Roo.form.Action = function(form, options){
7793     this.form = form;
7794     this.options = options || {};
7795 };
7796 /**
7797  * Client Validation Failed
7798  * @const 
7799  */
7800 Roo.form.Action.CLIENT_INVALID = 'client';
7801 /**
7802  * Server Validation Failed
7803  * @const 
7804  */
7805 Roo.form.Action.SERVER_INVALID = 'server';
7806  /**
7807  * Connect to Server Failed
7808  * @const 
7809  */
7810 Roo.form.Action.CONNECT_FAILURE = 'connect';
7811 /**
7812  * Reading Data from Server Failed
7813  * @const 
7814  */
7815 Roo.form.Action.LOAD_FAILURE = 'load';
7816
7817 Roo.form.Action.prototype = {
7818     type : 'default',
7819     failureType : undefined,
7820     response : undefined,
7821     result : undefined,
7822
7823     // interface method
7824     run : function(options){
7825
7826     },
7827
7828     // interface method
7829     success : function(response){
7830
7831     },
7832
7833     // interface method
7834     handleResponse : function(response){
7835
7836     },
7837
7838     // default connection failure
7839     failure : function(response){
7840         
7841         this.response = response;
7842         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7843         this.form.afterAction(this, false);
7844     },
7845
7846     processResponse : function(response){
7847         this.response = response;
7848         if(!response.responseText){
7849             return true;
7850         }
7851         this.result = this.handleResponse(response);
7852         return this.result;
7853     },
7854
7855     // utility functions used internally
7856     getUrl : function(appendParams){
7857         var url = this.options.url || this.form.url || this.form.el.dom.action;
7858         if(appendParams){
7859             var p = this.getParams();
7860             if(p){
7861                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7862             }
7863         }
7864         return url;
7865     },
7866
7867     getMethod : function(){
7868         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7869     },
7870
7871     getParams : function(){
7872         var bp = this.form.baseParams;
7873         var p = this.options.params;
7874         if(p){
7875             if(typeof p == "object"){
7876                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7877             }else if(typeof p == 'string' && bp){
7878                 p += '&' + Roo.urlEncode(bp);
7879             }
7880         }else if(bp){
7881             p = Roo.urlEncode(bp);
7882         }
7883         return p;
7884     },
7885
7886     createCallback : function(){
7887         return {
7888             success: this.success,
7889             failure: this.failure,
7890             scope: this,
7891             timeout: (this.form.timeout*1000),
7892             upload: this.form.fileUpload ? this.success : undefined
7893         };
7894     }
7895 };
7896
7897 Roo.form.Action.Submit = function(form, options){
7898     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7899 };
7900
7901 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7902     type : 'submit',
7903
7904     haveProgress : false,
7905     uploadComplete : false,
7906     
7907     // uploadProgress indicator.
7908     uploadProgress : function()
7909     {
7910         if (!this.form.progressUrl) {
7911             return;
7912         }
7913         
7914         if (!this.haveProgress) {
7915             Roo.MessageBox.progress("Uploading", "Uploading");
7916         }
7917         if (this.uploadComplete) {
7918            Roo.MessageBox.hide();
7919            return;
7920         }
7921         
7922         this.haveProgress = true;
7923    
7924         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7925         
7926         var c = new Roo.data.Connection();
7927         c.request({
7928             url : this.form.progressUrl,
7929             params: {
7930                 id : uid
7931             },
7932             method: 'GET',
7933             success : function(req){
7934                //console.log(data);
7935                 var rdata = false;
7936                 var edata;
7937                 try  {
7938                    rdata = Roo.decode(req.responseText)
7939                 } catch (e) {
7940                     Roo.log("Invalid data from server..");
7941                     Roo.log(edata);
7942                     return;
7943                 }
7944                 if (!rdata || !rdata.success) {
7945                     Roo.log(rdata);
7946                     Roo.MessageBox.alert(Roo.encode(rdata));
7947                     return;
7948                 }
7949                 var data = rdata.data;
7950                 
7951                 if (this.uploadComplete) {
7952                    Roo.MessageBox.hide();
7953                    return;
7954                 }
7955                    
7956                 if (data){
7957                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7958                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7959                     );
7960                 }
7961                 this.uploadProgress.defer(2000,this);
7962             },
7963        
7964             failure: function(data) {
7965                 Roo.log('progress url failed ');
7966                 Roo.log(data);
7967             },
7968             scope : this
7969         });
7970            
7971     },
7972     
7973     
7974     run : function()
7975     {
7976         // run get Values on the form, so it syncs any secondary forms.
7977         this.form.getValues();
7978         
7979         var o = this.options;
7980         var method = this.getMethod();
7981         var isPost = method == 'POST';
7982         if(o.clientValidation === false || this.form.isValid()){
7983             
7984             if (this.form.progressUrl) {
7985                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7986                     (new Date() * 1) + '' + Math.random());
7987                     
7988             } 
7989             
7990             
7991             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7992                 form:this.form.el.dom,
7993                 url:this.getUrl(!isPost),
7994                 method: method,
7995                 params:isPost ? this.getParams() : null,
7996                 isUpload: this.form.fileUpload,
7997                 formData : this.form.formData
7998             }));
7999             
8000             this.uploadProgress();
8001
8002         }else if (o.clientValidation !== false){ // client validation failed
8003             this.failureType = Roo.form.Action.CLIENT_INVALID;
8004             this.form.afterAction(this, false);
8005         }
8006     },
8007
8008     success : function(response)
8009     {
8010         this.uploadComplete= true;
8011         if (this.haveProgress) {
8012             Roo.MessageBox.hide();
8013         }
8014         
8015         
8016         var result = this.processResponse(response);
8017         if(result === true || result.success){
8018             this.form.afterAction(this, true);
8019             return;
8020         }
8021         if(result.errors){
8022             this.form.markInvalid(result.errors);
8023             this.failureType = Roo.form.Action.SERVER_INVALID;
8024         }
8025         this.form.afterAction(this, false);
8026     },
8027     failure : function(response)
8028     {
8029         this.uploadComplete= true;
8030         if (this.haveProgress) {
8031             Roo.MessageBox.hide();
8032         }
8033         
8034         this.response = response;
8035         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8036         this.form.afterAction(this, false);
8037     },
8038     
8039     handleResponse : function(response){
8040         if(this.form.errorReader){
8041             var rs = this.form.errorReader.read(response);
8042             var errors = [];
8043             if(rs.records){
8044                 for(var i = 0, len = rs.records.length; i < len; i++) {
8045                     var r = rs.records[i];
8046                     errors[i] = r.data;
8047                 }
8048             }
8049             if(errors.length < 1){
8050                 errors = null;
8051             }
8052             return {
8053                 success : rs.success,
8054                 errors : errors
8055             };
8056         }
8057         var ret = false;
8058         try {
8059             ret = Roo.decode(response.responseText);
8060         } catch (e) {
8061             ret = {
8062                 success: false,
8063                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8064                 errors : []
8065             };
8066         }
8067         return ret;
8068         
8069     }
8070 });
8071
8072
8073 Roo.form.Action.Load = function(form, options){
8074     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8075     this.reader = this.form.reader;
8076 };
8077
8078 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8079     type : 'load',
8080
8081     run : function(){
8082         
8083         Roo.Ajax.request(Roo.apply(
8084                 this.createCallback(), {
8085                     method:this.getMethod(),
8086                     url:this.getUrl(false),
8087                     params:this.getParams()
8088         }));
8089     },
8090
8091     success : function(response){
8092         
8093         var result = this.processResponse(response);
8094         if(result === true || !result.success || !result.data){
8095             this.failureType = Roo.form.Action.LOAD_FAILURE;
8096             this.form.afterAction(this, false);
8097             return;
8098         }
8099         this.form.clearInvalid();
8100         this.form.setValues(result.data);
8101         this.form.afterAction(this, true);
8102     },
8103
8104     handleResponse : function(response){
8105         if(this.form.reader){
8106             var rs = this.form.reader.read(response);
8107             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8108             return {
8109                 success : rs.success,
8110                 data : data
8111             };
8112         }
8113         return Roo.decode(response.responseText);
8114     }
8115 });
8116
8117 Roo.form.Action.ACTION_TYPES = {
8118     'load' : Roo.form.Action.Load,
8119     'submit' : Roo.form.Action.Submit
8120 };/*
8121  * - LGPL
8122  *
8123  * form
8124  *
8125  */
8126
8127 /**
8128  * @class Roo.bootstrap.Form
8129  * @extends Roo.bootstrap.Component
8130  * Bootstrap Form class
8131  * @cfg {String} method  GET | POST (default POST)
8132  * @cfg {String} labelAlign top | left (default top)
8133  * @cfg {String} align left  | right - for navbars
8134  * @cfg {Boolean} loadMask load mask when submit (default true)
8135
8136  *
8137  * @constructor
8138  * Create a new Form
8139  * @param {Object} config The config object
8140  */
8141
8142
8143 Roo.bootstrap.Form = function(config){
8144     
8145     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8146     
8147     Roo.bootstrap.Form.popover.apply();
8148     
8149     this.addEvents({
8150         /**
8151          * @event clientvalidation
8152          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8153          * @param {Form} this
8154          * @param {Boolean} valid true if the form has passed client-side validation
8155          */
8156         clientvalidation: true,
8157         /**
8158          * @event beforeaction
8159          * Fires before any action is performed. Return false to cancel the action.
8160          * @param {Form} this
8161          * @param {Action} action The action to be performed
8162          */
8163         beforeaction: true,
8164         /**
8165          * @event actionfailed
8166          * Fires when an action fails.
8167          * @param {Form} this
8168          * @param {Action} action The action that failed
8169          */
8170         actionfailed : true,
8171         /**
8172          * @event actioncomplete
8173          * Fires when an action is completed.
8174          * @param {Form} this
8175          * @param {Action} action The action that completed
8176          */
8177         actioncomplete : true
8178     });
8179 };
8180
8181 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8182
8183      /**
8184      * @cfg {String} method
8185      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8186      */
8187     method : 'POST',
8188     /**
8189      * @cfg {String} url
8190      * The URL to use for form actions if one isn't supplied in the action options.
8191      */
8192     /**
8193      * @cfg {Boolean} fileUpload
8194      * Set to true if this form is a file upload.
8195      */
8196
8197     /**
8198      * @cfg {Object} baseParams
8199      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8200      */
8201
8202     /**
8203      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8204      */
8205     timeout: 30,
8206     /**
8207      * @cfg {Sting} align (left|right) for navbar forms
8208      */
8209     align : 'left',
8210
8211     // private
8212     activeAction : null,
8213
8214     /**
8215      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8216      * element by passing it or its id or mask the form itself by passing in true.
8217      * @type Mixed
8218      */
8219     waitMsgTarget : false,
8220
8221     loadMask : true,
8222     
8223     /**
8224      * @cfg {Boolean} errorMask (true|false) default false
8225      */
8226     errorMask : false,
8227     
8228     /**
8229      * @cfg {Number} maskOffset Default 100
8230      */
8231     maskOffset : 100,
8232     
8233     /**
8234      * @cfg {Boolean} maskBody
8235      */
8236     maskBody : false,
8237
8238     getAutoCreate : function(){
8239
8240         var cfg = {
8241             tag: 'form',
8242             method : this.method || 'POST',
8243             id : this.id || Roo.id(),
8244             cls : ''
8245         };
8246         if (this.parent().xtype.match(/^Nav/)) {
8247             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8248
8249         }
8250
8251         if (this.labelAlign == 'left' ) {
8252             cfg.cls += ' form-horizontal';
8253         }
8254
8255
8256         return cfg;
8257     },
8258     initEvents : function()
8259     {
8260         this.el.on('submit', this.onSubmit, this);
8261         // this was added as random key presses on the form where triggering form submit.
8262         this.el.on('keypress', function(e) {
8263             if (e.getCharCode() != 13) {
8264                 return true;
8265             }
8266             // we might need to allow it for textareas.. and some other items.
8267             // check e.getTarget().
8268
8269             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8270                 return true;
8271             }
8272
8273             Roo.log("keypress blocked");
8274
8275             e.preventDefault();
8276             return false;
8277         });
8278         
8279     },
8280     // private
8281     onSubmit : function(e){
8282         e.stopEvent();
8283     },
8284
8285      /**
8286      * Returns true if client-side validation on the form is successful.
8287      * @return Boolean
8288      */
8289     isValid : function(){
8290         var items = this.getItems();
8291         var valid = true;
8292         var target = false;
8293         
8294         items.each(function(f){
8295             
8296             if(f.validate()){
8297                 return;
8298             }
8299             
8300             Roo.log('invalid field: ' + f.name);
8301             
8302             valid = false;
8303
8304             if(!target && f.el.isVisible(true)){
8305                 target = f;
8306             }
8307            
8308         });
8309         
8310         if(this.errorMask && !valid){
8311             Roo.bootstrap.Form.popover.mask(this, target);
8312         }
8313         
8314         return valid;
8315     },
8316     
8317     /**
8318      * Returns true if any fields in this form have changed since their original load.
8319      * @return Boolean
8320      */
8321     isDirty : function(){
8322         var dirty = false;
8323         var items = this.getItems();
8324         items.each(function(f){
8325            if(f.isDirty()){
8326                dirty = true;
8327                return false;
8328            }
8329            return true;
8330         });
8331         return dirty;
8332     },
8333      /**
8334      * Performs a predefined action (submit or load) or custom actions you define on this form.
8335      * @param {String} actionName The name of the action type
8336      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8337      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8338      * accept other config options):
8339      * <pre>
8340 Property          Type             Description
8341 ----------------  ---------------  ----------------------------------------------------------------------------------
8342 url               String           The url for the action (defaults to the form's url)
8343 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8344 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8345 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8346                                    validate the form on the client (defaults to false)
8347      * </pre>
8348      * @return {BasicForm} this
8349      */
8350     doAction : function(action, options){
8351         if(typeof action == 'string'){
8352             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8353         }
8354         if(this.fireEvent('beforeaction', this, action) !== false){
8355             this.beforeAction(action);
8356             action.run.defer(100, action);
8357         }
8358         return this;
8359     },
8360
8361     // private
8362     beforeAction : function(action){
8363         var o = action.options;
8364         
8365         if(this.loadMask){
8366             
8367             if(this.maskBody){
8368                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8369             } else {
8370                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8371             }
8372         }
8373         // not really supported yet.. ??
8374
8375         //if(this.waitMsgTarget === true){
8376         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8377         //}else if(this.waitMsgTarget){
8378         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8379         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8380         //}else {
8381         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8382        // }
8383
8384     },
8385
8386     // private
8387     afterAction : function(action, success){
8388         this.activeAction = null;
8389         var o = action.options;
8390
8391         if(this.loadMask){
8392             
8393             if(this.maskBody){
8394                 Roo.get(document.body).unmask();
8395             } else {
8396                 this.el.unmask();
8397             }
8398         }
8399         
8400         //if(this.waitMsgTarget === true){
8401 //            this.el.unmask();
8402         //}else if(this.waitMsgTarget){
8403         //    this.waitMsgTarget.unmask();
8404         //}else{
8405         //    Roo.MessageBox.updateProgress(1);
8406         //    Roo.MessageBox.hide();
8407        // }
8408         //
8409         if(success){
8410             if(o.reset){
8411                 this.reset();
8412             }
8413             Roo.callback(o.success, o.scope, [this, action]);
8414             this.fireEvent('actioncomplete', this, action);
8415
8416         }else{
8417
8418             // failure condition..
8419             // we have a scenario where updates need confirming.
8420             // eg. if a locking scenario exists..
8421             // we look for { errors : { needs_confirm : true }} in the response.
8422             if (
8423                 (typeof(action.result) != 'undefined')  &&
8424                 (typeof(action.result.errors) != 'undefined')  &&
8425                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8426            ){
8427                 var _t = this;
8428                 Roo.log("not supported yet");
8429                  /*
8430
8431                 Roo.MessageBox.confirm(
8432                     "Change requires confirmation",
8433                     action.result.errorMsg,
8434                     function(r) {
8435                         if (r != 'yes') {
8436                             return;
8437                         }
8438                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8439                     }
8440
8441                 );
8442                 */
8443
8444
8445                 return;
8446             }
8447
8448             Roo.callback(o.failure, o.scope, [this, action]);
8449             // show an error message if no failed handler is set..
8450             if (!this.hasListener('actionfailed')) {
8451                 Roo.log("need to add dialog support");
8452                 /*
8453                 Roo.MessageBox.alert("Error",
8454                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8455                         action.result.errorMsg :
8456                         "Saving Failed, please check your entries or try again"
8457                 );
8458                 */
8459             }
8460
8461             this.fireEvent('actionfailed', this, action);
8462         }
8463
8464     },
8465     /**
8466      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8467      * @param {String} id The value to search for
8468      * @return Field
8469      */
8470     findField : function(id){
8471         var items = this.getItems();
8472         var field = items.get(id);
8473         if(!field){
8474              items.each(function(f){
8475                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8476                     field = f;
8477                     return false;
8478                 }
8479                 return true;
8480             });
8481         }
8482         return field || null;
8483     },
8484      /**
8485      * Mark fields in this form invalid in bulk.
8486      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8487      * @return {BasicForm} this
8488      */
8489     markInvalid : function(errors){
8490         if(errors instanceof Array){
8491             for(var i = 0, len = errors.length; i < len; i++){
8492                 var fieldError = errors[i];
8493                 var f = this.findField(fieldError.id);
8494                 if(f){
8495                     f.markInvalid(fieldError.msg);
8496                 }
8497             }
8498         }else{
8499             var field, id;
8500             for(id in errors){
8501                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8502                     field.markInvalid(errors[id]);
8503                 }
8504             }
8505         }
8506         //Roo.each(this.childForms || [], function (f) {
8507         //    f.markInvalid(errors);
8508         //});
8509
8510         return this;
8511     },
8512
8513     /**
8514      * Set values for fields in this form in bulk.
8515      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8516      * @return {BasicForm} this
8517      */
8518     setValues : function(values){
8519         if(values instanceof Array){ // array of objects
8520             for(var i = 0, len = values.length; i < len; i++){
8521                 var v = values[i];
8522                 var f = this.findField(v.id);
8523                 if(f){
8524                     f.setValue(v.value);
8525                     if(this.trackResetOnLoad){
8526                         f.originalValue = f.getValue();
8527                     }
8528                 }
8529             }
8530         }else{ // object hash
8531             var field, id;
8532             for(id in values){
8533                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8534
8535                     if (field.setFromData &&
8536                         field.valueField &&
8537                         field.displayField &&
8538                         // combos' with local stores can
8539                         // be queried via setValue()
8540                         // to set their value..
8541                         (field.store && !field.store.isLocal)
8542                         ) {
8543                         // it's a combo
8544                         var sd = { };
8545                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8546                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8547                         field.setFromData(sd);
8548
8549                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8550                         
8551                         field.setFromData(values);
8552                         
8553                     } else {
8554                         field.setValue(values[id]);
8555                     }
8556
8557
8558                     if(this.trackResetOnLoad){
8559                         field.originalValue = field.getValue();
8560                     }
8561                 }
8562             }
8563         }
8564
8565         //Roo.each(this.childForms || [], function (f) {
8566         //    f.setValues(values);
8567         //});
8568
8569         return this;
8570     },
8571
8572     /**
8573      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8574      * they are returned as an array.
8575      * @param {Boolean} asString
8576      * @return {Object}
8577      */
8578     getValues : function(asString){
8579         //if (this.childForms) {
8580             // copy values from the child forms
8581         //    Roo.each(this.childForms, function (f) {
8582         //        this.setValues(f.getValues());
8583         //    }, this);
8584         //}
8585
8586
8587
8588         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8589         if(asString === true){
8590             return fs;
8591         }
8592         return Roo.urlDecode(fs);
8593     },
8594
8595     /**
8596      * Returns the fields in this form as an object with key/value pairs.
8597      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8598      * @return {Object}
8599      */
8600     getFieldValues : function(with_hidden)
8601     {
8602         var items = this.getItems();
8603         var ret = {};
8604         items.each(function(f){
8605             
8606             if (!f.getName()) {
8607                 return;
8608             }
8609             
8610             var v = f.getValue();
8611             
8612             if (f.inputType =='radio') {
8613                 if (typeof(ret[f.getName()]) == 'undefined') {
8614                     ret[f.getName()] = ''; // empty..
8615                 }
8616
8617                 if (!f.el.dom.checked) {
8618                     return;
8619
8620                 }
8621                 v = f.el.dom.value;
8622
8623             }
8624             
8625             if(f.xtype == 'MoneyField'){
8626                 ret[f.currencyName] = f.getCurrency();
8627             }
8628
8629             // not sure if this supported any more..
8630             if ((typeof(v) == 'object') && f.getRawValue) {
8631                 v = f.getRawValue() ; // dates..
8632             }
8633             // combo boxes where name != hiddenName...
8634             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8635                 ret[f.name] = f.getRawValue();
8636             }
8637             ret[f.getName()] = v;
8638         });
8639
8640         return ret;
8641     },
8642
8643     /**
8644      * Clears all invalid messages in this form.
8645      * @return {BasicForm} this
8646      */
8647     clearInvalid : function(){
8648         var items = this.getItems();
8649
8650         items.each(function(f){
8651            f.clearInvalid();
8652         });
8653
8654         return this;
8655     },
8656
8657     /**
8658      * Resets this form.
8659      * @return {BasicForm} this
8660      */
8661     reset : function(){
8662         var items = this.getItems();
8663         items.each(function(f){
8664             f.reset();
8665         });
8666
8667         Roo.each(this.childForms || [], function (f) {
8668             f.reset();
8669         });
8670
8671
8672         return this;
8673     },
8674     
8675     getItems : function()
8676     {
8677         var r=new Roo.util.MixedCollection(false, function(o){
8678             return o.id || (o.id = Roo.id());
8679         });
8680         var iter = function(el) {
8681             if (el.inputEl) {
8682                 r.add(el);
8683             }
8684             if (!el.items) {
8685                 return;
8686             }
8687             Roo.each(el.items,function(e) {
8688                 iter(e);
8689             });
8690         };
8691
8692         iter(this);
8693         return r;
8694     },
8695     
8696     hideFields : function(items)
8697     {
8698         Roo.each(items, function(i){
8699             
8700             var f = this.findField(i);
8701             
8702             if(!f){
8703                 return;
8704             }
8705             
8706             f.hide();
8707             
8708         }, this);
8709     },
8710     
8711     showFields : function(items)
8712     {
8713         Roo.each(items, function(i){
8714             
8715             var f = this.findField(i);
8716             
8717             if(!f){
8718                 return;
8719             }
8720             
8721             f.show();
8722             
8723         }, this);
8724     }
8725
8726 });
8727
8728 Roo.apply(Roo.bootstrap.Form, {
8729     
8730     popover : {
8731         
8732         padding : 5,
8733         
8734         isApplied : false,
8735         
8736         isMasked : false,
8737         
8738         form : false,
8739         
8740         target : false,
8741         
8742         toolTip : false,
8743         
8744         intervalID : false,
8745         
8746         maskEl : false,
8747         
8748         apply : function()
8749         {
8750             if(this.isApplied){
8751                 return;
8752             }
8753             
8754             this.maskEl = {
8755                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8756                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8757                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8758                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8759             };
8760             
8761             this.maskEl.top.enableDisplayMode("block");
8762             this.maskEl.left.enableDisplayMode("block");
8763             this.maskEl.bottom.enableDisplayMode("block");
8764             this.maskEl.right.enableDisplayMode("block");
8765             
8766             this.toolTip = new Roo.bootstrap.Tooltip({
8767                 cls : 'roo-form-error-popover',
8768                 alignment : {
8769                     'left' : ['r-l', [-2,0], 'right'],
8770                     'right' : ['l-r', [2,0], 'left'],
8771                     'bottom' : ['tl-bl', [0,2], 'top'],
8772                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8773                 }
8774             });
8775             
8776             this.toolTip.render(Roo.get(document.body));
8777
8778             this.toolTip.el.enableDisplayMode("block");
8779             
8780             Roo.get(document.body).on('click', function(){
8781                 this.unmask();
8782             }, this);
8783             
8784             Roo.get(document.body).on('touchstart', function(){
8785                 this.unmask();
8786             }, this);
8787             
8788             this.isApplied = true
8789         },
8790         
8791         mask : function(form, target)
8792         {
8793             this.form = form;
8794             
8795             this.target = target;
8796             
8797             if(!this.form.errorMask || !target.el){
8798                 return;
8799             }
8800             
8801             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8802             
8803             Roo.log(scrollable);
8804             
8805             var ot = this.target.el.calcOffsetsTo(scrollable);
8806             
8807             var scrollTo = ot[1] - this.form.maskOffset;
8808             
8809             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8810             
8811             scrollable.scrollTo('top', scrollTo);
8812             
8813             var box = this.target.el.getBox();
8814             Roo.log(box);
8815             var zIndex = Roo.bootstrap.Modal.zIndex++;
8816
8817             
8818             this.maskEl.top.setStyle('position', 'absolute');
8819             this.maskEl.top.setStyle('z-index', zIndex);
8820             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8821             this.maskEl.top.setLeft(0);
8822             this.maskEl.top.setTop(0);
8823             this.maskEl.top.show();
8824             
8825             this.maskEl.left.setStyle('position', 'absolute');
8826             this.maskEl.left.setStyle('z-index', zIndex);
8827             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8828             this.maskEl.left.setLeft(0);
8829             this.maskEl.left.setTop(box.y - this.padding);
8830             this.maskEl.left.show();
8831
8832             this.maskEl.bottom.setStyle('position', 'absolute');
8833             this.maskEl.bottom.setStyle('z-index', zIndex);
8834             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8835             this.maskEl.bottom.setLeft(0);
8836             this.maskEl.bottom.setTop(box.bottom + this.padding);
8837             this.maskEl.bottom.show();
8838
8839             this.maskEl.right.setStyle('position', 'absolute');
8840             this.maskEl.right.setStyle('z-index', zIndex);
8841             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8842             this.maskEl.right.setLeft(box.right + this.padding);
8843             this.maskEl.right.setTop(box.y - this.padding);
8844             this.maskEl.right.show();
8845
8846             this.toolTip.bindEl = this.target.el;
8847
8848             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8849
8850             var tip = this.target.blankText;
8851
8852             if(this.target.getValue() !== '' ) {
8853                 
8854                 if (this.target.invalidText.length) {
8855                     tip = this.target.invalidText;
8856                 } else if (this.target.regexText.length){
8857                     tip = this.target.regexText;
8858                 }
8859             }
8860
8861             this.toolTip.show(tip);
8862
8863             this.intervalID = window.setInterval(function() {
8864                 Roo.bootstrap.Form.popover.unmask();
8865             }, 10000);
8866
8867             window.onwheel = function(){ return false;};
8868             
8869             (function(){ this.isMasked = true; }).defer(500, this);
8870             
8871         },
8872         
8873         unmask : function()
8874         {
8875             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8876                 return;
8877             }
8878             
8879             this.maskEl.top.setStyle('position', 'absolute');
8880             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8881             this.maskEl.top.hide();
8882
8883             this.maskEl.left.setStyle('position', 'absolute');
8884             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8885             this.maskEl.left.hide();
8886
8887             this.maskEl.bottom.setStyle('position', 'absolute');
8888             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8889             this.maskEl.bottom.hide();
8890
8891             this.maskEl.right.setStyle('position', 'absolute');
8892             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8893             this.maskEl.right.hide();
8894             
8895             this.toolTip.hide();
8896             
8897             this.toolTip.el.hide();
8898             
8899             window.onwheel = function(){ return true;};
8900             
8901             if(this.intervalID){
8902                 window.clearInterval(this.intervalID);
8903                 this.intervalID = false;
8904             }
8905             
8906             this.isMasked = false;
8907             
8908         }
8909         
8910     }
8911     
8912 });
8913
8914 /*
8915  * Based on:
8916  * Ext JS Library 1.1.1
8917  * Copyright(c) 2006-2007, Ext JS, LLC.
8918  *
8919  * Originally Released Under LGPL - original licence link has changed is not relivant.
8920  *
8921  * Fork - LGPL
8922  * <script type="text/javascript">
8923  */
8924 /**
8925  * @class Roo.form.VTypes
8926  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8927  * @singleton
8928  */
8929 Roo.form.VTypes = function(){
8930     // closure these in so they are only created once.
8931     var alpha = /^[a-zA-Z_]+$/;
8932     var alphanum = /^[a-zA-Z0-9_]+$/;
8933     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8934     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8935
8936     // All these messages and functions are configurable
8937     return {
8938         /**
8939          * The function used to validate email addresses
8940          * @param {String} value The email address
8941          */
8942         'email' : function(v){
8943             return email.test(v);
8944         },
8945         /**
8946          * The error text to display when the email validation function returns false
8947          * @type String
8948          */
8949         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8950         /**
8951          * The keystroke filter mask to be applied on email input
8952          * @type RegExp
8953          */
8954         'emailMask' : /[a-z0-9_\.\-@]/i,
8955
8956         /**
8957          * The function used to validate URLs
8958          * @param {String} value The URL
8959          */
8960         'url' : function(v){
8961             return url.test(v);
8962         },
8963         /**
8964          * The error text to display when the url validation function returns false
8965          * @type String
8966          */
8967         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8968         
8969         /**
8970          * The function used to validate alpha values
8971          * @param {String} value The value
8972          */
8973         'alpha' : function(v){
8974             return alpha.test(v);
8975         },
8976         /**
8977          * The error text to display when the alpha validation function returns false
8978          * @type String
8979          */
8980         'alphaText' : 'This field should only contain letters and _',
8981         /**
8982          * The keystroke filter mask to be applied on alpha input
8983          * @type RegExp
8984          */
8985         'alphaMask' : /[a-z_]/i,
8986
8987         /**
8988          * The function used to validate alphanumeric values
8989          * @param {String} value The value
8990          */
8991         'alphanum' : function(v){
8992             return alphanum.test(v);
8993         },
8994         /**
8995          * The error text to display when the alphanumeric validation function returns false
8996          * @type String
8997          */
8998         'alphanumText' : 'This field should only contain letters, numbers and _',
8999         /**
9000          * The keystroke filter mask to be applied on alphanumeric input
9001          * @type RegExp
9002          */
9003         'alphanumMask' : /[a-z0-9_]/i
9004     };
9005 }();/*
9006  * - LGPL
9007  *
9008  * Input
9009  * 
9010  */
9011
9012 /**
9013  * @class Roo.bootstrap.Input
9014  * @extends Roo.bootstrap.Component
9015  * Bootstrap Input class
9016  * @cfg {Boolean} disabled is it disabled
9017  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9018  * @cfg {String} name name of the input
9019  * @cfg {string} fieldLabel - the label associated
9020  * @cfg {string} placeholder - placeholder to put in text.
9021  * @cfg {string}  before - input group add on before
9022  * @cfg {string} after - input group add on after
9023  * @cfg {string} size - (lg|sm) or leave empty..
9024  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9025  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9026  * @cfg {Number} md colspan out of 12 for computer-sized screens
9027  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9028  * @cfg {string} value default value of the input
9029  * @cfg {Number} labelWidth set the width of label 
9030  * @cfg {Number} labellg set the width of label (1-12)
9031  * @cfg {Number} labelmd set the width of label (1-12)
9032  * @cfg {Number} labelsm set the width of label (1-12)
9033  * @cfg {Number} labelxs set the width of label (1-12)
9034  * @cfg {String} labelAlign (top|left)
9035  * @cfg {Boolean} readOnly Specifies that the field should be read-only
9036  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9037  * @cfg {String} indicatorpos (left|right) default left
9038  * @cfg {String} capture (user|camera) use for file input only. (default empty)
9039  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9040
9041  * @cfg {String} align (left|center|right) Default left
9042  * @cfg {Boolean} forceFeedback (true|false) Default false
9043  * 
9044  * @constructor
9045  * Create a new Input
9046  * @param {Object} config The config object
9047  */
9048
9049 Roo.bootstrap.Input = function(config){
9050     
9051     Roo.bootstrap.Input.superclass.constructor.call(this, config);
9052     
9053     this.addEvents({
9054         /**
9055          * @event focus
9056          * Fires when this field receives input focus.
9057          * @param {Roo.form.Field} this
9058          */
9059         focus : true,
9060         /**
9061          * @event blur
9062          * Fires when this field loses input focus.
9063          * @param {Roo.form.Field} this
9064          */
9065         blur : true,
9066         /**
9067          * @event specialkey
9068          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9069          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9070          * @param {Roo.form.Field} this
9071          * @param {Roo.EventObject} e The event object
9072          */
9073         specialkey : true,
9074         /**
9075          * @event change
9076          * Fires just before the field blurs if the field value has changed.
9077          * @param {Roo.form.Field} this
9078          * @param {Mixed} newValue The new value
9079          * @param {Mixed} oldValue The original value
9080          */
9081         change : true,
9082         /**
9083          * @event invalid
9084          * Fires after the field has been marked as invalid.
9085          * @param {Roo.form.Field} this
9086          * @param {String} msg The validation message
9087          */
9088         invalid : true,
9089         /**
9090          * @event valid
9091          * Fires after the field has been validated with no errors.
9092          * @param {Roo.form.Field} this
9093          */
9094         valid : true,
9095          /**
9096          * @event keyup
9097          * Fires after the key up
9098          * @param {Roo.form.Field} this
9099          * @param {Roo.EventObject}  e The event Object
9100          */
9101         keyup : true
9102     });
9103 };
9104
9105 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9106      /**
9107      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9108       automatic validation (defaults to "keyup").
9109      */
9110     validationEvent : "keyup",
9111      /**
9112      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9113      */
9114     validateOnBlur : true,
9115     /**
9116      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9117      */
9118     validationDelay : 250,
9119      /**
9120      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9121      */
9122     focusClass : "x-form-focus",  // not needed???
9123     
9124        
9125     /**
9126      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9127      */
9128     invalidClass : "has-warning",
9129     
9130     /**
9131      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9132      */
9133     validClass : "has-success",
9134     
9135     /**
9136      * @cfg {Boolean} hasFeedback (true|false) default true
9137      */
9138     hasFeedback : true,
9139     
9140     /**
9141      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9142      */
9143     invalidFeedbackClass : "glyphicon-warning-sign",
9144     
9145     /**
9146      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9147      */
9148     validFeedbackClass : "glyphicon-ok",
9149     
9150     /**
9151      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9152      */
9153     selectOnFocus : false,
9154     
9155      /**
9156      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9157      */
9158     maskRe : null,
9159        /**
9160      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9161      */
9162     vtype : null,
9163     
9164       /**
9165      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9166      */
9167     disableKeyFilter : false,
9168     
9169        /**
9170      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9171      */
9172     disabled : false,
9173      /**
9174      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9175      */
9176     allowBlank : true,
9177     /**
9178      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9179      */
9180     blankText : "Please complete this mandatory field",
9181     
9182      /**
9183      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9184      */
9185     minLength : 0,
9186     /**
9187      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9188      */
9189     maxLength : Number.MAX_VALUE,
9190     /**
9191      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9192      */
9193     minLengthText : "The minimum length for this field is {0}",
9194     /**
9195      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9196      */
9197     maxLengthText : "The maximum length for this field is {0}",
9198   
9199     
9200     /**
9201      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9202      * If available, this function will be called only after the basic validators all return true, and will be passed the
9203      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9204      */
9205     validator : null,
9206     /**
9207      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9208      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9209      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9210      */
9211     regex : null,
9212     /**
9213      * @cfg {String} regexText -- Depricated - use Invalid Text
9214      */
9215     regexText : "",
9216     
9217     /**
9218      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9219      */
9220     invalidText : "",
9221     
9222     
9223     
9224     autocomplete: false,
9225     
9226     
9227     fieldLabel : '',
9228     inputType : 'text',
9229     
9230     name : false,
9231     placeholder: false,
9232     before : false,
9233     after : false,
9234     size : false,
9235     hasFocus : false,
9236     preventMark: false,
9237     isFormField : true,
9238     value : '',
9239     labelWidth : 2,
9240     labelAlign : false,
9241     readOnly : false,
9242     align : false,
9243     formatedValue : false,
9244     forceFeedback : false,
9245     
9246     indicatorpos : 'left',
9247     
9248     labellg : 0,
9249     labelmd : 0,
9250     labelsm : 0,
9251     labelxs : 0,
9252     
9253     capture : '',
9254     accept : '',
9255     
9256     parentLabelAlign : function()
9257     {
9258         var parent = this;
9259         while (parent.parent()) {
9260             parent = parent.parent();
9261             if (typeof(parent.labelAlign) !='undefined') {
9262                 return parent.labelAlign;
9263             }
9264         }
9265         return 'left';
9266         
9267     },
9268     
9269     getAutoCreate : function()
9270     {
9271         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9272         
9273         var id = Roo.id();
9274         
9275         var cfg = {};
9276         
9277         if(this.inputType != 'hidden'){
9278             cfg.cls = 'form-group' //input-group
9279         }
9280         
9281         var input =  {
9282             tag: 'input',
9283             id : id,
9284             type : this.inputType,
9285             value : this.value,
9286             cls : 'form-control',
9287             placeholder : this.placeholder || '',
9288             autocomplete : this.autocomplete || 'new-password'
9289         };
9290         
9291         if(this.capture.length){
9292             input.capture = this.capture;
9293         }
9294         
9295         if(this.accept.length){
9296             input.accept = this.accept + "/*";
9297         }
9298         
9299         if(this.align){
9300             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9301         }
9302         
9303         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9304             input.maxLength = this.maxLength;
9305         }
9306         
9307         if (this.disabled) {
9308             input.disabled=true;
9309         }
9310         
9311         if (this.readOnly) {
9312             input.readonly=true;
9313         }
9314         
9315         if (this.name) {
9316             input.name = this.name;
9317         }
9318         
9319         if (this.size) {
9320             input.cls += ' input-' + this.size;
9321         }
9322         
9323         var settings=this;
9324         ['xs','sm','md','lg'].map(function(size){
9325             if (settings[size]) {
9326                 cfg.cls += ' col-' + size + '-' + settings[size];
9327             }
9328         });
9329         
9330         var inputblock = input;
9331         
9332         var feedback = {
9333             tag: 'span',
9334             cls: 'glyphicon form-control-feedback'
9335         };
9336             
9337         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9338             
9339             inputblock = {
9340                 cls : 'has-feedback',
9341                 cn :  [
9342                     input,
9343                     feedback
9344                 ] 
9345             };  
9346         }
9347         
9348         if (this.before || this.after) {
9349             
9350             inputblock = {
9351                 cls : 'input-group',
9352                 cn :  [] 
9353             };
9354             
9355             if (this.before && typeof(this.before) == 'string') {
9356                 
9357                 inputblock.cn.push({
9358                     tag :'span',
9359                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9360                     html : this.before
9361                 });
9362             }
9363             if (this.before && typeof(this.before) == 'object') {
9364                 this.before = Roo.factory(this.before);
9365                 
9366                 inputblock.cn.push({
9367                     tag :'span',
9368                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9369                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9370                 });
9371             }
9372             
9373             inputblock.cn.push(input);
9374             
9375             if (this.after && typeof(this.after) == 'string') {
9376                 inputblock.cn.push({
9377                     tag :'span',
9378                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9379                     html : this.after
9380                 });
9381             }
9382             if (this.after && typeof(this.after) == 'object') {
9383                 this.after = Roo.factory(this.after);
9384                 
9385                 inputblock.cn.push({
9386                     tag :'span',
9387                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9388                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9389                 });
9390             }
9391             
9392             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9393                 inputblock.cls += ' has-feedback';
9394                 inputblock.cn.push(feedback);
9395             }
9396         };
9397         var indicator = {
9398             tag : 'i',
9399             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9400             tooltip : 'This field is required'
9401         };
9402         if (Roo.bootstrap.version == 4) {
9403             indicator = {
9404                 tag : 'i',
9405                 style : 'display-none'
9406             };
9407         }
9408         if (align ==='left' && this.fieldLabel.length) {
9409             
9410             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9411             
9412             cfg.cn = [
9413                 indicator,
9414                 {
9415                     tag: 'label',
9416                     'for' :  id,
9417                     cls : 'control-label col-form-label',
9418                     html : this.fieldLabel
9419
9420                 },
9421                 {
9422                     cls : "", 
9423                     cn: [
9424                         inputblock
9425                     ]
9426                 }
9427             ];
9428             
9429             var labelCfg = cfg.cn[1];
9430             var contentCfg = cfg.cn[2];
9431             
9432             if(this.indicatorpos == 'right'){
9433                 cfg.cn = [
9434                     {
9435                         tag: 'label',
9436                         'for' :  id,
9437                         cls : 'control-label col-form-label',
9438                         cn : [
9439                             {
9440                                 tag : 'span',
9441                                 html : this.fieldLabel
9442                             },
9443                             indicator
9444                         ]
9445                     },
9446                     {
9447                         cls : "",
9448                         cn: [
9449                             inputblock
9450                         ]
9451                     }
9452
9453                 ];
9454                 
9455                 labelCfg = cfg.cn[0];
9456                 contentCfg = cfg.cn[1];
9457             
9458             }
9459             
9460             if(this.labelWidth > 12){
9461                 labelCfg.style = "width: " + this.labelWidth + 'px';
9462             }
9463             
9464             if(this.labelWidth < 13 && this.labelmd == 0){
9465                 this.labelmd = this.labelWidth;
9466             }
9467             
9468             if(this.labellg > 0){
9469                 labelCfg.cls += ' col-lg-' + this.labellg;
9470                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9471             }
9472             
9473             if(this.labelmd > 0){
9474                 labelCfg.cls += ' col-md-' + this.labelmd;
9475                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9476             }
9477             
9478             if(this.labelsm > 0){
9479                 labelCfg.cls += ' col-sm-' + this.labelsm;
9480                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9481             }
9482             
9483             if(this.labelxs > 0){
9484                 labelCfg.cls += ' col-xs-' + this.labelxs;
9485                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9486             }
9487             
9488             
9489         } else if ( this.fieldLabel.length) {
9490                 
9491             cfg.cn = [
9492                 {
9493                     tag : 'i',
9494                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9495                     tooltip : 'This field is required'
9496                 },
9497                 {
9498                     tag: 'label',
9499                    //cls : 'input-group-addon',
9500                     html : this.fieldLabel
9501
9502                 },
9503
9504                inputblock
9505
9506            ];
9507            
9508            if(this.indicatorpos == 'right'){
9509                 
9510                 cfg.cn = [
9511                     {
9512                         tag: 'label',
9513                        //cls : 'input-group-addon',
9514                         html : this.fieldLabel
9515
9516                     },
9517                     {
9518                         tag : 'i',
9519                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9520                         tooltip : 'This field is required'
9521                     },
9522
9523                    inputblock
9524
9525                ];
9526
9527             }
9528
9529         } else {
9530             
9531             cfg.cn = [
9532
9533                     inputblock
9534
9535             ];
9536                 
9537                 
9538         };
9539         
9540         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9541            cfg.cls += ' navbar-form';
9542         }
9543         
9544         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9545             // on BS4 we do this only if not form 
9546             cfg.cls += ' navbar-form';
9547             cfg.tag = 'li';
9548         }
9549         
9550         return cfg;
9551         
9552     },
9553     /**
9554      * return the real input element.
9555      */
9556     inputEl: function ()
9557     {
9558         return this.el.select('input.form-control',true).first();
9559     },
9560     
9561     tooltipEl : function()
9562     {
9563         return this.inputEl();
9564     },
9565     
9566     indicatorEl : function()
9567     {
9568         if (Roo.bootstrap.version == 4) {
9569             return false; // not enabled in v4 yet.
9570         }
9571         
9572         var indicator = this.el.select('i.roo-required-indicator',true).first();
9573         
9574         if(!indicator){
9575             return false;
9576         }
9577         
9578         return indicator;
9579         
9580     },
9581     
9582     setDisabled : function(v)
9583     {
9584         var i  = this.inputEl().dom;
9585         if (!v) {
9586             i.removeAttribute('disabled');
9587             return;
9588             
9589         }
9590         i.setAttribute('disabled','true');
9591     },
9592     initEvents : function()
9593     {
9594           
9595         this.inputEl().on("keydown" , this.fireKey,  this);
9596         this.inputEl().on("focus", this.onFocus,  this);
9597         this.inputEl().on("blur", this.onBlur,  this);
9598         
9599         this.inputEl().relayEvent('keyup', this);
9600         
9601         this.indicator = this.indicatorEl();
9602         
9603         if(this.indicator){
9604             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9605         }
9606  
9607         // reference to original value for reset
9608         this.originalValue = this.getValue();
9609         //Roo.form.TextField.superclass.initEvents.call(this);
9610         if(this.validationEvent == 'keyup'){
9611             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9612             this.inputEl().on('keyup', this.filterValidation, this);
9613         }
9614         else if(this.validationEvent !== false){
9615             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9616         }
9617         
9618         if(this.selectOnFocus){
9619             this.on("focus", this.preFocus, this);
9620             
9621         }
9622         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9623             this.inputEl().on("keypress", this.filterKeys, this);
9624         } else {
9625             this.inputEl().relayEvent('keypress', this);
9626         }
9627        /* if(this.grow){
9628             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9629             this.el.on("click", this.autoSize,  this);
9630         }
9631         */
9632         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9633             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9634         }
9635         
9636         if (typeof(this.before) == 'object') {
9637             this.before.render(this.el.select('.roo-input-before',true).first());
9638         }
9639         if (typeof(this.after) == 'object') {
9640             this.after.render(this.el.select('.roo-input-after',true).first());
9641         }
9642         
9643         this.inputEl().on('change', this.onChange, this);
9644         
9645     },
9646     filterValidation : function(e){
9647         if(!e.isNavKeyPress()){
9648             this.validationTask.delay(this.validationDelay);
9649         }
9650     },
9651      /**
9652      * Validates the field value
9653      * @return {Boolean} True if the value is valid, else false
9654      */
9655     validate : function(){
9656         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9657         if(this.disabled || this.validateValue(this.getRawValue())){
9658             this.markValid();
9659             return true;
9660         }
9661         
9662         this.markInvalid();
9663         return false;
9664     },
9665     
9666     
9667     /**
9668      * Validates a value according to the field's validation rules and marks the field as invalid
9669      * if the validation fails
9670      * @param {Mixed} value The value to validate
9671      * @return {Boolean} True if the value is valid, else false
9672      */
9673     validateValue : function(value)
9674     {
9675         if(this.getVisibilityEl().hasClass('hidden')){
9676             return true;
9677         }
9678         
9679         if(value.length < 1)  { // if it's blank
9680             if(this.allowBlank){
9681                 return true;
9682             }
9683             return false;
9684         }
9685         
9686         if(value.length < this.minLength){
9687             return false;
9688         }
9689         if(value.length > this.maxLength){
9690             return false;
9691         }
9692         if(this.vtype){
9693             var vt = Roo.form.VTypes;
9694             if(!vt[this.vtype](value, this)){
9695                 return false;
9696             }
9697         }
9698         if(typeof this.validator == "function"){
9699             var msg = this.validator(value);
9700             if(msg !== true){
9701                 return false;
9702             }
9703             if (typeof(msg) == 'string') {
9704                 this.invalidText = msg;
9705             }
9706         }
9707         
9708         if(this.regex && !this.regex.test(value)){
9709             return false;
9710         }
9711         
9712         return true;
9713     },
9714     
9715      // private
9716     fireKey : function(e){
9717         //Roo.log('field ' + e.getKey());
9718         if(e.isNavKeyPress()){
9719             this.fireEvent("specialkey", this, e);
9720         }
9721     },
9722     focus : function (selectText){
9723         if(this.rendered){
9724             this.inputEl().focus();
9725             if(selectText === true){
9726                 this.inputEl().dom.select();
9727             }
9728         }
9729         return this;
9730     } ,
9731     
9732     onFocus : function(){
9733         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9734            // this.el.addClass(this.focusClass);
9735         }
9736         if(!this.hasFocus){
9737             this.hasFocus = true;
9738             this.startValue = this.getValue();
9739             this.fireEvent("focus", this);
9740         }
9741     },
9742     
9743     beforeBlur : Roo.emptyFn,
9744
9745     
9746     // private
9747     onBlur : function(){
9748         this.beforeBlur();
9749         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9750             //this.el.removeClass(this.focusClass);
9751         }
9752         this.hasFocus = false;
9753         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9754             this.validate();
9755         }
9756         var v = this.getValue();
9757         if(String(v) !== String(this.startValue)){
9758             this.fireEvent('change', this, v, this.startValue);
9759         }
9760         this.fireEvent("blur", this);
9761     },
9762     
9763     onChange : function(e)
9764     {
9765         var v = this.getValue();
9766         if(String(v) !== String(this.startValue)){
9767             this.fireEvent('change', this, v, this.startValue);
9768         }
9769         
9770     },
9771     
9772     /**
9773      * Resets the current field value to the originally loaded value and clears any validation messages
9774      */
9775     reset : function(){
9776         this.setValue(this.originalValue);
9777         this.validate();
9778     },
9779      /**
9780      * Returns the name of the field
9781      * @return {Mixed} name The name field
9782      */
9783     getName: function(){
9784         return this.name;
9785     },
9786      /**
9787      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9788      * @return {Mixed} value The field value
9789      */
9790     getValue : function(){
9791         
9792         var v = this.inputEl().getValue();
9793         
9794         return v;
9795     },
9796     /**
9797      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9798      * @return {Mixed} value The field value
9799      */
9800     getRawValue : function(){
9801         var v = this.inputEl().getValue();
9802         
9803         return v;
9804     },
9805     
9806     /**
9807      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9808      * @param {Mixed} value The value to set
9809      */
9810     setRawValue : function(v){
9811         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9812     },
9813     
9814     selectText : function(start, end){
9815         var v = this.getRawValue();
9816         if(v.length > 0){
9817             start = start === undefined ? 0 : start;
9818             end = end === undefined ? v.length : end;
9819             var d = this.inputEl().dom;
9820             if(d.setSelectionRange){
9821                 d.setSelectionRange(start, end);
9822             }else if(d.createTextRange){
9823                 var range = d.createTextRange();
9824                 range.moveStart("character", start);
9825                 range.moveEnd("character", v.length-end);
9826                 range.select();
9827             }
9828         }
9829     },
9830     
9831     /**
9832      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9833      * @param {Mixed} value The value to set
9834      */
9835     setValue : function(v){
9836         this.value = v;
9837         if(this.rendered){
9838             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9839             this.validate();
9840         }
9841     },
9842     
9843     /*
9844     processValue : function(value){
9845         if(this.stripCharsRe){
9846             var newValue = value.replace(this.stripCharsRe, '');
9847             if(newValue !== value){
9848                 this.setRawValue(newValue);
9849                 return newValue;
9850             }
9851         }
9852         return value;
9853     },
9854   */
9855     preFocus : function(){
9856         
9857         if(this.selectOnFocus){
9858             this.inputEl().dom.select();
9859         }
9860     },
9861     filterKeys : function(e){
9862         var k = e.getKey();
9863         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9864             return;
9865         }
9866         var c = e.getCharCode(), cc = String.fromCharCode(c);
9867         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9868             return;
9869         }
9870         if(!this.maskRe.test(cc)){
9871             e.stopEvent();
9872         }
9873     },
9874      /**
9875      * Clear any invalid styles/messages for this field
9876      */
9877     clearInvalid : function(){
9878         
9879         if(!this.el || this.preventMark){ // not rendered
9880             return;
9881         }
9882         
9883         
9884         this.el.removeClass([this.invalidClass, 'is-invalid']);
9885         
9886         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9887             
9888             var feedback = this.el.select('.form-control-feedback', true).first();
9889             
9890             if(feedback){
9891                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9892             }
9893             
9894         }
9895         
9896         if(this.indicator){
9897             this.indicator.removeClass('visible');
9898             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9899         }
9900         
9901         this.fireEvent('valid', this);
9902     },
9903     
9904      /**
9905      * Mark this field as valid
9906      */
9907     markValid : function()
9908     {
9909         if(!this.el  || this.preventMark){ // not rendered...
9910             return;
9911         }
9912         
9913         this.el.removeClass([this.invalidClass, this.validClass]);
9914         this.inputEl().removeClass(['is-valid', 'is-invalid']);
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         }
9921         
9922         if(this.indicator){
9923             this.indicator.removeClass('visible');
9924             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9925         }
9926         
9927         if(this.disabled){
9928             return;
9929         }
9930         
9931         if(this.allowBlank && !this.getRawValue().length){
9932             return;
9933         }
9934         if (Roo.bootstrap.version == 3) {
9935             this.el.addClass(this.validClass);
9936         } else {
9937             this.inputEl().addClass('is-valid');
9938         }
9939
9940         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9941             
9942             var feedback = this.el.select('.form-control-feedback', true).first();
9943             
9944             if(feedback){
9945                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9946                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9947             }
9948             
9949         }
9950         
9951         this.fireEvent('valid', this);
9952     },
9953     
9954      /**
9955      * Mark this field as invalid
9956      * @param {String} msg The validation message
9957      */
9958     markInvalid : function(msg)
9959     {
9960         if(!this.el  || this.preventMark){ // not rendered
9961             return;
9962         }
9963         
9964         this.el.removeClass([this.invalidClass, this.validClass]);
9965         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9966         
9967         var feedback = this.el.select('.form-control-feedback', true).first();
9968             
9969         if(feedback){
9970             this.el.select('.form-control-feedback', true).first().removeClass(
9971                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9972         }
9973
9974         if(this.disabled){
9975             return;
9976         }
9977         
9978         if(this.allowBlank && !this.getRawValue().length){
9979             return;
9980         }
9981         
9982         if(this.indicator){
9983             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9984             this.indicator.addClass('visible');
9985         }
9986         if (Roo.bootstrap.version == 3) {
9987             this.el.addClass(this.invalidClass);
9988         } else {
9989             this.inputEl().addClass('is-invalid');
9990         }
9991         
9992         
9993         
9994         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9995             
9996             var feedback = this.el.select('.form-control-feedback', true).first();
9997             
9998             if(feedback){
9999                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10000                 
10001                 if(this.getValue().length || this.forceFeedback){
10002                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10003                 }
10004                 
10005             }
10006             
10007         }
10008         
10009         this.fireEvent('invalid', this, msg);
10010     },
10011     // private
10012     SafariOnKeyDown : function(event)
10013     {
10014         // this is a workaround for a password hang bug on chrome/ webkit.
10015         if (this.inputEl().dom.type != 'password') {
10016             return;
10017         }
10018         
10019         var isSelectAll = false;
10020         
10021         if(this.inputEl().dom.selectionEnd > 0){
10022             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10023         }
10024         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10025             event.preventDefault();
10026             this.setValue('');
10027             return;
10028         }
10029         
10030         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10031             
10032             event.preventDefault();
10033             // this is very hacky as keydown always get's upper case.
10034             //
10035             var cc = String.fromCharCode(event.getCharCode());
10036             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
10037             
10038         }
10039     },
10040     adjustWidth : function(tag, w){
10041         tag = tag.toLowerCase();
10042         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10043             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10044                 if(tag == 'input'){
10045                     return w + 2;
10046                 }
10047                 if(tag == 'textarea'){
10048                     return w-2;
10049                 }
10050             }else if(Roo.isOpera){
10051                 if(tag == 'input'){
10052                     return w + 2;
10053                 }
10054                 if(tag == 'textarea'){
10055                     return w-2;
10056                 }
10057             }
10058         }
10059         return w;
10060     },
10061     
10062     setFieldLabel : function(v)
10063     {
10064         if(!this.rendered){
10065             return;
10066         }
10067         
10068         if(this.indicatorEl()){
10069             var ar = this.el.select('label > span',true);
10070             
10071             if (ar.elements.length) {
10072                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10073                 this.fieldLabel = v;
10074                 return;
10075             }
10076             
10077             var br = this.el.select('label',true);
10078             
10079             if(br.elements.length) {
10080                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10081                 this.fieldLabel = v;
10082                 return;
10083             }
10084             
10085             Roo.log('Cannot Found any of label > span || label in input');
10086             return;
10087         }
10088         
10089         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10090         this.fieldLabel = v;
10091         
10092         
10093     }
10094 });
10095
10096  
10097 /*
10098  * - LGPL
10099  *
10100  * Input
10101  * 
10102  */
10103
10104 /**
10105  * @class Roo.bootstrap.TextArea
10106  * @extends Roo.bootstrap.Input
10107  * Bootstrap TextArea class
10108  * @cfg {Number} cols Specifies the visible width of a text area
10109  * @cfg {Number} rows Specifies the visible number of lines in a text area
10110  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10111  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10112  * @cfg {string} html text
10113  * 
10114  * @constructor
10115  * Create a new TextArea
10116  * @param {Object} config The config object
10117  */
10118
10119 Roo.bootstrap.TextArea = function(config){
10120     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10121    
10122 };
10123
10124 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10125      
10126     cols : false,
10127     rows : 5,
10128     readOnly : false,
10129     warp : 'soft',
10130     resize : false,
10131     value: false,
10132     html: false,
10133     
10134     getAutoCreate : function(){
10135         
10136         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10137         
10138         var id = Roo.id();
10139         
10140         var cfg = {};
10141         
10142         if(this.inputType != 'hidden'){
10143             cfg.cls = 'form-group' //input-group
10144         }
10145         
10146         var input =  {
10147             tag: 'textarea',
10148             id : id,
10149             warp : this.warp,
10150             rows : this.rows,
10151             value : this.value || '',
10152             html: this.html || '',
10153             cls : 'form-control',
10154             placeholder : this.placeholder || '' 
10155             
10156         };
10157         
10158         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10159             input.maxLength = this.maxLength;
10160         }
10161         
10162         if(this.resize){
10163             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10164         }
10165         
10166         if(this.cols){
10167             input.cols = this.cols;
10168         }
10169         
10170         if (this.readOnly) {
10171             input.readonly = true;
10172         }
10173         
10174         if (this.name) {
10175             input.name = this.name;
10176         }
10177         
10178         if (this.size) {
10179             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10180         }
10181         
10182         var settings=this;
10183         ['xs','sm','md','lg'].map(function(size){
10184             if (settings[size]) {
10185                 cfg.cls += ' col-' + size + '-' + settings[size];
10186             }
10187         });
10188         
10189         var inputblock = input;
10190         
10191         if(this.hasFeedback && !this.allowBlank){
10192             
10193             var feedback = {
10194                 tag: 'span',
10195                 cls: 'glyphicon form-control-feedback'
10196             };
10197
10198             inputblock = {
10199                 cls : 'has-feedback',
10200                 cn :  [
10201                     input,
10202                     feedback
10203                 ] 
10204             };  
10205         }
10206         
10207         
10208         if (this.before || this.after) {
10209             
10210             inputblock = {
10211                 cls : 'input-group',
10212                 cn :  [] 
10213             };
10214             if (this.before) {
10215                 inputblock.cn.push({
10216                     tag :'span',
10217                     cls : 'input-group-addon',
10218                     html : this.before
10219                 });
10220             }
10221             
10222             inputblock.cn.push(input);
10223             
10224             if(this.hasFeedback && !this.allowBlank){
10225                 inputblock.cls += ' has-feedback';
10226                 inputblock.cn.push(feedback);
10227             }
10228             
10229             if (this.after) {
10230                 inputblock.cn.push({
10231                     tag :'span',
10232                     cls : 'input-group-addon',
10233                     html : this.after
10234                 });
10235             }
10236             
10237         }
10238         
10239         if (align ==='left' && this.fieldLabel.length) {
10240             cfg.cn = [
10241                 {
10242                     tag: 'label',
10243                     'for' :  id,
10244                     cls : 'control-label',
10245                     html : this.fieldLabel
10246                 },
10247                 {
10248                     cls : "",
10249                     cn: [
10250                         inputblock
10251                     ]
10252                 }
10253
10254             ];
10255             
10256             if(this.labelWidth > 12){
10257                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10258             }
10259
10260             if(this.labelWidth < 13 && this.labelmd == 0){
10261                 this.labelmd = this.labelWidth;
10262             }
10263
10264             if(this.labellg > 0){
10265                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10266                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10267             }
10268
10269             if(this.labelmd > 0){
10270                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10271                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10272             }
10273
10274             if(this.labelsm > 0){
10275                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10276                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10277             }
10278
10279             if(this.labelxs > 0){
10280                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10281                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10282             }
10283             
10284         } else if ( this.fieldLabel.length) {
10285             cfg.cn = [
10286
10287                {
10288                    tag: 'label',
10289                    //cls : 'input-group-addon',
10290                    html : this.fieldLabel
10291
10292                },
10293
10294                inputblock
10295
10296            ];
10297
10298         } else {
10299
10300             cfg.cn = [
10301
10302                 inputblock
10303
10304             ];
10305                 
10306         }
10307         
10308         if (this.disabled) {
10309             input.disabled=true;
10310         }
10311         
10312         return cfg;
10313         
10314     },
10315     /**
10316      * return the real textarea element.
10317      */
10318     inputEl: function ()
10319     {
10320         return this.el.select('textarea.form-control',true).first();
10321     },
10322     
10323     /**
10324      * Clear any invalid styles/messages for this field
10325      */
10326     clearInvalid : function()
10327     {
10328         
10329         if(!this.el || this.preventMark){ // not rendered
10330             return;
10331         }
10332         
10333         var label = this.el.select('label', true).first();
10334         var icon = this.el.select('i.fa-star', true).first();
10335         
10336         if(label && icon){
10337             icon.remove();
10338         }
10339         this.el.removeClass( this.validClass);
10340         this.inputEl().removeClass('is-invalid');
10341          
10342         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10343             
10344             var feedback = this.el.select('.form-control-feedback', true).first();
10345             
10346             if(feedback){
10347                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10348             }
10349             
10350         }
10351         
10352         this.fireEvent('valid', this);
10353     },
10354     
10355      /**
10356      * Mark this field as valid
10357      */
10358     markValid : function()
10359     {
10360         if(!this.el  || this.preventMark){ // not rendered
10361             return;
10362         }
10363         
10364         this.el.removeClass([this.invalidClass, this.validClass]);
10365         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10366         
10367         var feedback = this.el.select('.form-control-feedback', true).first();
10368             
10369         if(feedback){
10370             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10371         }
10372
10373         if(this.disabled || this.allowBlank){
10374             return;
10375         }
10376         
10377         var label = this.el.select('label', true).first();
10378         var icon = this.el.select('i.fa-star', true).first();
10379         
10380         if(label && icon){
10381             icon.remove();
10382         }
10383         if (Roo.bootstrap.version == 3) {
10384             this.el.addClass(this.validClass);
10385         } else {
10386             this.inputEl().addClass('is-valid');
10387         }
10388         
10389         
10390         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10391             
10392             var feedback = this.el.select('.form-control-feedback', true).first();
10393             
10394             if(feedback){
10395                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10396                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10397             }
10398             
10399         }
10400         
10401         this.fireEvent('valid', this);
10402     },
10403     
10404      /**
10405      * Mark this field as invalid
10406      * @param {String} msg The validation message
10407      */
10408     markInvalid : function(msg)
10409     {
10410         if(!this.el  || this.preventMark){ // not rendered
10411             return;
10412         }
10413         
10414         this.el.removeClass([this.invalidClass, this.validClass]);
10415         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10416         
10417         var feedback = this.el.select('.form-control-feedback', true).first();
10418             
10419         if(feedback){
10420             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10421         }
10422
10423         if(this.disabled || this.allowBlank){
10424             return;
10425         }
10426         
10427         var label = this.el.select('label', true).first();
10428         var icon = this.el.select('i.fa-star', true).first();
10429         
10430         if(!this.getValue().length && label && !icon){
10431             this.el.createChild({
10432                 tag : 'i',
10433                 cls : 'text-danger fa fa-lg fa-star',
10434                 tooltip : 'This field is required',
10435                 style : 'margin-right:5px;'
10436             }, label, true);
10437         }
10438         
10439         if (Roo.bootstrap.version == 3) {
10440             this.el.addClass(this.invalidClass);
10441         } else {
10442             this.inputEl().addClass('is-invalid');
10443         }
10444         
10445         // fixme ... this may be depricated need to test..
10446         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10447             
10448             var feedback = this.el.select('.form-control-feedback', true).first();
10449             
10450             if(feedback){
10451                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10452                 
10453                 if(this.getValue().length || this.forceFeedback){
10454                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10455                 }
10456                 
10457             }
10458             
10459         }
10460         
10461         this.fireEvent('invalid', this, msg);
10462     }
10463 });
10464
10465  
10466 /*
10467  * - LGPL
10468  *
10469  * trigger field - base class for combo..
10470  * 
10471  */
10472  
10473 /**
10474  * @class Roo.bootstrap.TriggerField
10475  * @extends Roo.bootstrap.Input
10476  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10477  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10478  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10479  * for which you can provide a custom implementation.  For example:
10480  * <pre><code>
10481 var trigger = new Roo.bootstrap.TriggerField();
10482 trigger.onTriggerClick = myTriggerFn;
10483 trigger.applyTo('my-field');
10484 </code></pre>
10485  *
10486  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10487  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10488  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10489  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10490  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10491
10492  * @constructor
10493  * Create a new TriggerField.
10494  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10495  * to the base TextField)
10496  */
10497 Roo.bootstrap.TriggerField = function(config){
10498     this.mimicing = false;
10499     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10500 };
10501
10502 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10503     /**
10504      * @cfg {String} triggerClass A CSS class to apply to the trigger
10505      */
10506      /**
10507      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10508      */
10509     hideTrigger:false,
10510
10511     /**
10512      * @cfg {Boolean} removable (true|false) special filter default false
10513      */
10514     removable : false,
10515     
10516     /** @cfg {Boolean} grow @hide */
10517     /** @cfg {Number} growMin @hide */
10518     /** @cfg {Number} growMax @hide */
10519
10520     /**
10521      * @hide 
10522      * @method
10523      */
10524     autoSize: Roo.emptyFn,
10525     // private
10526     monitorTab : true,
10527     // private
10528     deferHeight : true,
10529
10530     
10531     actionMode : 'wrap',
10532     
10533     caret : false,
10534     
10535     
10536     getAutoCreate : function(){
10537        
10538         var align = this.labelAlign || this.parentLabelAlign();
10539         
10540         var id = Roo.id();
10541         
10542         var cfg = {
10543             cls: 'form-group' //input-group
10544         };
10545         
10546         
10547         var input =  {
10548             tag: 'input',
10549             id : id,
10550             type : this.inputType,
10551             cls : 'form-control',
10552             autocomplete: 'new-password',
10553             placeholder : this.placeholder || '' 
10554             
10555         };
10556         if (this.name) {
10557             input.name = this.name;
10558         }
10559         if (this.size) {
10560             input.cls += ' input-' + this.size;
10561         }
10562         
10563         if (this.disabled) {
10564             input.disabled=true;
10565         }
10566         
10567         var inputblock = input;
10568         
10569         if(this.hasFeedback && !this.allowBlank){
10570             
10571             var feedback = {
10572                 tag: 'span',
10573                 cls: 'glyphicon form-control-feedback'
10574             };
10575             
10576             if(this.removable && !this.editable && !this.tickable){
10577                 inputblock = {
10578                     cls : 'has-feedback',
10579                     cn :  [
10580                         inputblock,
10581                         {
10582                             tag: 'button',
10583                             html : 'x',
10584                             cls : 'roo-combo-removable-btn close'
10585                         },
10586                         feedback
10587                     ] 
10588                 };
10589             } else {
10590                 inputblock = {
10591                     cls : 'has-feedback',
10592                     cn :  [
10593                         inputblock,
10594                         feedback
10595                     ] 
10596                 };
10597             }
10598
10599         } else {
10600             if(this.removable && !this.editable && !this.tickable){
10601                 inputblock = {
10602                     cls : 'roo-removable',
10603                     cn :  [
10604                         inputblock,
10605                         {
10606                             tag: 'button',
10607                             html : 'x',
10608                             cls : 'roo-combo-removable-btn close'
10609                         }
10610                     ] 
10611                 };
10612             }
10613         }
10614         
10615         if (this.before || this.after) {
10616             
10617             inputblock = {
10618                 cls : 'input-group',
10619                 cn :  [] 
10620             };
10621             if (this.before) {
10622                 inputblock.cn.push({
10623                     tag :'span',
10624                     cls : 'input-group-addon input-group-prepend input-group-text',
10625                     html : this.before
10626                 });
10627             }
10628             
10629             inputblock.cn.push(input);
10630             
10631             if(this.hasFeedback && !this.allowBlank){
10632                 inputblock.cls += ' has-feedback';
10633                 inputblock.cn.push(feedback);
10634             }
10635             
10636             if (this.after) {
10637                 inputblock.cn.push({
10638                     tag :'span',
10639                     cls : 'input-group-addon input-group-append input-group-text',
10640                     html : this.after
10641                 });
10642             }
10643             
10644         };
10645         
10646       
10647         
10648         var ibwrap = inputblock;
10649         
10650         if(this.multiple){
10651             ibwrap = {
10652                 tag: 'ul',
10653                 cls: 'roo-select2-choices',
10654                 cn:[
10655                     {
10656                         tag: 'li',
10657                         cls: 'roo-select2-search-field',
10658                         cn: [
10659
10660                             inputblock
10661                         ]
10662                     }
10663                 ]
10664             };
10665                 
10666         }
10667         
10668         var combobox = {
10669             cls: 'roo-select2-container input-group',
10670             cn: [
10671                  {
10672                     tag: 'input',
10673                     type : 'hidden',
10674                     cls: 'form-hidden-field'
10675                 },
10676                 ibwrap
10677             ]
10678         };
10679         
10680         if(!this.multiple && this.showToggleBtn){
10681             
10682             var caret = {
10683                         tag: 'span',
10684                         cls: 'caret'
10685              };
10686             if (this.caret != false) {
10687                 caret = {
10688                      tag: 'i',
10689                      cls: 'fa fa-' + this.caret
10690                 };
10691                 
10692             }
10693             
10694             combobox.cn.push({
10695                 tag :'span',
10696                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10697                 cn : [
10698                     Roo.bootstrap.version == 3 ? caret : '',
10699                     {
10700                         tag: 'span',
10701                         cls: 'combobox-clear',
10702                         cn  : [
10703                             {
10704                                 tag : 'i',
10705                                 cls: 'icon-remove'
10706                             }
10707                         ]
10708                     }
10709                 ]
10710
10711             })
10712         }
10713         
10714         if(this.multiple){
10715             combobox.cls += ' roo-select2-container-multi';
10716         }
10717          var indicator = {
10718             tag : 'i',
10719             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10720             tooltip : 'This field is required'
10721         };
10722         if (Roo.bootstrap.version == 4) {
10723             indicator = {
10724                 tag : 'i',
10725                 style : 'display:none'
10726             };
10727         }
10728         
10729         
10730         if (align ==='left' && this.fieldLabel.length) {
10731             
10732             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10733
10734             cfg.cn = [
10735                 indicator,
10736                 {
10737                     tag: 'label',
10738                     'for' :  id,
10739                     cls : 'control-label',
10740                     html : this.fieldLabel
10741
10742                 },
10743                 {
10744                     cls : "", 
10745                     cn: [
10746                         combobox
10747                     ]
10748                 }
10749
10750             ];
10751             
10752             var labelCfg = cfg.cn[1];
10753             var contentCfg = cfg.cn[2];
10754             
10755             if(this.indicatorpos == 'right'){
10756                 cfg.cn = [
10757                     {
10758                         tag: 'label',
10759                         'for' :  id,
10760                         cls : 'control-label',
10761                         cn : [
10762                             {
10763                                 tag : 'span',
10764                                 html : this.fieldLabel
10765                             },
10766                             indicator
10767                         ]
10768                     },
10769                     {
10770                         cls : "", 
10771                         cn: [
10772                             combobox
10773                         ]
10774                     }
10775
10776                 ];
10777                 
10778                 labelCfg = cfg.cn[0];
10779                 contentCfg = cfg.cn[1];
10780             }
10781             
10782             if(this.labelWidth > 12){
10783                 labelCfg.style = "width: " + this.labelWidth + 'px';
10784             }
10785             
10786             if(this.labelWidth < 13 && this.labelmd == 0){
10787                 this.labelmd = this.labelWidth;
10788             }
10789             
10790             if(this.labellg > 0){
10791                 labelCfg.cls += ' col-lg-' + this.labellg;
10792                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10793             }
10794             
10795             if(this.labelmd > 0){
10796                 labelCfg.cls += ' col-md-' + this.labelmd;
10797                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10798             }
10799             
10800             if(this.labelsm > 0){
10801                 labelCfg.cls += ' col-sm-' + this.labelsm;
10802                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10803             }
10804             
10805             if(this.labelxs > 0){
10806                 labelCfg.cls += ' col-xs-' + this.labelxs;
10807                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10808             }
10809             
10810         } else if ( this.fieldLabel.length) {
10811 //                Roo.log(" label");
10812             cfg.cn = [
10813                 indicator,
10814                {
10815                    tag: 'label',
10816                    //cls : 'input-group-addon',
10817                    html : this.fieldLabel
10818
10819                },
10820
10821                combobox
10822
10823             ];
10824             
10825             if(this.indicatorpos == 'right'){
10826                 
10827                 cfg.cn = [
10828                     {
10829                        tag: 'label',
10830                        cn : [
10831                            {
10832                                tag : 'span',
10833                                html : this.fieldLabel
10834                            },
10835                            indicator
10836                        ]
10837
10838                     },
10839                     combobox
10840
10841                 ];
10842
10843             }
10844
10845         } else {
10846             
10847 //                Roo.log(" no label && no align");
10848                 cfg = combobox
10849                      
10850                 
10851         }
10852         
10853         var settings=this;
10854         ['xs','sm','md','lg'].map(function(size){
10855             if (settings[size]) {
10856                 cfg.cls += ' col-' + size + '-' + settings[size];
10857             }
10858         });
10859         
10860         return cfg;
10861         
10862     },
10863     
10864     
10865     
10866     // private
10867     onResize : function(w, h){
10868 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10869 //        if(typeof w == 'number'){
10870 //            var x = w - this.trigger.getWidth();
10871 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10872 //            this.trigger.setStyle('left', x+'px');
10873 //        }
10874     },
10875
10876     // private
10877     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10878
10879     // private
10880     getResizeEl : function(){
10881         return this.inputEl();
10882     },
10883
10884     // private
10885     getPositionEl : function(){
10886         return this.inputEl();
10887     },
10888
10889     // private
10890     alignErrorIcon : function(){
10891         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10892     },
10893
10894     // private
10895     initEvents : function(){
10896         
10897         this.createList();
10898         
10899         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10900         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10901         if(!this.multiple && this.showToggleBtn){
10902             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10903             if(this.hideTrigger){
10904                 this.trigger.setDisplayed(false);
10905             }
10906             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10907         }
10908         
10909         if(this.multiple){
10910             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10911         }
10912         
10913         if(this.removable && !this.editable && !this.tickable){
10914             var close = this.closeTriggerEl();
10915             
10916             if(close){
10917                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10918                 close.on('click', this.removeBtnClick, this, close);
10919             }
10920         }
10921         
10922         //this.trigger.addClassOnOver('x-form-trigger-over');
10923         //this.trigger.addClassOnClick('x-form-trigger-click');
10924         
10925         //if(!this.width){
10926         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10927         //}
10928     },
10929     
10930     closeTriggerEl : function()
10931     {
10932         var close = this.el.select('.roo-combo-removable-btn', true).first();
10933         return close ? close : false;
10934     },
10935     
10936     removeBtnClick : function(e, h, el)
10937     {
10938         e.preventDefault();
10939         
10940         if(this.fireEvent("remove", this) !== false){
10941             this.reset();
10942             this.fireEvent("afterremove", this)
10943         }
10944     },
10945     
10946     createList : function()
10947     {
10948         this.list = Roo.get(document.body).createChild({
10949             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10950             cls: 'typeahead typeahead-long dropdown-menu',
10951             style: 'display:none'
10952         });
10953         
10954         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10955         
10956     },
10957
10958     // private
10959     initTrigger : function(){
10960        
10961     },
10962
10963     // private
10964     onDestroy : function(){
10965         if(this.trigger){
10966             this.trigger.removeAllListeners();
10967           //  this.trigger.remove();
10968         }
10969         //if(this.wrap){
10970         //    this.wrap.remove();
10971         //}
10972         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10973     },
10974
10975     // private
10976     onFocus : function(){
10977         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10978         /*
10979         if(!this.mimicing){
10980             this.wrap.addClass('x-trigger-wrap-focus');
10981             this.mimicing = true;
10982             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10983             if(this.monitorTab){
10984                 this.el.on("keydown", this.checkTab, this);
10985             }
10986         }
10987         */
10988     },
10989
10990     // private
10991     checkTab : function(e){
10992         if(e.getKey() == e.TAB){
10993             this.triggerBlur();
10994         }
10995     },
10996
10997     // private
10998     onBlur : function(){
10999         // do nothing
11000     },
11001
11002     // private
11003     mimicBlur : function(e, t){
11004         /*
11005         if(!this.wrap.contains(t) && this.validateBlur()){
11006             this.triggerBlur();
11007         }
11008         */
11009     },
11010
11011     // private
11012     triggerBlur : function(){
11013         this.mimicing = false;
11014         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11015         if(this.monitorTab){
11016             this.el.un("keydown", this.checkTab, this);
11017         }
11018         //this.wrap.removeClass('x-trigger-wrap-focus');
11019         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11020     },
11021
11022     // private
11023     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11024     validateBlur : function(e, t){
11025         return true;
11026     },
11027
11028     // private
11029     onDisable : function(){
11030         this.inputEl().dom.disabled = true;
11031         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11032         //if(this.wrap){
11033         //    this.wrap.addClass('x-item-disabled');
11034         //}
11035     },
11036
11037     // private
11038     onEnable : function(){
11039         this.inputEl().dom.disabled = false;
11040         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11041         //if(this.wrap){
11042         //    this.el.removeClass('x-item-disabled');
11043         //}
11044     },
11045
11046     // private
11047     onShow : function(){
11048         var ae = this.getActionEl();
11049         
11050         if(ae){
11051             ae.dom.style.display = '';
11052             ae.dom.style.visibility = 'visible';
11053         }
11054     },
11055
11056     // private
11057     
11058     onHide : function(){
11059         var ae = this.getActionEl();
11060         ae.dom.style.display = 'none';
11061     },
11062
11063     /**
11064      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11065      * by an implementing function.
11066      * @method
11067      * @param {EventObject} e
11068      */
11069     onTriggerClick : Roo.emptyFn
11070 });
11071  /*
11072  * Based on:
11073  * Ext JS Library 1.1.1
11074  * Copyright(c) 2006-2007, Ext JS, LLC.
11075  *
11076  * Originally Released Under LGPL - original licence link has changed is not relivant.
11077  *
11078  * Fork - LGPL
11079  * <script type="text/javascript">
11080  */
11081
11082
11083 /**
11084  * @class Roo.data.SortTypes
11085  * @singleton
11086  * Defines the default sorting (casting?) comparison functions used when sorting data.
11087  */
11088 Roo.data.SortTypes = {
11089     /**
11090      * Default sort that does nothing
11091      * @param {Mixed} s The value being converted
11092      * @return {Mixed} The comparison value
11093      */
11094     none : function(s){
11095         return s;
11096     },
11097     
11098     /**
11099      * The regular expression used to strip tags
11100      * @type {RegExp}
11101      * @property
11102      */
11103     stripTagsRE : /<\/?[^>]+>/gi,
11104     
11105     /**
11106      * Strips all HTML tags to sort on text only
11107      * @param {Mixed} s The value being converted
11108      * @return {String} The comparison value
11109      */
11110     asText : function(s){
11111         return String(s).replace(this.stripTagsRE, "");
11112     },
11113     
11114     /**
11115      * Strips all HTML tags to sort on text only - Case insensitive
11116      * @param {Mixed} s The value being converted
11117      * @return {String} The comparison value
11118      */
11119     asUCText : function(s){
11120         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11121     },
11122     
11123     /**
11124      * Case insensitive string
11125      * @param {Mixed} s The value being converted
11126      * @return {String} The comparison value
11127      */
11128     asUCString : function(s) {
11129         return String(s).toUpperCase();
11130     },
11131     
11132     /**
11133      * Date sorting
11134      * @param {Mixed} s The value being converted
11135      * @return {Number} The comparison value
11136      */
11137     asDate : function(s) {
11138         if(!s){
11139             return 0;
11140         }
11141         if(s instanceof Date){
11142             return s.getTime();
11143         }
11144         return Date.parse(String(s));
11145     },
11146     
11147     /**
11148      * Float sorting
11149      * @param {Mixed} s The value being converted
11150      * @return {Float} The comparison value
11151      */
11152     asFloat : function(s) {
11153         var val = parseFloat(String(s).replace(/,/g, ""));
11154         if(isNaN(val)) {
11155             val = 0;
11156         }
11157         return val;
11158     },
11159     
11160     /**
11161      * Integer sorting
11162      * @param {Mixed} s The value being converted
11163      * @return {Number} The comparison value
11164      */
11165     asInt : function(s) {
11166         var val = parseInt(String(s).replace(/,/g, ""));
11167         if(isNaN(val)) {
11168             val = 0;
11169         }
11170         return val;
11171     }
11172 };/*
11173  * Based on:
11174  * Ext JS Library 1.1.1
11175  * Copyright(c) 2006-2007, Ext JS, LLC.
11176  *
11177  * Originally Released Under LGPL - original licence link has changed is not relivant.
11178  *
11179  * Fork - LGPL
11180  * <script type="text/javascript">
11181  */
11182
11183 /**
11184 * @class Roo.data.Record
11185  * Instances of this class encapsulate both record <em>definition</em> information, and record
11186  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11187  * to access Records cached in an {@link Roo.data.Store} object.<br>
11188  * <p>
11189  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11190  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11191  * objects.<br>
11192  * <p>
11193  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11194  * @constructor
11195  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11196  * {@link #create}. The parameters are the same.
11197  * @param {Array} data An associative Array of data values keyed by the field name.
11198  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11199  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11200  * not specified an integer id is generated.
11201  */
11202 Roo.data.Record = function(data, id){
11203     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11204     this.data = data;
11205 };
11206
11207 /**
11208  * Generate a constructor for a specific record layout.
11209  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11210  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11211  * Each field definition object may contain the following properties: <ul>
11212  * <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,
11213  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11214  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11215  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11216  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11217  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11218  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11219  * this may be omitted.</p></li>
11220  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11221  * <ul><li>auto (Default, implies no conversion)</li>
11222  * <li>string</li>
11223  * <li>int</li>
11224  * <li>float</li>
11225  * <li>boolean</li>
11226  * <li>date</li></ul></p></li>
11227  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11228  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11229  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11230  * by the Reader into an object that will be stored in the Record. It is passed the
11231  * following parameters:<ul>
11232  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11233  * </ul></p></li>
11234  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11235  * </ul>
11236  * <br>usage:<br><pre><code>
11237 var TopicRecord = Roo.data.Record.create(
11238     {name: 'title', mapping: 'topic_title'},
11239     {name: 'author', mapping: 'username'},
11240     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11241     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11242     {name: 'lastPoster', mapping: 'user2'},
11243     {name: 'excerpt', mapping: 'post_text'}
11244 );
11245
11246 var myNewRecord = new TopicRecord({
11247     title: 'Do my job please',
11248     author: 'noobie',
11249     totalPosts: 1,
11250     lastPost: new Date(),
11251     lastPoster: 'Animal',
11252     excerpt: 'No way dude!'
11253 });
11254 myStore.add(myNewRecord);
11255 </code></pre>
11256  * @method create
11257  * @static
11258  */
11259 Roo.data.Record.create = function(o){
11260     var f = function(){
11261         f.superclass.constructor.apply(this, arguments);
11262     };
11263     Roo.extend(f, Roo.data.Record);
11264     var p = f.prototype;
11265     p.fields = new Roo.util.MixedCollection(false, function(field){
11266         return field.name;
11267     });
11268     for(var i = 0, len = o.length; i < len; i++){
11269         p.fields.add(new Roo.data.Field(o[i]));
11270     }
11271     f.getField = function(name){
11272         return p.fields.get(name);  
11273     };
11274     return f;
11275 };
11276
11277 Roo.data.Record.AUTO_ID = 1000;
11278 Roo.data.Record.EDIT = 'edit';
11279 Roo.data.Record.REJECT = 'reject';
11280 Roo.data.Record.COMMIT = 'commit';
11281
11282 Roo.data.Record.prototype = {
11283     /**
11284      * Readonly flag - true if this record has been modified.
11285      * @type Boolean
11286      */
11287     dirty : false,
11288     editing : false,
11289     error: null,
11290     modified: null,
11291
11292     // private
11293     join : function(store){
11294         this.store = store;
11295     },
11296
11297     /**
11298      * Set the named field to the specified value.
11299      * @param {String} name The name of the field to set.
11300      * @param {Object} value The value to set the field to.
11301      */
11302     set : function(name, value){
11303         if(this.data[name] == value){
11304             return;
11305         }
11306         this.dirty = true;
11307         if(!this.modified){
11308             this.modified = {};
11309         }
11310         if(typeof this.modified[name] == 'undefined'){
11311             this.modified[name] = this.data[name];
11312         }
11313         this.data[name] = value;
11314         if(!this.editing && this.store){
11315             this.store.afterEdit(this);
11316         }       
11317     },
11318
11319     /**
11320      * Get the value of the named field.
11321      * @param {String} name The name of the field to get the value of.
11322      * @return {Object} The value of the field.
11323      */
11324     get : function(name){
11325         return this.data[name]; 
11326     },
11327
11328     // private
11329     beginEdit : function(){
11330         this.editing = true;
11331         this.modified = {}; 
11332     },
11333
11334     // private
11335     cancelEdit : function(){
11336         this.editing = false;
11337         delete this.modified;
11338     },
11339
11340     // private
11341     endEdit : function(){
11342         this.editing = false;
11343         if(this.dirty && this.store){
11344             this.store.afterEdit(this);
11345         }
11346     },
11347
11348     /**
11349      * Usually called by the {@link Roo.data.Store} which owns the Record.
11350      * Rejects all changes made to the Record since either creation, or the last commit operation.
11351      * Modified fields are reverted to their original values.
11352      * <p>
11353      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11354      * of reject operations.
11355      */
11356     reject : function(){
11357         var m = this.modified;
11358         for(var n in m){
11359             if(typeof m[n] != "function"){
11360                 this.data[n] = m[n];
11361             }
11362         }
11363         this.dirty = false;
11364         delete this.modified;
11365         this.editing = false;
11366         if(this.store){
11367             this.store.afterReject(this);
11368         }
11369     },
11370
11371     /**
11372      * Usually called by the {@link Roo.data.Store} which owns the Record.
11373      * Commits all changes made to the Record since either creation, or the last commit operation.
11374      * <p>
11375      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11376      * of commit operations.
11377      */
11378     commit : function(){
11379         this.dirty = false;
11380         delete this.modified;
11381         this.editing = false;
11382         if(this.store){
11383             this.store.afterCommit(this);
11384         }
11385     },
11386
11387     // private
11388     hasError : function(){
11389         return this.error != null;
11390     },
11391
11392     // private
11393     clearError : function(){
11394         this.error = null;
11395     },
11396
11397     /**
11398      * Creates a copy of this record.
11399      * @param {String} id (optional) A new record id if you don't want to use this record's id
11400      * @return {Record}
11401      */
11402     copy : function(newId) {
11403         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11404     }
11405 };/*
11406  * Based on:
11407  * Ext JS Library 1.1.1
11408  * Copyright(c) 2006-2007, Ext JS, LLC.
11409  *
11410  * Originally Released Under LGPL - original licence link has changed is not relivant.
11411  *
11412  * Fork - LGPL
11413  * <script type="text/javascript">
11414  */
11415
11416
11417
11418 /**
11419  * @class Roo.data.Store
11420  * @extends Roo.util.Observable
11421  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11422  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11423  * <p>
11424  * 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
11425  * has no knowledge of the format of the data returned by the Proxy.<br>
11426  * <p>
11427  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11428  * instances from the data object. These records are cached and made available through accessor functions.
11429  * @constructor
11430  * Creates a new Store.
11431  * @param {Object} config A config object containing the objects needed for the Store to access data,
11432  * and read the data into Records.
11433  */
11434 Roo.data.Store = function(config){
11435     this.data = new Roo.util.MixedCollection(false);
11436     this.data.getKey = function(o){
11437         return o.id;
11438     };
11439     this.baseParams = {};
11440     // private
11441     this.paramNames = {
11442         "start" : "start",
11443         "limit" : "limit",
11444         "sort" : "sort",
11445         "dir" : "dir",
11446         "multisort" : "_multisort"
11447     };
11448
11449     if(config && config.data){
11450         this.inlineData = config.data;
11451         delete config.data;
11452     }
11453
11454     Roo.apply(this, config);
11455     
11456     if(this.reader){ // reader passed
11457         this.reader = Roo.factory(this.reader, Roo.data);
11458         this.reader.xmodule = this.xmodule || false;
11459         if(!this.recordType){
11460             this.recordType = this.reader.recordType;
11461         }
11462         if(this.reader.onMetaChange){
11463             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11464         }
11465     }
11466
11467     if(this.recordType){
11468         this.fields = this.recordType.prototype.fields;
11469     }
11470     this.modified = [];
11471
11472     this.addEvents({
11473         /**
11474          * @event datachanged
11475          * Fires when the data cache has changed, and a widget which is using this Store
11476          * as a Record cache should refresh its view.
11477          * @param {Store} this
11478          */
11479         datachanged : true,
11480         /**
11481          * @event metachange
11482          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11483          * @param {Store} this
11484          * @param {Object} meta The JSON metadata
11485          */
11486         metachange : true,
11487         /**
11488          * @event add
11489          * Fires when Records have been added to the Store
11490          * @param {Store} this
11491          * @param {Roo.data.Record[]} records The array of Records added
11492          * @param {Number} index The index at which the record(s) were added
11493          */
11494         add : true,
11495         /**
11496          * @event remove
11497          * Fires when a Record has been removed from the Store
11498          * @param {Store} this
11499          * @param {Roo.data.Record} record The Record that was removed
11500          * @param {Number} index The index at which the record was removed
11501          */
11502         remove : true,
11503         /**
11504          * @event update
11505          * Fires when a Record has been updated
11506          * @param {Store} this
11507          * @param {Roo.data.Record} record The Record that was updated
11508          * @param {String} operation The update operation being performed.  Value may be one of:
11509          * <pre><code>
11510  Roo.data.Record.EDIT
11511  Roo.data.Record.REJECT
11512  Roo.data.Record.COMMIT
11513          * </code></pre>
11514          */
11515         update : true,
11516         /**
11517          * @event clear
11518          * Fires when the data cache has been cleared.
11519          * @param {Store} this
11520          */
11521         clear : true,
11522         /**
11523          * @event beforeload
11524          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11525          * the load action will be canceled.
11526          * @param {Store} this
11527          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11528          */
11529         beforeload : true,
11530         /**
11531          * @event beforeloadadd
11532          * Fires after a new set of Records has been loaded.
11533          * @param {Store} this
11534          * @param {Roo.data.Record[]} records The Records that were loaded
11535          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11536          */
11537         beforeloadadd : true,
11538         /**
11539          * @event load
11540          * Fires after a new set of Records has been loaded, before they are added to the store.
11541          * @param {Store} this
11542          * @param {Roo.data.Record[]} records The Records that were loaded
11543          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11544          * @params {Object} return from reader
11545          */
11546         load : true,
11547         /**
11548          * @event loadexception
11549          * Fires if an exception occurs in the Proxy during loading.
11550          * Called with the signature of the Proxy's "loadexception" event.
11551          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11552          * 
11553          * @param {Proxy} 
11554          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11555          * @param {Object} load options 
11556          * @param {Object} jsonData from your request (normally this contains the Exception)
11557          */
11558         loadexception : true
11559     });
11560     
11561     if(this.proxy){
11562         this.proxy = Roo.factory(this.proxy, Roo.data);
11563         this.proxy.xmodule = this.xmodule || false;
11564         this.relayEvents(this.proxy,  ["loadexception"]);
11565     }
11566     this.sortToggle = {};
11567     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11568
11569     Roo.data.Store.superclass.constructor.call(this);
11570
11571     if(this.inlineData){
11572         this.loadData(this.inlineData);
11573         delete this.inlineData;
11574     }
11575 };
11576
11577 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11578      /**
11579     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11580     * without a remote query - used by combo/forms at present.
11581     */
11582     
11583     /**
11584     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11585     */
11586     /**
11587     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11588     */
11589     /**
11590     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11591     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11592     */
11593     /**
11594     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11595     * on any HTTP request
11596     */
11597     /**
11598     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11599     */
11600     /**
11601     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11602     */
11603     multiSort: false,
11604     /**
11605     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11606     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11607     */
11608     remoteSort : false,
11609
11610     /**
11611     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11612      * loaded or when a record is removed. (defaults to false).
11613     */
11614     pruneModifiedRecords : false,
11615
11616     // private
11617     lastOptions : null,
11618
11619     /**
11620      * Add Records to the Store and fires the add event.
11621      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11622      */
11623     add : function(records){
11624         records = [].concat(records);
11625         for(var i = 0, len = records.length; i < len; i++){
11626             records[i].join(this);
11627         }
11628         var index = this.data.length;
11629         this.data.addAll(records);
11630         this.fireEvent("add", this, records, index);
11631     },
11632
11633     /**
11634      * Remove a Record from the Store and fires the remove event.
11635      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11636      */
11637     remove : function(record){
11638         var index = this.data.indexOf(record);
11639         this.data.removeAt(index);
11640  
11641         if(this.pruneModifiedRecords){
11642             this.modified.remove(record);
11643         }
11644         this.fireEvent("remove", this, record, index);
11645     },
11646
11647     /**
11648      * Remove all Records from the Store and fires the clear event.
11649      */
11650     removeAll : function(){
11651         this.data.clear();
11652         if(this.pruneModifiedRecords){
11653             this.modified = [];
11654         }
11655         this.fireEvent("clear", this);
11656     },
11657
11658     /**
11659      * Inserts Records to the Store at the given index and fires the add event.
11660      * @param {Number} index The start index at which to insert the passed Records.
11661      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11662      */
11663     insert : function(index, records){
11664         records = [].concat(records);
11665         for(var i = 0, len = records.length; i < len; i++){
11666             this.data.insert(index, records[i]);
11667             records[i].join(this);
11668         }
11669         this.fireEvent("add", this, records, index);
11670     },
11671
11672     /**
11673      * Get the index within the cache of the passed Record.
11674      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11675      * @return {Number} The index of the passed Record. Returns -1 if not found.
11676      */
11677     indexOf : function(record){
11678         return this.data.indexOf(record);
11679     },
11680
11681     /**
11682      * Get the index within the cache of the Record with the passed id.
11683      * @param {String} id The id of the Record to find.
11684      * @return {Number} The index of the Record. Returns -1 if not found.
11685      */
11686     indexOfId : function(id){
11687         return this.data.indexOfKey(id);
11688     },
11689
11690     /**
11691      * Get the Record with the specified id.
11692      * @param {String} id The id of the Record to find.
11693      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11694      */
11695     getById : function(id){
11696         return this.data.key(id);
11697     },
11698
11699     /**
11700      * Get the Record at the specified index.
11701      * @param {Number} index The index of the Record to find.
11702      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11703      */
11704     getAt : function(index){
11705         return this.data.itemAt(index);
11706     },
11707
11708     /**
11709      * Returns a range of Records between specified indices.
11710      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11711      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11712      * @return {Roo.data.Record[]} An array of Records
11713      */
11714     getRange : function(start, end){
11715         return this.data.getRange(start, end);
11716     },
11717
11718     // private
11719     storeOptions : function(o){
11720         o = Roo.apply({}, o);
11721         delete o.callback;
11722         delete o.scope;
11723         this.lastOptions = o;
11724     },
11725
11726     /**
11727      * Loads the Record cache from the configured Proxy using the configured Reader.
11728      * <p>
11729      * If using remote paging, then the first load call must specify the <em>start</em>
11730      * and <em>limit</em> properties in the options.params property to establish the initial
11731      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11732      * <p>
11733      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11734      * and this call will return before the new data has been loaded. Perform any post-processing
11735      * in a callback function, or in a "load" event handler.</strong>
11736      * <p>
11737      * @param {Object} options An object containing properties which control loading options:<ul>
11738      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11739      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11740      * passed the following arguments:<ul>
11741      * <li>r : Roo.data.Record[]</li>
11742      * <li>options: Options object from the load call</li>
11743      * <li>success: Boolean success indicator</li></ul></li>
11744      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11745      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11746      * </ul>
11747      */
11748     load : function(options){
11749         options = options || {};
11750         if(this.fireEvent("beforeload", this, options) !== false){
11751             this.storeOptions(options);
11752             var p = Roo.apply(options.params || {}, this.baseParams);
11753             // if meta was not loaded from remote source.. try requesting it.
11754             if (!this.reader.metaFromRemote) {
11755                 p._requestMeta = 1;
11756             }
11757             if(this.sortInfo && this.remoteSort){
11758                 var pn = this.paramNames;
11759                 p[pn["sort"]] = this.sortInfo.field;
11760                 p[pn["dir"]] = this.sortInfo.direction;
11761             }
11762             if (this.multiSort) {
11763                 var pn = this.paramNames;
11764                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11765             }
11766             
11767             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11768         }
11769     },
11770
11771     /**
11772      * Reloads the Record cache from the configured Proxy using the configured Reader and
11773      * the options from the last load operation performed.
11774      * @param {Object} options (optional) An object containing properties which may override the options
11775      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11776      * the most recently used options are reused).
11777      */
11778     reload : function(options){
11779         this.load(Roo.applyIf(options||{}, this.lastOptions));
11780     },
11781
11782     // private
11783     // Called as a callback by the Reader during a load operation.
11784     loadRecords : function(o, options, success){
11785         if(!o || success === false){
11786             if(success !== false){
11787                 this.fireEvent("load", this, [], options, o);
11788             }
11789             if(options.callback){
11790                 options.callback.call(options.scope || this, [], options, false);
11791             }
11792             return;
11793         }
11794         // if data returned failure - throw an exception.
11795         if (o.success === false) {
11796             // show a message if no listener is registered.
11797             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11798                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11799             }
11800             // loadmask wil be hooked into this..
11801             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11802             return;
11803         }
11804         var r = o.records, t = o.totalRecords || r.length;
11805         
11806         this.fireEvent("beforeloadadd", this, r, options, o);
11807         
11808         if(!options || options.add !== true){
11809             if(this.pruneModifiedRecords){
11810                 this.modified = [];
11811             }
11812             for(var i = 0, len = r.length; i < len; i++){
11813                 r[i].join(this);
11814             }
11815             if(this.snapshot){
11816                 this.data = this.snapshot;
11817                 delete this.snapshot;
11818             }
11819             this.data.clear();
11820             this.data.addAll(r);
11821             this.totalLength = t;
11822             this.applySort();
11823             this.fireEvent("datachanged", this);
11824         }else{
11825             this.totalLength = Math.max(t, this.data.length+r.length);
11826             this.add(r);
11827         }
11828         
11829         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11830                 
11831             var e = new Roo.data.Record({});
11832
11833             e.set(this.parent.displayField, this.parent.emptyTitle);
11834             e.set(this.parent.valueField, '');
11835
11836             this.insert(0, e);
11837         }
11838             
11839         this.fireEvent("load", this, r, options, o);
11840         if(options.callback){
11841             options.callback.call(options.scope || this, r, options, true);
11842         }
11843     },
11844
11845
11846     /**
11847      * Loads data from a passed data block. A Reader which understands the format of the data
11848      * must have been configured in the constructor.
11849      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11850      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11851      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11852      */
11853     loadData : function(o, append){
11854         var r = this.reader.readRecords(o);
11855         this.loadRecords(r, {add: append}, true);
11856     },
11857
11858     /**
11859      * Gets the number of cached records.
11860      * <p>
11861      * <em>If using paging, this may not be the total size of the dataset. If the data object
11862      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11863      * the data set size</em>
11864      */
11865     getCount : function(){
11866         return this.data.length || 0;
11867     },
11868
11869     /**
11870      * Gets the total number of records in the dataset as returned by the server.
11871      * <p>
11872      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11873      * the dataset size</em>
11874      */
11875     getTotalCount : function(){
11876         return this.totalLength || 0;
11877     },
11878
11879     /**
11880      * Returns the sort state of the Store as an object with two properties:
11881      * <pre><code>
11882  field {String} The name of the field by which the Records are sorted
11883  direction {String} The sort order, "ASC" or "DESC"
11884      * </code></pre>
11885      */
11886     getSortState : function(){
11887         return this.sortInfo;
11888     },
11889
11890     // private
11891     applySort : function(){
11892         if(this.sortInfo && !this.remoteSort){
11893             var s = this.sortInfo, f = s.field;
11894             var st = this.fields.get(f).sortType;
11895             var fn = function(r1, r2){
11896                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11897                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11898             };
11899             this.data.sort(s.direction, fn);
11900             if(this.snapshot && this.snapshot != this.data){
11901                 this.snapshot.sort(s.direction, fn);
11902             }
11903         }
11904     },
11905
11906     /**
11907      * Sets the default sort column and order to be used by the next load operation.
11908      * @param {String} fieldName The name of the field to sort by.
11909      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11910      */
11911     setDefaultSort : function(field, dir){
11912         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11913     },
11914
11915     /**
11916      * Sort the Records.
11917      * If remote sorting is used, the sort is performed on the server, and the cache is
11918      * reloaded. If local sorting is used, the cache is sorted internally.
11919      * @param {String} fieldName The name of the field to sort by.
11920      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11921      */
11922     sort : function(fieldName, dir){
11923         var f = this.fields.get(fieldName);
11924         if(!dir){
11925             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11926             
11927             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11928                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11929             }else{
11930                 dir = f.sortDir;
11931             }
11932         }
11933         this.sortToggle[f.name] = dir;
11934         this.sortInfo = {field: f.name, direction: dir};
11935         if(!this.remoteSort){
11936             this.applySort();
11937             this.fireEvent("datachanged", this);
11938         }else{
11939             this.load(this.lastOptions);
11940         }
11941     },
11942
11943     /**
11944      * Calls the specified function for each of the Records in the cache.
11945      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11946      * Returning <em>false</em> aborts and exits the iteration.
11947      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11948      */
11949     each : function(fn, scope){
11950         this.data.each(fn, scope);
11951     },
11952
11953     /**
11954      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11955      * (e.g., during paging).
11956      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11957      */
11958     getModifiedRecords : function(){
11959         return this.modified;
11960     },
11961
11962     // private
11963     createFilterFn : function(property, value, anyMatch){
11964         if(!value.exec){ // not a regex
11965             value = String(value);
11966             if(value.length == 0){
11967                 return false;
11968             }
11969             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11970         }
11971         return function(r){
11972             return value.test(r.data[property]);
11973         };
11974     },
11975
11976     /**
11977      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11978      * @param {String} property A field on your records
11979      * @param {Number} start The record index to start at (defaults to 0)
11980      * @param {Number} end The last record index to include (defaults to length - 1)
11981      * @return {Number} The sum
11982      */
11983     sum : function(property, start, end){
11984         var rs = this.data.items, v = 0;
11985         start = start || 0;
11986         end = (end || end === 0) ? end : rs.length-1;
11987
11988         for(var i = start; i <= end; i++){
11989             v += (rs[i].data[property] || 0);
11990         }
11991         return v;
11992     },
11993
11994     /**
11995      * Filter the records by a specified property.
11996      * @param {String} field A field on your records
11997      * @param {String/RegExp} value Either a string that the field
11998      * should start with or a RegExp to test against the field
11999      * @param {Boolean} anyMatch True to match any part not just the beginning
12000      */
12001     filter : function(property, value, anyMatch){
12002         var fn = this.createFilterFn(property, value, anyMatch);
12003         return fn ? this.filterBy(fn) : this.clearFilter();
12004     },
12005
12006     /**
12007      * Filter by a function. The specified function will be called with each
12008      * record in this data source. If the function returns true the record is included,
12009      * otherwise it is filtered.
12010      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12011      * @param {Object} scope (optional) The scope of the function (defaults to this)
12012      */
12013     filterBy : function(fn, scope){
12014         this.snapshot = this.snapshot || this.data;
12015         this.data = this.queryBy(fn, scope||this);
12016         this.fireEvent("datachanged", this);
12017     },
12018
12019     /**
12020      * Query the records by a specified property.
12021      * @param {String} field A field on your records
12022      * @param {String/RegExp} value Either a string that the field
12023      * should start with or a RegExp to test against the field
12024      * @param {Boolean} anyMatch True to match any part not just the beginning
12025      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12026      */
12027     query : function(property, value, anyMatch){
12028         var fn = this.createFilterFn(property, value, anyMatch);
12029         return fn ? this.queryBy(fn) : this.data.clone();
12030     },
12031
12032     /**
12033      * Query by a function. The specified function will be called with each
12034      * record in this data source. If the function returns true the record is included
12035      * in the results.
12036      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12037      * @param {Object} scope (optional) The scope of the function (defaults to this)
12038       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12039      **/
12040     queryBy : function(fn, scope){
12041         var data = this.snapshot || this.data;
12042         return data.filterBy(fn, scope||this);
12043     },
12044
12045     /**
12046      * Collects unique values for a particular dataIndex from this store.
12047      * @param {String} dataIndex The property to collect
12048      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12049      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12050      * @return {Array} An array of the unique values
12051      **/
12052     collect : function(dataIndex, allowNull, bypassFilter){
12053         var d = (bypassFilter === true && this.snapshot) ?
12054                 this.snapshot.items : this.data.items;
12055         var v, sv, r = [], l = {};
12056         for(var i = 0, len = d.length; i < len; i++){
12057             v = d[i].data[dataIndex];
12058             sv = String(v);
12059             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12060                 l[sv] = true;
12061                 r[r.length] = v;
12062             }
12063         }
12064         return r;
12065     },
12066
12067     /**
12068      * Revert to a view of the Record cache with no filtering applied.
12069      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12070      */
12071     clearFilter : function(suppressEvent){
12072         if(this.snapshot && this.snapshot != this.data){
12073             this.data = this.snapshot;
12074             delete this.snapshot;
12075             if(suppressEvent !== true){
12076                 this.fireEvent("datachanged", this);
12077             }
12078         }
12079     },
12080
12081     // private
12082     afterEdit : function(record){
12083         if(this.modified.indexOf(record) == -1){
12084             this.modified.push(record);
12085         }
12086         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12087     },
12088     
12089     // private
12090     afterReject : function(record){
12091         this.modified.remove(record);
12092         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12093     },
12094
12095     // private
12096     afterCommit : function(record){
12097         this.modified.remove(record);
12098         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12099     },
12100
12101     /**
12102      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12103      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12104      */
12105     commitChanges : function(){
12106         var m = this.modified.slice(0);
12107         this.modified = [];
12108         for(var i = 0, len = m.length; i < len; i++){
12109             m[i].commit();
12110         }
12111     },
12112
12113     /**
12114      * Cancel outstanding changes on all changed records.
12115      */
12116     rejectChanges : function(){
12117         var m = this.modified.slice(0);
12118         this.modified = [];
12119         for(var i = 0, len = m.length; i < len; i++){
12120             m[i].reject();
12121         }
12122     },
12123
12124     onMetaChange : function(meta, rtype, o){
12125         this.recordType = rtype;
12126         this.fields = rtype.prototype.fields;
12127         delete this.snapshot;
12128         this.sortInfo = meta.sortInfo || this.sortInfo;
12129         this.modified = [];
12130         this.fireEvent('metachange', this, this.reader.meta);
12131     },
12132     
12133     moveIndex : function(data, type)
12134     {
12135         var index = this.indexOf(data);
12136         
12137         var newIndex = index + type;
12138         
12139         this.remove(data);
12140         
12141         this.insert(newIndex, data);
12142         
12143     }
12144 });/*
12145  * Based on:
12146  * Ext JS Library 1.1.1
12147  * Copyright(c) 2006-2007, Ext JS, LLC.
12148  *
12149  * Originally Released Under LGPL - original licence link has changed is not relivant.
12150  *
12151  * Fork - LGPL
12152  * <script type="text/javascript">
12153  */
12154
12155 /**
12156  * @class Roo.data.SimpleStore
12157  * @extends Roo.data.Store
12158  * Small helper class to make creating Stores from Array data easier.
12159  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12160  * @cfg {Array} fields An array of field definition objects, or field name strings.
12161  * @cfg {Array} data The multi-dimensional array of data
12162  * @constructor
12163  * @param {Object} config
12164  */
12165 Roo.data.SimpleStore = function(config){
12166     Roo.data.SimpleStore.superclass.constructor.call(this, {
12167         isLocal : true,
12168         reader: new Roo.data.ArrayReader({
12169                 id: config.id
12170             },
12171             Roo.data.Record.create(config.fields)
12172         ),
12173         proxy : new Roo.data.MemoryProxy(config.data)
12174     });
12175     this.load();
12176 };
12177 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12178  * Based on:
12179  * Ext JS Library 1.1.1
12180  * Copyright(c) 2006-2007, Ext JS, LLC.
12181  *
12182  * Originally Released Under LGPL - original licence link has changed is not relivant.
12183  *
12184  * Fork - LGPL
12185  * <script type="text/javascript">
12186  */
12187
12188 /**
12189 /**
12190  * @extends Roo.data.Store
12191  * @class Roo.data.JsonStore
12192  * Small helper class to make creating Stores for JSON data easier. <br/>
12193 <pre><code>
12194 var store = new Roo.data.JsonStore({
12195     url: 'get-images.php',
12196     root: 'images',
12197     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12198 });
12199 </code></pre>
12200  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12201  * JsonReader and HttpProxy (unless inline data is provided).</b>
12202  * @cfg {Array} fields An array of field definition objects, or field name strings.
12203  * @constructor
12204  * @param {Object} config
12205  */
12206 Roo.data.JsonStore = function(c){
12207     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12208         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12209         reader: new Roo.data.JsonReader(c, c.fields)
12210     }));
12211 };
12212 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12213  * Based on:
12214  * Ext JS Library 1.1.1
12215  * Copyright(c) 2006-2007, Ext JS, LLC.
12216  *
12217  * Originally Released Under LGPL - original licence link has changed is not relivant.
12218  *
12219  * Fork - LGPL
12220  * <script type="text/javascript">
12221  */
12222
12223  
12224 Roo.data.Field = function(config){
12225     if(typeof config == "string"){
12226         config = {name: config};
12227     }
12228     Roo.apply(this, config);
12229     
12230     if(!this.type){
12231         this.type = "auto";
12232     }
12233     
12234     var st = Roo.data.SortTypes;
12235     // named sortTypes are supported, here we look them up
12236     if(typeof this.sortType == "string"){
12237         this.sortType = st[this.sortType];
12238     }
12239     
12240     // set default sortType for strings and dates
12241     if(!this.sortType){
12242         switch(this.type){
12243             case "string":
12244                 this.sortType = st.asUCString;
12245                 break;
12246             case "date":
12247                 this.sortType = st.asDate;
12248                 break;
12249             default:
12250                 this.sortType = st.none;
12251         }
12252     }
12253
12254     // define once
12255     var stripRe = /[\$,%]/g;
12256
12257     // prebuilt conversion function for this field, instead of
12258     // switching every time we're reading a value
12259     if(!this.convert){
12260         var cv, dateFormat = this.dateFormat;
12261         switch(this.type){
12262             case "":
12263             case "auto":
12264             case undefined:
12265                 cv = function(v){ return v; };
12266                 break;
12267             case "string":
12268                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12269                 break;
12270             case "int":
12271                 cv = function(v){
12272                     return v !== undefined && v !== null && v !== '' ?
12273                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12274                     };
12275                 break;
12276             case "float":
12277                 cv = function(v){
12278                     return v !== undefined && v !== null && v !== '' ?
12279                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12280                     };
12281                 break;
12282             case "bool":
12283             case "boolean":
12284                 cv = function(v){ return v === true || v === "true" || v == 1; };
12285                 break;
12286             case "date":
12287                 cv = function(v){
12288                     if(!v){
12289                         return '';
12290                     }
12291                     if(v instanceof Date){
12292                         return v;
12293                     }
12294                     if(dateFormat){
12295                         if(dateFormat == "timestamp"){
12296                             return new Date(v*1000);
12297                         }
12298                         return Date.parseDate(v, dateFormat);
12299                     }
12300                     var parsed = Date.parse(v);
12301                     return parsed ? new Date(parsed) : null;
12302                 };
12303              break;
12304             
12305         }
12306         this.convert = cv;
12307     }
12308 };
12309
12310 Roo.data.Field.prototype = {
12311     dateFormat: null,
12312     defaultValue: "",
12313     mapping: null,
12314     sortType : null,
12315     sortDir : "ASC"
12316 };/*
12317  * Based on:
12318  * Ext JS Library 1.1.1
12319  * Copyright(c) 2006-2007, Ext JS, LLC.
12320  *
12321  * Originally Released Under LGPL - original licence link has changed is not relivant.
12322  *
12323  * Fork - LGPL
12324  * <script type="text/javascript">
12325  */
12326  
12327 // Base class for reading structured data from a data source.  This class is intended to be
12328 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12329
12330 /**
12331  * @class Roo.data.DataReader
12332  * Base class for reading structured data from a data source.  This class is intended to be
12333  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12334  */
12335
12336 Roo.data.DataReader = function(meta, recordType){
12337     
12338     this.meta = meta;
12339     
12340     this.recordType = recordType instanceof Array ? 
12341         Roo.data.Record.create(recordType) : recordType;
12342 };
12343
12344 Roo.data.DataReader.prototype = {
12345      /**
12346      * Create an empty record
12347      * @param {Object} data (optional) - overlay some values
12348      * @return {Roo.data.Record} record created.
12349      */
12350     newRow :  function(d) {
12351         var da =  {};
12352         this.recordType.prototype.fields.each(function(c) {
12353             switch( c.type) {
12354                 case 'int' : da[c.name] = 0; break;
12355                 case 'date' : da[c.name] = new Date(); break;
12356                 case 'float' : da[c.name] = 0.0; break;
12357                 case 'boolean' : da[c.name] = false; break;
12358                 default : da[c.name] = ""; break;
12359             }
12360             
12361         });
12362         return new this.recordType(Roo.apply(da, d));
12363     }
12364     
12365 };/*
12366  * Based on:
12367  * Ext JS Library 1.1.1
12368  * Copyright(c) 2006-2007, Ext JS, LLC.
12369  *
12370  * Originally Released Under LGPL - original licence link has changed is not relivant.
12371  *
12372  * Fork - LGPL
12373  * <script type="text/javascript">
12374  */
12375
12376 /**
12377  * @class Roo.data.DataProxy
12378  * @extends Roo.data.Observable
12379  * This class is an abstract base class for implementations which provide retrieval of
12380  * unformatted data objects.<br>
12381  * <p>
12382  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12383  * (of the appropriate type which knows how to parse the data object) to provide a block of
12384  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12385  * <p>
12386  * Custom implementations must implement the load method as described in
12387  * {@link Roo.data.HttpProxy#load}.
12388  */
12389 Roo.data.DataProxy = function(){
12390     this.addEvents({
12391         /**
12392          * @event beforeload
12393          * Fires before a network request is made to retrieve a data object.
12394          * @param {Object} This DataProxy object.
12395          * @param {Object} params The params parameter to the load function.
12396          */
12397         beforeload : true,
12398         /**
12399          * @event load
12400          * Fires before the load method's callback is called.
12401          * @param {Object} This DataProxy object.
12402          * @param {Object} o The data object.
12403          * @param {Object} arg The callback argument object passed to the load function.
12404          */
12405         load : true,
12406         /**
12407          * @event loadexception
12408          * Fires if an Exception occurs during data retrieval.
12409          * @param {Object} This DataProxy object.
12410          * @param {Object} o The data object.
12411          * @param {Object} arg The callback argument object passed to the load function.
12412          * @param {Object} e The Exception.
12413          */
12414         loadexception : true
12415     });
12416     Roo.data.DataProxy.superclass.constructor.call(this);
12417 };
12418
12419 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12420
12421     /**
12422      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12423      */
12424 /*
12425  * Based on:
12426  * Ext JS Library 1.1.1
12427  * Copyright(c) 2006-2007, Ext JS, LLC.
12428  *
12429  * Originally Released Under LGPL - original licence link has changed is not relivant.
12430  *
12431  * Fork - LGPL
12432  * <script type="text/javascript">
12433  */
12434 /**
12435  * @class Roo.data.MemoryProxy
12436  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12437  * to the Reader when its load method is called.
12438  * @constructor
12439  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12440  */
12441 Roo.data.MemoryProxy = function(data){
12442     if (data.data) {
12443         data = data.data;
12444     }
12445     Roo.data.MemoryProxy.superclass.constructor.call(this);
12446     this.data = data;
12447 };
12448
12449 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12450     
12451     /**
12452      * Load data from the requested source (in this case an in-memory
12453      * data object passed to the constructor), read the data object into
12454      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12455      * process that block using the passed callback.
12456      * @param {Object} params This parameter is not used by the MemoryProxy class.
12457      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12458      * object into a block of Roo.data.Records.
12459      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12460      * The function must be passed <ul>
12461      * <li>The Record block object</li>
12462      * <li>The "arg" argument from the load function</li>
12463      * <li>A boolean success indicator</li>
12464      * </ul>
12465      * @param {Object} scope The scope in which to call the callback
12466      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12467      */
12468     load : function(params, reader, callback, scope, arg){
12469         params = params || {};
12470         var result;
12471         try {
12472             result = reader.readRecords(params.data ? params.data :this.data);
12473         }catch(e){
12474             this.fireEvent("loadexception", this, arg, null, e);
12475             callback.call(scope, null, arg, false);
12476             return;
12477         }
12478         callback.call(scope, result, arg, true);
12479     },
12480     
12481     // private
12482     update : function(params, records){
12483         
12484     }
12485 });/*
12486  * Based on:
12487  * Ext JS Library 1.1.1
12488  * Copyright(c) 2006-2007, Ext JS, LLC.
12489  *
12490  * Originally Released Under LGPL - original licence link has changed is not relivant.
12491  *
12492  * Fork - LGPL
12493  * <script type="text/javascript">
12494  */
12495 /**
12496  * @class Roo.data.HttpProxy
12497  * @extends Roo.data.DataProxy
12498  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12499  * configured to reference a certain URL.<br><br>
12500  * <p>
12501  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12502  * from which the running page was served.<br><br>
12503  * <p>
12504  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12505  * <p>
12506  * Be aware that to enable the browser to parse an XML document, the server must set
12507  * the Content-Type header in the HTTP response to "text/xml".
12508  * @constructor
12509  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12510  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12511  * will be used to make the request.
12512  */
12513 Roo.data.HttpProxy = function(conn){
12514     Roo.data.HttpProxy.superclass.constructor.call(this);
12515     // is conn a conn config or a real conn?
12516     this.conn = conn;
12517     this.useAjax = !conn || !conn.events;
12518   
12519 };
12520
12521 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12522     // thse are take from connection...
12523     
12524     /**
12525      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12526      */
12527     /**
12528      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12529      * extra parameters to each request made by this object. (defaults to undefined)
12530      */
12531     /**
12532      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12533      *  to each request made by this object. (defaults to undefined)
12534      */
12535     /**
12536      * @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)
12537      */
12538     /**
12539      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12540      */
12541      /**
12542      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12543      * @type Boolean
12544      */
12545   
12546
12547     /**
12548      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12549      * @type Boolean
12550      */
12551     /**
12552      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12553      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12554      * a finer-grained basis than the DataProxy events.
12555      */
12556     getConnection : function(){
12557         return this.useAjax ? Roo.Ajax : this.conn;
12558     },
12559
12560     /**
12561      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12562      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12563      * process that block using the passed callback.
12564      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12565      * for the request to the remote server.
12566      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12567      * object into a block of Roo.data.Records.
12568      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12569      * The function must be passed <ul>
12570      * <li>The Record block object</li>
12571      * <li>The "arg" argument from the load function</li>
12572      * <li>A boolean success indicator</li>
12573      * </ul>
12574      * @param {Object} scope The scope in which to call the callback
12575      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12576      */
12577     load : function(params, reader, callback, scope, arg){
12578         if(this.fireEvent("beforeload", this, params) !== false){
12579             var  o = {
12580                 params : params || {},
12581                 request: {
12582                     callback : callback,
12583                     scope : scope,
12584                     arg : arg
12585                 },
12586                 reader: reader,
12587                 callback : this.loadResponse,
12588                 scope: this
12589             };
12590             if(this.useAjax){
12591                 Roo.applyIf(o, this.conn);
12592                 if(this.activeRequest){
12593                     Roo.Ajax.abort(this.activeRequest);
12594                 }
12595                 this.activeRequest = Roo.Ajax.request(o);
12596             }else{
12597                 this.conn.request(o);
12598             }
12599         }else{
12600             callback.call(scope||this, null, arg, false);
12601         }
12602     },
12603
12604     // private
12605     loadResponse : function(o, success, response){
12606         delete this.activeRequest;
12607         if(!success){
12608             this.fireEvent("loadexception", this, o, response);
12609             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12610             return;
12611         }
12612         var result;
12613         try {
12614             result = o.reader.read(response);
12615         }catch(e){
12616             this.fireEvent("loadexception", this, o, response, e);
12617             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12618             return;
12619         }
12620         
12621         this.fireEvent("load", this, o, o.request.arg);
12622         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12623     },
12624
12625     // private
12626     update : function(dataSet){
12627
12628     },
12629
12630     // private
12631     updateResponse : function(dataSet){
12632
12633     }
12634 });/*
12635  * Based on:
12636  * Ext JS Library 1.1.1
12637  * Copyright(c) 2006-2007, Ext JS, LLC.
12638  *
12639  * Originally Released Under LGPL - original licence link has changed is not relivant.
12640  *
12641  * Fork - LGPL
12642  * <script type="text/javascript">
12643  */
12644
12645 /**
12646  * @class Roo.data.ScriptTagProxy
12647  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12648  * other than the originating domain of the running page.<br><br>
12649  * <p>
12650  * <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
12651  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12652  * <p>
12653  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12654  * source code that is used as the source inside a &lt;script> tag.<br><br>
12655  * <p>
12656  * In order for the browser to process the returned data, the server must wrap the data object
12657  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12658  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12659  * depending on whether the callback name was passed:
12660  * <p>
12661  * <pre><code>
12662 boolean scriptTag = false;
12663 String cb = request.getParameter("callback");
12664 if (cb != null) {
12665     scriptTag = true;
12666     response.setContentType("text/javascript");
12667 } else {
12668     response.setContentType("application/x-json");
12669 }
12670 Writer out = response.getWriter();
12671 if (scriptTag) {
12672     out.write(cb + "(");
12673 }
12674 out.print(dataBlock.toJsonString());
12675 if (scriptTag) {
12676     out.write(");");
12677 }
12678 </pre></code>
12679  *
12680  * @constructor
12681  * @param {Object} config A configuration object.
12682  */
12683 Roo.data.ScriptTagProxy = function(config){
12684     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12685     Roo.apply(this, config);
12686     this.head = document.getElementsByTagName("head")[0];
12687 };
12688
12689 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12690
12691 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12692     /**
12693      * @cfg {String} url The URL from which to request the data object.
12694      */
12695     /**
12696      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12697      */
12698     timeout : 30000,
12699     /**
12700      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12701      * the server the name of the callback function set up by the load call to process the returned data object.
12702      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12703      * javascript output which calls this named function passing the data object as its only parameter.
12704      */
12705     callbackParam : "callback",
12706     /**
12707      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12708      * name to the request.
12709      */
12710     nocache : true,
12711
12712     /**
12713      * Load data from the configured URL, read the data object into
12714      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12715      * process that block using the passed callback.
12716      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12717      * for the request to the remote server.
12718      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12719      * object into a block of Roo.data.Records.
12720      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12721      * The function must be passed <ul>
12722      * <li>The Record block object</li>
12723      * <li>The "arg" argument from the load function</li>
12724      * <li>A boolean success indicator</li>
12725      * </ul>
12726      * @param {Object} scope The scope in which to call the callback
12727      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12728      */
12729     load : function(params, reader, callback, scope, arg){
12730         if(this.fireEvent("beforeload", this, params) !== false){
12731
12732             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12733
12734             var url = this.url;
12735             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12736             if(this.nocache){
12737                 url += "&_dc=" + (new Date().getTime());
12738             }
12739             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12740             var trans = {
12741                 id : transId,
12742                 cb : "stcCallback"+transId,
12743                 scriptId : "stcScript"+transId,
12744                 params : params,
12745                 arg : arg,
12746                 url : url,
12747                 callback : callback,
12748                 scope : scope,
12749                 reader : reader
12750             };
12751             var conn = this;
12752
12753             window[trans.cb] = function(o){
12754                 conn.handleResponse(o, trans);
12755             };
12756
12757             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12758
12759             if(this.autoAbort !== false){
12760                 this.abort();
12761             }
12762
12763             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12764
12765             var script = document.createElement("script");
12766             script.setAttribute("src", url);
12767             script.setAttribute("type", "text/javascript");
12768             script.setAttribute("id", trans.scriptId);
12769             this.head.appendChild(script);
12770
12771             this.trans = trans;
12772         }else{
12773             callback.call(scope||this, null, arg, false);
12774         }
12775     },
12776
12777     // private
12778     isLoading : function(){
12779         return this.trans ? true : false;
12780     },
12781
12782     /**
12783      * Abort the current server request.
12784      */
12785     abort : function(){
12786         if(this.isLoading()){
12787             this.destroyTrans(this.trans);
12788         }
12789     },
12790
12791     // private
12792     destroyTrans : function(trans, isLoaded){
12793         this.head.removeChild(document.getElementById(trans.scriptId));
12794         clearTimeout(trans.timeoutId);
12795         if(isLoaded){
12796             window[trans.cb] = undefined;
12797             try{
12798                 delete window[trans.cb];
12799             }catch(e){}
12800         }else{
12801             // if hasn't been loaded, wait for load to remove it to prevent script error
12802             window[trans.cb] = function(){
12803                 window[trans.cb] = undefined;
12804                 try{
12805                     delete window[trans.cb];
12806                 }catch(e){}
12807             };
12808         }
12809     },
12810
12811     // private
12812     handleResponse : function(o, trans){
12813         this.trans = false;
12814         this.destroyTrans(trans, true);
12815         var result;
12816         try {
12817             result = trans.reader.readRecords(o);
12818         }catch(e){
12819             this.fireEvent("loadexception", this, o, trans.arg, e);
12820             trans.callback.call(trans.scope||window, null, trans.arg, false);
12821             return;
12822         }
12823         this.fireEvent("load", this, o, trans.arg);
12824         trans.callback.call(trans.scope||window, result, trans.arg, true);
12825     },
12826
12827     // private
12828     handleFailure : function(trans){
12829         this.trans = false;
12830         this.destroyTrans(trans, false);
12831         this.fireEvent("loadexception", this, null, trans.arg);
12832         trans.callback.call(trans.scope||window, null, trans.arg, false);
12833     }
12834 });/*
12835  * Based on:
12836  * Ext JS Library 1.1.1
12837  * Copyright(c) 2006-2007, Ext JS, LLC.
12838  *
12839  * Originally Released Under LGPL - original licence link has changed is not relivant.
12840  *
12841  * Fork - LGPL
12842  * <script type="text/javascript">
12843  */
12844
12845 /**
12846  * @class Roo.data.JsonReader
12847  * @extends Roo.data.DataReader
12848  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12849  * based on mappings in a provided Roo.data.Record constructor.
12850  * 
12851  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12852  * in the reply previously. 
12853  * 
12854  * <p>
12855  * Example code:
12856  * <pre><code>
12857 var RecordDef = Roo.data.Record.create([
12858     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12859     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12860 ]);
12861 var myReader = new Roo.data.JsonReader({
12862     totalProperty: "results",    // The property which contains the total dataset size (optional)
12863     root: "rows",                // The property which contains an Array of row objects
12864     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12865 }, RecordDef);
12866 </code></pre>
12867  * <p>
12868  * This would consume a JSON file like this:
12869  * <pre><code>
12870 { 'results': 2, 'rows': [
12871     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12872     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12873 }
12874 </code></pre>
12875  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12876  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12877  * paged from the remote server.
12878  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12879  * @cfg {String} root name of the property which contains the Array of row objects.
12880  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12881  * @cfg {Array} fields Array of field definition objects
12882  * @constructor
12883  * Create a new JsonReader
12884  * @param {Object} meta Metadata configuration options
12885  * @param {Object} recordType Either an Array of field definition objects,
12886  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12887  */
12888 Roo.data.JsonReader = function(meta, recordType){
12889     
12890     meta = meta || {};
12891     // set some defaults:
12892     Roo.applyIf(meta, {
12893         totalProperty: 'total',
12894         successProperty : 'success',
12895         root : 'data',
12896         id : 'id'
12897     });
12898     
12899     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12900 };
12901 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12902     
12903     /**
12904      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12905      * Used by Store query builder to append _requestMeta to params.
12906      * 
12907      */
12908     metaFromRemote : false,
12909     /**
12910      * This method is only used by a DataProxy which has retrieved data from a remote server.
12911      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12912      * @return {Object} data A data block which is used by an Roo.data.Store object as
12913      * a cache of Roo.data.Records.
12914      */
12915     read : function(response){
12916         var json = response.responseText;
12917        
12918         var o = /* eval:var:o */ eval("("+json+")");
12919         if(!o) {
12920             throw {message: "JsonReader.read: Json object not found"};
12921         }
12922         
12923         if(o.metaData){
12924             
12925             delete this.ef;
12926             this.metaFromRemote = true;
12927             this.meta = o.metaData;
12928             this.recordType = Roo.data.Record.create(o.metaData.fields);
12929             this.onMetaChange(this.meta, this.recordType, o);
12930         }
12931         return this.readRecords(o);
12932     },
12933
12934     // private function a store will implement
12935     onMetaChange : function(meta, recordType, o){
12936
12937     },
12938
12939     /**
12940          * @ignore
12941          */
12942     simpleAccess: function(obj, subsc) {
12943         return obj[subsc];
12944     },
12945
12946         /**
12947          * @ignore
12948          */
12949     getJsonAccessor: function(){
12950         var re = /[\[\.]/;
12951         return function(expr) {
12952             try {
12953                 return(re.test(expr))
12954                     ? new Function("obj", "return obj." + expr)
12955                     : function(obj){
12956                         return obj[expr];
12957                     };
12958             } catch(e){}
12959             return Roo.emptyFn;
12960         };
12961     }(),
12962
12963     /**
12964      * Create a data block containing Roo.data.Records from an XML document.
12965      * @param {Object} o An object which contains an Array of row objects in the property specified
12966      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12967      * which contains the total size of the dataset.
12968      * @return {Object} data A data block which is used by an Roo.data.Store object as
12969      * a cache of Roo.data.Records.
12970      */
12971     readRecords : function(o){
12972         /**
12973          * After any data loads, the raw JSON data is available for further custom processing.
12974          * @type Object
12975          */
12976         this.o = o;
12977         var s = this.meta, Record = this.recordType,
12978             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12979
12980 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12981         if (!this.ef) {
12982             if(s.totalProperty) {
12983                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12984                 }
12985                 if(s.successProperty) {
12986                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12987                 }
12988                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12989                 if (s.id) {
12990                         var g = this.getJsonAccessor(s.id);
12991                         this.getId = function(rec) {
12992                                 var r = g(rec);  
12993                                 return (r === undefined || r === "") ? null : r;
12994                         };
12995                 } else {
12996                         this.getId = function(){return null;};
12997                 }
12998             this.ef = [];
12999             for(var jj = 0; jj < fl; jj++){
13000                 f = fi[jj];
13001                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13002                 this.ef[jj] = this.getJsonAccessor(map);
13003             }
13004         }
13005
13006         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13007         if(s.totalProperty){
13008             var vt = parseInt(this.getTotal(o), 10);
13009             if(!isNaN(vt)){
13010                 totalRecords = vt;
13011             }
13012         }
13013         if(s.successProperty){
13014             var vs = this.getSuccess(o);
13015             if(vs === false || vs === 'false'){
13016                 success = false;
13017             }
13018         }
13019         var records = [];
13020         for(var i = 0; i < c; i++){
13021                 var n = root[i];
13022             var values = {};
13023             var id = this.getId(n);
13024             for(var j = 0; j < fl; j++){
13025                 f = fi[j];
13026             var v = this.ef[j](n);
13027             if (!f.convert) {
13028                 Roo.log('missing convert for ' + f.name);
13029                 Roo.log(f);
13030                 continue;
13031             }
13032             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13033             }
13034             var record = new Record(values, id);
13035             record.json = n;
13036             records[i] = record;
13037         }
13038         return {
13039             raw : o,
13040             success : success,
13041             records : records,
13042             totalRecords : totalRecords
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      
13096     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13097 };
13098
13099 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
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         var sid = this.meta ? this.meta.id : null;
13108         var recordType = this.recordType, fields = recordType.prototype.fields;
13109         var records = [];
13110         var root = o;
13111             for(var i = 0; i < root.length; i++){
13112                     var n = root[i];
13113                 var values = {};
13114                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13115                 for(var j = 0, jlen = fields.length; j < jlen; j++){
13116                 var f = fields.items[j];
13117                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13118                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13119                 v = f.convert(v);
13120                 values[f.name] = v;
13121             }
13122                 var record = new recordType(values, id);
13123                 record.json = n;
13124                 records[records.length] = record;
13125             }
13126             return {
13127                 records : records,
13128                 totalRecords : records.length
13129             };
13130     }
13131 });/*
13132  * - LGPL
13133  * * 
13134  */
13135
13136 /**
13137  * @class Roo.bootstrap.ComboBox
13138  * @extends Roo.bootstrap.TriggerField
13139  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13140  * @cfg {Boolean} append (true|false) default false
13141  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13142  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13143  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13144  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13145  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13146  * @cfg {Boolean} animate default true
13147  * @cfg {Boolean} emptyResultText only for touch device
13148  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13149  * @cfg {String} emptyTitle default ''
13150  * @constructor
13151  * Create a new ComboBox.
13152  * @param {Object} config Configuration options
13153  */
13154 Roo.bootstrap.ComboBox = function(config){
13155     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13156     this.addEvents({
13157         /**
13158          * @event expand
13159          * Fires when the dropdown list is expanded
13160         * @param {Roo.bootstrap.ComboBox} combo This combo box
13161         */
13162         'expand' : true,
13163         /**
13164          * @event collapse
13165          * Fires when the dropdown list is collapsed
13166         * @param {Roo.bootstrap.ComboBox} combo This combo box
13167         */
13168         'collapse' : true,
13169         /**
13170          * @event beforeselect
13171          * Fires before a list item is selected. Return false to cancel the selection.
13172         * @param {Roo.bootstrap.ComboBox} combo This combo box
13173         * @param {Roo.data.Record} record The data record returned from the underlying store
13174         * @param {Number} index The index of the selected item in the dropdown list
13175         */
13176         'beforeselect' : true,
13177         /**
13178          * @event select
13179          * Fires when a list item is selected
13180         * @param {Roo.bootstrap.ComboBox} combo This combo box
13181         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13182         * @param {Number} index The index of the selected item in the dropdown list
13183         */
13184         'select' : true,
13185         /**
13186          * @event beforequery
13187          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13188          * The event object passed has these properties:
13189         * @param {Roo.bootstrap.ComboBox} combo This combo box
13190         * @param {String} query The query
13191         * @param {Boolean} forceAll true to force "all" query
13192         * @param {Boolean} cancel true to cancel the query
13193         * @param {Object} e The query event object
13194         */
13195         'beforequery': true,
13196          /**
13197          * @event add
13198          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13199         * @param {Roo.bootstrap.ComboBox} combo This combo box
13200         */
13201         'add' : true,
13202         /**
13203          * @event edit
13204          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13205         * @param {Roo.bootstrap.ComboBox} combo This combo box
13206         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13207         */
13208         'edit' : true,
13209         /**
13210          * @event remove
13211          * Fires when the remove value from the combobox array
13212         * @param {Roo.bootstrap.ComboBox} combo This combo box
13213         */
13214         'remove' : true,
13215         /**
13216          * @event afterremove
13217          * Fires when the remove value from the combobox array
13218         * @param {Roo.bootstrap.ComboBox} combo This combo box
13219         */
13220         'afterremove' : true,
13221         /**
13222          * @event specialfilter
13223          * Fires when specialfilter
13224             * @param {Roo.bootstrap.ComboBox} combo This combo box
13225             */
13226         'specialfilter' : true,
13227         /**
13228          * @event tick
13229          * Fires when tick the element
13230             * @param {Roo.bootstrap.ComboBox} combo This combo box
13231             */
13232         'tick' : true,
13233         /**
13234          * @event touchviewdisplay
13235          * Fires when touch view require special display (default is using displayField)
13236             * @param {Roo.bootstrap.ComboBox} combo This combo box
13237             * @param {Object} cfg set html .
13238             */
13239         'touchviewdisplay' : true
13240         
13241     });
13242     
13243     this.item = [];
13244     this.tickItems = [];
13245     
13246     this.selectedIndex = -1;
13247     if(this.mode == 'local'){
13248         if(config.queryDelay === undefined){
13249             this.queryDelay = 10;
13250         }
13251         if(config.minChars === undefined){
13252             this.minChars = 0;
13253         }
13254     }
13255 };
13256
13257 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13258      
13259     /**
13260      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13261      * rendering into an Roo.Editor, defaults to false)
13262      */
13263     /**
13264      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13265      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13266      */
13267     /**
13268      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13269      */
13270     /**
13271      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13272      * the dropdown list (defaults to undefined, with no header element)
13273      */
13274
13275      /**
13276      * @cfg {String/Roo.Template} tpl The template to use to render the output
13277      */
13278      
13279      /**
13280      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13281      */
13282     listWidth: undefined,
13283     /**
13284      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13285      * mode = 'remote' or 'text' if mode = 'local')
13286      */
13287     displayField: undefined,
13288     
13289     /**
13290      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13291      * mode = 'remote' or 'value' if mode = 'local'). 
13292      * Note: use of a valueField requires the user make a selection
13293      * in order for a value to be mapped.
13294      */
13295     valueField: undefined,
13296     /**
13297      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13298      */
13299     modalTitle : '',
13300     
13301     /**
13302      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13303      * field's data value (defaults to the underlying DOM element's name)
13304      */
13305     hiddenName: undefined,
13306     /**
13307      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13308      */
13309     listClass: '',
13310     /**
13311      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13312      */
13313     selectedClass: 'active',
13314     
13315     /**
13316      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13317      */
13318     shadow:'sides',
13319     /**
13320      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13321      * anchor positions (defaults to 'tl-bl')
13322      */
13323     listAlign: 'tl-bl?',
13324     /**
13325      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13326      */
13327     maxHeight: 300,
13328     /**
13329      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13330      * query specified by the allQuery config option (defaults to 'query')
13331      */
13332     triggerAction: 'query',
13333     /**
13334      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13335      * (defaults to 4, does not apply if editable = false)
13336      */
13337     minChars : 4,
13338     /**
13339      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13340      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13341      */
13342     typeAhead: false,
13343     /**
13344      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13345      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13346      */
13347     queryDelay: 500,
13348     /**
13349      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13350      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13351      */
13352     pageSize: 0,
13353     /**
13354      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13355      * when editable = true (defaults to false)
13356      */
13357     selectOnFocus:false,
13358     /**
13359      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13360      */
13361     queryParam: 'query',
13362     /**
13363      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13364      * when mode = 'remote' (defaults to 'Loading...')
13365      */
13366     loadingText: 'Loading...',
13367     /**
13368      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13369      */
13370     resizable: false,
13371     /**
13372      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13373      */
13374     handleHeight : 8,
13375     /**
13376      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13377      * traditional select (defaults to true)
13378      */
13379     editable: true,
13380     /**
13381      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13382      */
13383     allQuery: '',
13384     /**
13385      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13386      */
13387     mode: 'remote',
13388     /**
13389      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13390      * listWidth has a higher value)
13391      */
13392     minListWidth : 70,
13393     /**
13394      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13395      * allow the user to set arbitrary text into the field (defaults to false)
13396      */
13397     forceSelection:false,
13398     /**
13399      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13400      * if typeAhead = true (defaults to 250)
13401      */
13402     typeAheadDelay : 250,
13403     /**
13404      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13405      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13406      */
13407     valueNotFoundText : undefined,
13408     /**
13409      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13410      */
13411     blockFocus : false,
13412     
13413     /**
13414      * @cfg {Boolean} disableClear Disable showing of clear button.
13415      */
13416     disableClear : false,
13417     /**
13418      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13419      */
13420     alwaysQuery : false,
13421     
13422     /**
13423      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13424      */
13425     multiple : false,
13426     
13427     /**
13428      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13429      */
13430     invalidClass : "has-warning",
13431     
13432     /**
13433      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13434      */
13435     validClass : "has-success",
13436     
13437     /**
13438      * @cfg {Boolean} specialFilter (true|false) special filter default false
13439      */
13440     specialFilter : false,
13441     
13442     /**
13443      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13444      */
13445     mobileTouchView : true,
13446     
13447     /**
13448      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13449      */
13450     useNativeIOS : false,
13451     
13452     /**
13453      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13454      */
13455     mobile_restrict_height : false,
13456     
13457     ios_options : false,
13458     
13459     //private
13460     addicon : false,
13461     editicon: false,
13462     
13463     page: 0,
13464     hasQuery: false,
13465     append: false,
13466     loadNext: false,
13467     autoFocus : true,
13468     tickable : false,
13469     btnPosition : 'right',
13470     triggerList : true,
13471     showToggleBtn : true,
13472     animate : true,
13473     emptyResultText: 'Empty',
13474     triggerText : 'Select',
13475     emptyTitle : '',
13476     
13477     // element that contains real text value.. (when hidden is used..)
13478     
13479     getAutoCreate : function()
13480     {   
13481         var cfg = false;
13482         //render
13483         /*
13484          * Render classic select for iso
13485          */
13486         
13487         if(Roo.isIOS && this.useNativeIOS){
13488             cfg = this.getAutoCreateNativeIOS();
13489             return cfg;
13490         }
13491         
13492         /*
13493          * Touch Devices
13494          */
13495         
13496         if(Roo.isTouch && this.mobileTouchView){
13497             cfg = this.getAutoCreateTouchView();
13498             return cfg;;
13499         }
13500         
13501         /*
13502          *  Normal ComboBox
13503          */
13504         if(!this.tickable){
13505             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13506             return cfg;
13507         }
13508         
13509         /*
13510          *  ComboBox with tickable selections
13511          */
13512              
13513         var align = this.labelAlign || this.parentLabelAlign();
13514         
13515         cfg = {
13516             cls : 'form-group roo-combobox-tickable' //input-group
13517         };
13518         
13519         var btn_text_select = '';
13520         var btn_text_done = '';
13521         var btn_text_cancel = '';
13522         
13523         if (this.btn_text_show) {
13524             btn_text_select = 'Select';
13525             btn_text_done = 'Done';
13526             btn_text_cancel = 'Cancel'; 
13527         }
13528         
13529         var buttons = {
13530             tag : 'div',
13531             cls : 'tickable-buttons',
13532             cn : [
13533                 {
13534                     tag : 'button',
13535                     type : 'button',
13536                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13537                     //html : this.triggerText
13538                     html: btn_text_select
13539                 },
13540                 {
13541                     tag : 'button',
13542                     type : 'button',
13543                     name : 'ok',
13544                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13545                     //html : 'Done'
13546                     html: btn_text_done
13547                 },
13548                 {
13549                     tag : 'button',
13550                     type : 'button',
13551                     name : 'cancel',
13552                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13553                     //html : 'Cancel'
13554                     html: btn_text_cancel
13555                 }
13556             ]
13557         };
13558         
13559         if(this.editable){
13560             buttons.cn.unshift({
13561                 tag: 'input',
13562                 cls: 'roo-select2-search-field-input'
13563             });
13564         }
13565         
13566         var _this = this;
13567         
13568         Roo.each(buttons.cn, function(c){
13569             if (_this.size) {
13570                 c.cls += ' btn-' + _this.size;
13571             }
13572
13573             if (_this.disabled) {
13574                 c.disabled = true;
13575             }
13576         });
13577         
13578         var box = {
13579             tag: 'div',
13580             style : 'display: contents',
13581             cn: [
13582                 {
13583                     tag: 'input',
13584                     type : 'hidden',
13585                     cls: 'form-hidden-field'
13586                 },
13587                 {
13588                     tag: 'ul',
13589                     cls: 'roo-select2-choices',
13590                     cn:[
13591                         {
13592                             tag: 'li',
13593                             cls: 'roo-select2-search-field',
13594                             cn: [
13595                                 buttons
13596                             ]
13597                         }
13598                     ]
13599                 }
13600             ]
13601         };
13602         
13603         var combobox = {
13604             cls: 'roo-select2-container input-group roo-select2-container-multi',
13605             cn: [
13606                 
13607                 box
13608 //                {
13609 //                    tag: 'ul',
13610 //                    cls: 'typeahead typeahead-long dropdown-menu',
13611 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13612 //                }
13613             ]
13614         };
13615         
13616         if(this.hasFeedback && !this.allowBlank){
13617             
13618             var feedback = {
13619                 tag: 'span',
13620                 cls: 'glyphicon form-control-feedback'
13621             };
13622
13623             combobox.cn.push(feedback);
13624         }
13625         
13626         var indicator = {
13627             tag : 'i',
13628             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13629             tooltip : 'This field is required'
13630         };
13631         if (Roo.bootstrap.version == 4) {
13632             indicator = {
13633                 tag : 'i',
13634                 style : 'display:none'
13635             };
13636         }
13637         if (align ==='left' && this.fieldLabel.length) {
13638             
13639             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13640             
13641             cfg.cn = [
13642                 indicator,
13643                 {
13644                     tag: 'label',
13645                     'for' :  id,
13646                     cls : 'control-label col-form-label',
13647                     html : this.fieldLabel
13648
13649                 },
13650                 {
13651                     cls : "", 
13652                     cn: [
13653                         combobox
13654                     ]
13655                 }
13656
13657             ];
13658             
13659             var labelCfg = cfg.cn[1];
13660             var contentCfg = cfg.cn[2];
13661             
13662
13663             if(this.indicatorpos == 'right'){
13664                 
13665                 cfg.cn = [
13666                     {
13667                         tag: 'label',
13668                         'for' :  id,
13669                         cls : 'control-label col-form-label',
13670                         cn : [
13671                             {
13672                                 tag : 'span',
13673                                 html : this.fieldLabel
13674                             },
13675                             indicator
13676                         ]
13677                     },
13678                     {
13679                         cls : "",
13680                         cn: [
13681                             combobox
13682                         ]
13683                     }
13684
13685                 ];
13686                 
13687                 
13688                 
13689                 labelCfg = cfg.cn[0];
13690                 contentCfg = cfg.cn[1];
13691             
13692             }
13693             
13694             if(this.labelWidth > 12){
13695                 labelCfg.style = "width: " + this.labelWidth + 'px';
13696             }
13697             
13698             if(this.labelWidth < 13 && this.labelmd == 0){
13699                 this.labelmd = this.labelWidth;
13700             }
13701             
13702             if(this.labellg > 0){
13703                 labelCfg.cls += ' col-lg-' + this.labellg;
13704                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13705             }
13706             
13707             if(this.labelmd > 0){
13708                 labelCfg.cls += ' col-md-' + this.labelmd;
13709                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13710             }
13711             
13712             if(this.labelsm > 0){
13713                 labelCfg.cls += ' col-sm-' + this.labelsm;
13714                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13715             }
13716             
13717             if(this.labelxs > 0){
13718                 labelCfg.cls += ' col-xs-' + this.labelxs;
13719                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13720             }
13721                 
13722                 
13723         } else if ( this.fieldLabel.length) {
13724 //                Roo.log(" label");
13725                  cfg.cn = [
13726                    indicator,
13727                     {
13728                         tag: 'label',
13729                         //cls : 'input-group-addon',
13730                         html : this.fieldLabel
13731                     },
13732                     combobox
13733                 ];
13734                 
13735                 if(this.indicatorpos == 'right'){
13736                     cfg.cn = [
13737                         {
13738                             tag: 'label',
13739                             //cls : 'input-group-addon',
13740                             html : this.fieldLabel
13741                         },
13742                         indicator,
13743                         combobox
13744                     ];
13745                     
13746                 }
13747
13748         } else {
13749             
13750 //                Roo.log(" no label && no align");
13751                 cfg = combobox
13752                      
13753                 
13754         }
13755          
13756         var settings=this;
13757         ['xs','sm','md','lg'].map(function(size){
13758             if (settings[size]) {
13759                 cfg.cls += ' col-' + size + '-' + settings[size];
13760             }
13761         });
13762         
13763         return cfg;
13764         
13765     },
13766     
13767     _initEventsCalled : false,
13768     
13769     // private
13770     initEvents: function()
13771     {   
13772         if (this._initEventsCalled) { // as we call render... prevent looping...
13773             return;
13774         }
13775         this._initEventsCalled = true;
13776         
13777         if (!this.store) {
13778             throw "can not find store for combo";
13779         }
13780         
13781         this.indicator = this.indicatorEl();
13782         
13783         this.store = Roo.factory(this.store, Roo.data);
13784         this.store.parent = this;
13785         
13786         // if we are building from html. then this element is so complex, that we can not really
13787         // use the rendered HTML.
13788         // so we have to trash and replace the previous code.
13789         if (Roo.XComponent.build_from_html) {
13790             // remove this element....
13791             var e = this.el.dom, k=0;
13792             while (e ) { e = e.previousSibling;  ++k;}
13793
13794             this.el.remove();
13795             
13796             this.el=false;
13797             this.rendered = false;
13798             
13799             this.render(this.parent().getChildContainer(true), k);
13800         }
13801         
13802         if(Roo.isIOS && this.useNativeIOS){
13803             this.initIOSView();
13804             return;
13805         }
13806         
13807         /*
13808          * Touch Devices
13809          */
13810         
13811         if(Roo.isTouch && this.mobileTouchView){
13812             this.initTouchView();
13813             return;
13814         }
13815         
13816         if(this.tickable){
13817             this.initTickableEvents();
13818             return;
13819         }
13820         
13821         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13822         
13823         if(this.hiddenName){
13824             
13825             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13826             
13827             this.hiddenField.dom.value =
13828                 this.hiddenValue !== undefined ? this.hiddenValue :
13829                 this.value !== undefined ? this.value : '';
13830
13831             // prevent input submission
13832             this.el.dom.removeAttribute('name');
13833             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13834              
13835              
13836         }
13837         //if(Roo.isGecko){
13838         //    this.el.dom.setAttribute('autocomplete', 'off');
13839         //}
13840         
13841         var cls = 'x-combo-list';
13842         
13843         //this.list = new Roo.Layer({
13844         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13845         //});
13846         
13847         var _this = this;
13848         
13849         (function(){
13850             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13851             _this.list.setWidth(lw);
13852         }).defer(100);
13853         
13854         this.list.on('mouseover', this.onViewOver, this);
13855         this.list.on('mousemove', this.onViewMove, this);
13856         this.list.on('scroll', this.onViewScroll, this);
13857         
13858         /*
13859         this.list.swallowEvent('mousewheel');
13860         this.assetHeight = 0;
13861
13862         if(this.title){
13863             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13864             this.assetHeight += this.header.getHeight();
13865         }
13866
13867         this.innerList = this.list.createChild({cls:cls+'-inner'});
13868         this.innerList.on('mouseover', this.onViewOver, this);
13869         this.innerList.on('mousemove', this.onViewMove, this);
13870         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13871         
13872         if(this.allowBlank && !this.pageSize && !this.disableClear){
13873             this.footer = this.list.createChild({cls:cls+'-ft'});
13874             this.pageTb = new Roo.Toolbar(this.footer);
13875            
13876         }
13877         if(this.pageSize){
13878             this.footer = this.list.createChild({cls:cls+'-ft'});
13879             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13880                     {pageSize: this.pageSize});
13881             
13882         }
13883         
13884         if (this.pageTb && this.allowBlank && !this.disableClear) {
13885             var _this = this;
13886             this.pageTb.add(new Roo.Toolbar.Fill(), {
13887                 cls: 'x-btn-icon x-btn-clear',
13888                 text: '&#160;',
13889                 handler: function()
13890                 {
13891                     _this.collapse();
13892                     _this.clearValue();
13893                     _this.onSelect(false, -1);
13894                 }
13895             });
13896         }
13897         if (this.footer) {
13898             this.assetHeight += this.footer.getHeight();
13899         }
13900         */
13901             
13902         if(!this.tpl){
13903             this.tpl = Roo.bootstrap.version == 4 ?
13904                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13905                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13906         }
13907
13908         this.view = new Roo.View(this.list, this.tpl, {
13909             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13910         });
13911         //this.view.wrapEl.setDisplayed(false);
13912         this.view.on('click', this.onViewClick, this);
13913         
13914         
13915         this.store.on('beforeload', this.onBeforeLoad, this);
13916         this.store.on('load', this.onLoad, this);
13917         this.store.on('loadexception', this.onLoadException, this);
13918         /*
13919         if(this.resizable){
13920             this.resizer = new Roo.Resizable(this.list,  {
13921                pinned:true, handles:'se'
13922             });
13923             this.resizer.on('resize', function(r, w, h){
13924                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13925                 this.listWidth = w;
13926                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13927                 this.restrictHeight();
13928             }, this);
13929             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13930         }
13931         */
13932         if(!this.editable){
13933             this.editable = true;
13934             this.setEditable(false);
13935         }
13936         
13937         /*
13938         
13939         if (typeof(this.events.add.listeners) != 'undefined') {
13940             
13941             this.addicon = this.wrap.createChild(
13942                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13943        
13944             this.addicon.on('click', function(e) {
13945                 this.fireEvent('add', this);
13946             }, this);
13947         }
13948         if (typeof(this.events.edit.listeners) != 'undefined') {
13949             
13950             this.editicon = this.wrap.createChild(
13951                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13952             if (this.addicon) {
13953                 this.editicon.setStyle('margin-left', '40px');
13954             }
13955             this.editicon.on('click', function(e) {
13956                 
13957                 // we fire even  if inothing is selected..
13958                 this.fireEvent('edit', this, this.lastData );
13959                 
13960             }, this);
13961         }
13962         */
13963         
13964         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13965             "up" : function(e){
13966                 this.inKeyMode = true;
13967                 this.selectPrev();
13968             },
13969
13970             "down" : function(e){
13971                 if(!this.isExpanded()){
13972                     this.onTriggerClick();
13973                 }else{
13974                     this.inKeyMode = true;
13975                     this.selectNext();
13976                 }
13977             },
13978
13979             "enter" : function(e){
13980 //                this.onViewClick();
13981                 //return true;
13982                 this.collapse();
13983                 
13984                 if(this.fireEvent("specialkey", this, e)){
13985                     this.onViewClick(false);
13986                 }
13987                 
13988                 return true;
13989             },
13990
13991             "esc" : function(e){
13992                 this.collapse();
13993             },
13994
13995             "tab" : function(e){
13996                 this.collapse();
13997                 
13998                 if(this.fireEvent("specialkey", this, e)){
13999                     this.onViewClick(false);
14000                 }
14001                 
14002                 return true;
14003             },
14004
14005             scope : this,
14006
14007             doRelay : function(foo, bar, hname){
14008                 if(hname == 'down' || this.scope.isExpanded()){
14009                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14010                 }
14011                 return true;
14012             },
14013
14014             forceKeyDown: true
14015         });
14016         
14017         
14018         this.queryDelay = Math.max(this.queryDelay || 10,
14019                 this.mode == 'local' ? 10 : 250);
14020         
14021         
14022         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14023         
14024         if(this.typeAhead){
14025             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14026         }
14027         if(this.editable !== false){
14028             this.inputEl().on("keyup", this.onKeyUp, this);
14029         }
14030         if(this.forceSelection){
14031             this.inputEl().on('blur', this.doForce, this);
14032         }
14033         
14034         if(this.multiple){
14035             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14036             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14037         }
14038     },
14039     
14040     initTickableEvents: function()
14041     {   
14042         this.createList();
14043         
14044         if(this.hiddenName){
14045             
14046             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14047             
14048             this.hiddenField.dom.value =
14049                 this.hiddenValue !== undefined ? this.hiddenValue :
14050                 this.value !== undefined ? this.value : '';
14051
14052             // prevent input submission
14053             this.el.dom.removeAttribute('name');
14054             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14055              
14056              
14057         }
14058         
14059 //        this.list = this.el.select('ul.dropdown-menu',true).first();
14060         
14061         this.choices = this.el.select('ul.roo-select2-choices', true).first();
14062         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14063         if(this.triggerList){
14064             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14065         }
14066          
14067         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14068         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14069         
14070         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14071         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14072         
14073         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14074         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14075         
14076         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14077         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14078         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14079         
14080         this.okBtn.hide();
14081         this.cancelBtn.hide();
14082         
14083         var _this = this;
14084         
14085         (function(){
14086             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14087             _this.list.setWidth(lw);
14088         }).defer(100);
14089         
14090         this.list.on('mouseover', this.onViewOver, this);
14091         this.list.on('mousemove', this.onViewMove, this);
14092         
14093         this.list.on('scroll', this.onViewScroll, this);
14094         
14095         if(!this.tpl){
14096             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14097                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14098         }
14099
14100         this.view = new Roo.View(this.list, this.tpl, {
14101             singleSelect:true,
14102             tickable:true,
14103             parent:this,
14104             store: this.store,
14105             selectedClass: this.selectedClass
14106         });
14107         
14108         //this.view.wrapEl.setDisplayed(false);
14109         this.view.on('click', this.onViewClick, this);
14110         
14111         
14112         
14113         this.store.on('beforeload', this.onBeforeLoad, this);
14114         this.store.on('load', this.onLoad, this);
14115         this.store.on('loadexception', this.onLoadException, this);
14116         
14117         if(this.editable){
14118             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14119                 "up" : function(e){
14120                     this.inKeyMode = true;
14121                     this.selectPrev();
14122                 },
14123
14124                 "down" : function(e){
14125                     this.inKeyMode = true;
14126                     this.selectNext();
14127                 },
14128
14129                 "enter" : function(e){
14130                     if(this.fireEvent("specialkey", this, e)){
14131                         this.onViewClick(false);
14132                     }
14133                     
14134                     return true;
14135                 },
14136
14137                 "esc" : function(e){
14138                     this.onTickableFooterButtonClick(e, false, false);
14139                 },
14140
14141                 "tab" : function(e){
14142                     this.fireEvent("specialkey", this, e);
14143                     
14144                     this.onTickableFooterButtonClick(e, false, false);
14145                     
14146                     return true;
14147                 },
14148
14149                 scope : this,
14150
14151                 doRelay : function(e, fn, key){
14152                     if(this.scope.isExpanded()){
14153                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14154                     }
14155                     return true;
14156                 },
14157
14158                 forceKeyDown: true
14159             });
14160         }
14161         
14162         this.queryDelay = Math.max(this.queryDelay || 10,
14163                 this.mode == 'local' ? 10 : 250);
14164         
14165         
14166         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14167         
14168         if(this.typeAhead){
14169             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14170         }
14171         
14172         if(this.editable !== false){
14173             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14174         }
14175         
14176         this.indicator = this.indicatorEl();
14177         
14178         if(this.indicator){
14179             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14180             this.indicator.hide();
14181         }
14182         
14183     },
14184
14185     onDestroy : function(){
14186         if(this.view){
14187             this.view.setStore(null);
14188             this.view.el.removeAllListeners();
14189             this.view.el.remove();
14190             this.view.purgeListeners();
14191         }
14192         if(this.list){
14193             this.list.dom.innerHTML  = '';
14194         }
14195         
14196         if(this.store){
14197             this.store.un('beforeload', this.onBeforeLoad, this);
14198             this.store.un('load', this.onLoad, this);
14199             this.store.un('loadexception', this.onLoadException, this);
14200         }
14201         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14202     },
14203
14204     // private
14205     fireKey : function(e){
14206         if(e.isNavKeyPress() && !this.list.isVisible()){
14207             this.fireEvent("specialkey", this, e);
14208         }
14209     },
14210
14211     // private
14212     onResize: function(w, h){
14213 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14214 //        
14215 //        if(typeof w != 'number'){
14216 //            // we do not handle it!?!?
14217 //            return;
14218 //        }
14219 //        var tw = this.trigger.getWidth();
14220 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14221 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14222 //        var x = w - tw;
14223 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14224 //            
14225 //        //this.trigger.setStyle('left', x+'px');
14226 //        
14227 //        if(this.list && this.listWidth === undefined){
14228 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14229 //            this.list.setWidth(lw);
14230 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14231 //        }
14232         
14233     
14234         
14235     },
14236
14237     /**
14238      * Allow or prevent the user from directly editing the field text.  If false is passed,
14239      * the user will only be able to select from the items defined in the dropdown list.  This method
14240      * is the runtime equivalent of setting the 'editable' config option at config time.
14241      * @param {Boolean} value True to allow the user to directly edit the field text
14242      */
14243     setEditable : function(value){
14244         if(value == this.editable){
14245             return;
14246         }
14247         this.editable = value;
14248         if(!value){
14249             this.inputEl().dom.setAttribute('readOnly', true);
14250             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14251             this.inputEl().addClass('x-combo-noedit');
14252         }else{
14253             this.inputEl().dom.setAttribute('readOnly', false);
14254             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14255             this.inputEl().removeClass('x-combo-noedit');
14256         }
14257     },
14258
14259     // private
14260     
14261     onBeforeLoad : function(combo,opts){
14262         if(!this.hasFocus){
14263             return;
14264         }
14265          if (!opts.add) {
14266             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14267          }
14268         this.restrictHeight();
14269         this.selectedIndex = -1;
14270     },
14271
14272     // private
14273     onLoad : function(){
14274         
14275         this.hasQuery = false;
14276         
14277         if(!this.hasFocus){
14278             return;
14279         }
14280         
14281         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14282             this.loading.hide();
14283         }
14284         
14285         if(this.store.getCount() > 0){
14286             
14287             this.expand();
14288             this.restrictHeight();
14289             if(this.lastQuery == this.allQuery){
14290                 if(this.editable && !this.tickable){
14291                     this.inputEl().dom.select();
14292                 }
14293                 
14294                 if(
14295                     !this.selectByValue(this.value, true) &&
14296                     this.autoFocus && 
14297                     (
14298                         !this.store.lastOptions ||
14299                         typeof(this.store.lastOptions.add) == 'undefined' || 
14300                         this.store.lastOptions.add != true
14301                     )
14302                 ){
14303                     this.select(0, true);
14304                 }
14305             }else{
14306                 if(this.autoFocus){
14307                     this.selectNext();
14308                 }
14309                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14310                     this.taTask.delay(this.typeAheadDelay);
14311                 }
14312             }
14313         }else{
14314             this.onEmptyResults();
14315         }
14316         
14317         //this.el.focus();
14318     },
14319     // private
14320     onLoadException : function()
14321     {
14322         this.hasQuery = false;
14323         
14324         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14325             this.loading.hide();
14326         }
14327         
14328         if(this.tickable && this.editable){
14329             return;
14330         }
14331         
14332         this.collapse();
14333         // only causes errors at present
14334         //Roo.log(this.store.reader.jsonData);
14335         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14336             // fixme
14337             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14338         //}
14339         
14340         
14341     },
14342     // private
14343     onTypeAhead : function(){
14344         if(this.store.getCount() > 0){
14345             var r = this.store.getAt(0);
14346             var newValue = r.data[this.displayField];
14347             var len = newValue.length;
14348             var selStart = this.getRawValue().length;
14349             
14350             if(selStart != len){
14351                 this.setRawValue(newValue);
14352                 this.selectText(selStart, newValue.length);
14353             }
14354         }
14355     },
14356
14357     // private
14358     onSelect : function(record, index){
14359         
14360         if(this.fireEvent('beforeselect', this, record, index) !== false){
14361         
14362             this.setFromData(index > -1 ? record.data : false);
14363             
14364             this.collapse();
14365             this.fireEvent('select', this, record, index);
14366         }
14367     },
14368
14369     /**
14370      * Returns the currently selected field value or empty string if no value is set.
14371      * @return {String} value The selected value
14372      */
14373     getValue : function()
14374     {
14375         if(Roo.isIOS && this.useNativeIOS){
14376             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14377         }
14378         
14379         if(this.multiple){
14380             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14381         }
14382         
14383         if(this.valueField){
14384             return typeof this.value != 'undefined' ? this.value : '';
14385         }else{
14386             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14387         }
14388     },
14389     
14390     getRawValue : function()
14391     {
14392         if(Roo.isIOS && this.useNativeIOS){
14393             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14394         }
14395         
14396         var v = this.inputEl().getValue();
14397         
14398         return v;
14399     },
14400
14401     /**
14402      * Clears any text/value currently set in the field
14403      */
14404     clearValue : function(){
14405         
14406         if(this.hiddenField){
14407             this.hiddenField.dom.value = '';
14408         }
14409         this.value = '';
14410         this.setRawValue('');
14411         this.lastSelectionText = '';
14412         this.lastData = false;
14413         
14414         var close = this.closeTriggerEl();
14415         
14416         if(close){
14417             close.hide();
14418         }
14419         
14420         this.validate();
14421         
14422     },
14423
14424     /**
14425      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14426      * will be displayed in the field.  If the value does not match the data value of an existing item,
14427      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14428      * Otherwise the field will be blank (although the value will still be set).
14429      * @param {String} value The value to match
14430      */
14431     setValue : function(v)
14432     {
14433         if(Roo.isIOS && this.useNativeIOS){
14434             this.setIOSValue(v);
14435             return;
14436         }
14437         
14438         if(this.multiple){
14439             this.syncValue();
14440             return;
14441         }
14442         
14443         var text = v;
14444         if(this.valueField){
14445             var r = this.findRecord(this.valueField, v);
14446             if(r){
14447                 text = r.data[this.displayField];
14448             }else if(this.valueNotFoundText !== undefined){
14449                 text = this.valueNotFoundText;
14450             }
14451         }
14452         this.lastSelectionText = text;
14453         if(this.hiddenField){
14454             this.hiddenField.dom.value = v;
14455         }
14456         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14457         this.value = v;
14458         
14459         var close = this.closeTriggerEl();
14460         
14461         if(close){
14462             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14463         }
14464         
14465         this.validate();
14466     },
14467     /**
14468      * @property {Object} the last set data for the element
14469      */
14470     
14471     lastData : false,
14472     /**
14473      * Sets the value of the field based on a object which is related to the record format for the store.
14474      * @param {Object} value the value to set as. or false on reset?
14475      */
14476     setFromData : function(o){
14477         
14478         if(this.multiple){
14479             this.addItem(o);
14480             return;
14481         }
14482             
14483         var dv = ''; // display value
14484         var vv = ''; // value value..
14485         this.lastData = o;
14486         if (this.displayField) {
14487             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14488         } else {
14489             // this is an error condition!!!
14490             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14491         }
14492         
14493         if(this.valueField){
14494             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14495         }
14496         
14497         var close = this.closeTriggerEl();
14498         
14499         if(close){
14500             if(dv.length || vv * 1 > 0){
14501                 close.show() ;
14502                 this.blockFocus=true;
14503             } else {
14504                 close.hide();
14505             }             
14506         }
14507         
14508         if(this.hiddenField){
14509             this.hiddenField.dom.value = vv;
14510             
14511             this.lastSelectionText = dv;
14512             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14513             this.value = vv;
14514             return;
14515         }
14516         // no hidden field.. - we store the value in 'value', but still display
14517         // display field!!!!
14518         this.lastSelectionText = dv;
14519         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14520         this.value = vv;
14521         
14522         
14523         
14524     },
14525     // private
14526     reset : function(){
14527         // overridden so that last data is reset..
14528         
14529         if(this.multiple){
14530             this.clearItem();
14531             return;
14532         }
14533         
14534         this.setValue(this.originalValue);
14535         //this.clearInvalid();
14536         this.lastData = false;
14537         if (this.view) {
14538             this.view.clearSelections();
14539         }
14540         
14541         this.validate();
14542     },
14543     // private
14544     findRecord : function(prop, value){
14545         var record;
14546         if(this.store.getCount() > 0){
14547             this.store.each(function(r){
14548                 if(r.data[prop] == value){
14549                     record = r;
14550                     return false;
14551                 }
14552                 return true;
14553             });
14554         }
14555         return record;
14556     },
14557     
14558     getName: function()
14559     {
14560         // returns hidden if it's set..
14561         if (!this.rendered) {return ''};
14562         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14563         
14564     },
14565     // private
14566     onViewMove : function(e, t){
14567         this.inKeyMode = false;
14568     },
14569
14570     // private
14571     onViewOver : function(e, t){
14572         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14573             return;
14574         }
14575         var item = this.view.findItemFromChild(t);
14576         
14577         if(item){
14578             var index = this.view.indexOf(item);
14579             this.select(index, false);
14580         }
14581     },
14582
14583     // private
14584     onViewClick : function(view, doFocus, el, e)
14585     {
14586         var index = this.view.getSelectedIndexes()[0];
14587         
14588         var r = this.store.getAt(index);
14589         
14590         if(this.tickable){
14591             
14592             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14593                 return;
14594             }
14595             
14596             var rm = false;
14597             var _this = this;
14598             
14599             Roo.each(this.tickItems, function(v,k){
14600                 
14601                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14602                     Roo.log(v);
14603                     _this.tickItems.splice(k, 1);
14604                     
14605                     if(typeof(e) == 'undefined' && view == false){
14606                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14607                     }
14608                     
14609                     rm = true;
14610                     return;
14611                 }
14612             });
14613             
14614             if(rm){
14615                 return;
14616             }
14617             
14618             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14619                 this.tickItems.push(r.data);
14620             }
14621             
14622             if(typeof(e) == 'undefined' && view == false){
14623                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14624             }
14625                     
14626             return;
14627         }
14628         
14629         if(r){
14630             this.onSelect(r, index);
14631         }
14632         if(doFocus !== false && !this.blockFocus){
14633             this.inputEl().focus();
14634         }
14635     },
14636
14637     // private
14638     restrictHeight : function(){
14639         //this.innerList.dom.style.height = '';
14640         //var inner = this.innerList.dom;
14641         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14642         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14643         //this.list.beginUpdate();
14644         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14645         this.list.alignTo(this.inputEl(), this.listAlign);
14646         this.list.alignTo(this.inputEl(), this.listAlign);
14647         //this.list.endUpdate();
14648     },
14649
14650     // private
14651     onEmptyResults : function(){
14652         
14653         if(this.tickable && this.editable){
14654             this.hasFocus = false;
14655             this.restrictHeight();
14656             return;
14657         }
14658         
14659         this.collapse();
14660     },
14661
14662     /**
14663      * Returns true if the dropdown list is expanded, else false.
14664      */
14665     isExpanded : function(){
14666         return this.list.isVisible();
14667     },
14668
14669     /**
14670      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14671      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14672      * @param {String} value The data value of the item to select
14673      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14674      * selected item if it is not currently in view (defaults to true)
14675      * @return {Boolean} True if the value matched an item in the list, else false
14676      */
14677     selectByValue : function(v, scrollIntoView){
14678         if(v !== undefined && v !== null){
14679             var r = this.findRecord(this.valueField || this.displayField, v);
14680             if(r){
14681                 this.select(this.store.indexOf(r), scrollIntoView);
14682                 return true;
14683             }
14684         }
14685         return false;
14686     },
14687
14688     /**
14689      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14690      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14691      * @param {Number} index The zero-based index of the list item to select
14692      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14693      * selected item if it is not currently in view (defaults to true)
14694      */
14695     select : function(index, scrollIntoView){
14696         this.selectedIndex = index;
14697         this.view.select(index);
14698         if(scrollIntoView !== false){
14699             var el = this.view.getNode(index);
14700             /*
14701              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14702              */
14703             if(el){
14704                 this.list.scrollChildIntoView(el, false);
14705             }
14706         }
14707     },
14708
14709     // private
14710     selectNext : function(){
14711         var ct = this.store.getCount();
14712         if(ct > 0){
14713             if(this.selectedIndex == -1){
14714                 this.select(0);
14715             }else if(this.selectedIndex < ct-1){
14716                 this.select(this.selectedIndex+1);
14717             }
14718         }
14719     },
14720
14721     // private
14722     selectPrev : function(){
14723         var ct = this.store.getCount();
14724         if(ct > 0){
14725             if(this.selectedIndex == -1){
14726                 this.select(0);
14727             }else if(this.selectedIndex != 0){
14728                 this.select(this.selectedIndex-1);
14729             }
14730         }
14731     },
14732
14733     // private
14734     onKeyUp : function(e){
14735         if(this.editable !== false && !e.isSpecialKey()){
14736             this.lastKey = e.getKey();
14737             this.dqTask.delay(this.queryDelay);
14738         }
14739     },
14740
14741     // private
14742     validateBlur : function(){
14743         return !this.list || !this.list.isVisible();   
14744     },
14745
14746     // private
14747     initQuery : function(){
14748         
14749         var v = this.getRawValue();
14750         
14751         if(this.tickable && this.editable){
14752             v = this.tickableInputEl().getValue();
14753         }
14754         
14755         this.doQuery(v);
14756     },
14757
14758     // private
14759     doForce : function(){
14760         if(this.inputEl().dom.value.length > 0){
14761             this.inputEl().dom.value =
14762                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14763              
14764         }
14765     },
14766
14767     /**
14768      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14769      * query allowing the query action to be canceled if needed.
14770      * @param {String} query The SQL query to execute
14771      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14772      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14773      * saved in the current store (defaults to false)
14774      */
14775     doQuery : function(q, forceAll){
14776         
14777         if(q === undefined || q === null){
14778             q = '';
14779         }
14780         var qe = {
14781             query: q,
14782             forceAll: forceAll,
14783             combo: this,
14784             cancel:false
14785         };
14786         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14787             return false;
14788         }
14789         q = qe.query;
14790         
14791         forceAll = qe.forceAll;
14792         if(forceAll === true || (q.length >= this.minChars)){
14793             
14794             this.hasQuery = true;
14795             
14796             if(this.lastQuery != q || this.alwaysQuery){
14797                 this.lastQuery = q;
14798                 if(this.mode == 'local'){
14799                     this.selectedIndex = -1;
14800                     if(forceAll){
14801                         this.store.clearFilter();
14802                     }else{
14803                         
14804                         if(this.specialFilter){
14805                             this.fireEvent('specialfilter', this);
14806                             this.onLoad();
14807                             return;
14808                         }
14809                         
14810                         this.store.filter(this.displayField, q);
14811                     }
14812                     
14813                     this.store.fireEvent("datachanged", this.store);
14814                     
14815                     this.onLoad();
14816                     
14817                     
14818                 }else{
14819                     
14820                     this.store.baseParams[this.queryParam] = q;
14821                     
14822                     var options = {params : this.getParams(q)};
14823                     
14824                     if(this.loadNext){
14825                         options.add = true;
14826                         options.params.start = this.page * this.pageSize;
14827                     }
14828                     
14829                     this.store.load(options);
14830                     
14831                     /*
14832                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14833                      *  we should expand the list on onLoad
14834                      *  so command out it
14835                      */
14836 //                    this.expand();
14837                 }
14838             }else{
14839                 this.selectedIndex = -1;
14840                 this.onLoad();   
14841             }
14842         }
14843         
14844         this.loadNext = false;
14845     },
14846     
14847     // private
14848     getParams : function(q){
14849         var p = {};
14850         //p[this.queryParam] = q;
14851         
14852         if(this.pageSize){
14853             p.start = 0;
14854             p.limit = this.pageSize;
14855         }
14856         return p;
14857     },
14858
14859     /**
14860      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14861      */
14862     collapse : function(){
14863         if(!this.isExpanded()){
14864             return;
14865         }
14866         
14867         this.list.hide();
14868         
14869         this.hasFocus = false;
14870         
14871         if(this.tickable){
14872             this.okBtn.hide();
14873             this.cancelBtn.hide();
14874             this.trigger.show();
14875             
14876             if(this.editable){
14877                 this.tickableInputEl().dom.value = '';
14878                 this.tickableInputEl().blur();
14879             }
14880             
14881         }
14882         
14883         Roo.get(document).un('mousedown', this.collapseIf, this);
14884         Roo.get(document).un('mousewheel', this.collapseIf, this);
14885         if (!this.editable) {
14886             Roo.get(document).un('keydown', this.listKeyPress, this);
14887         }
14888         this.fireEvent('collapse', this);
14889         
14890         this.validate();
14891     },
14892
14893     // private
14894     collapseIf : function(e){
14895         var in_combo  = e.within(this.el);
14896         var in_list =  e.within(this.list);
14897         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14898         
14899         if (in_combo || in_list || is_list) {
14900             //e.stopPropagation();
14901             return;
14902         }
14903         
14904         if(this.tickable){
14905             this.onTickableFooterButtonClick(e, false, false);
14906         }
14907
14908         this.collapse();
14909         
14910     },
14911
14912     /**
14913      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14914      */
14915     expand : function(){
14916        
14917         if(this.isExpanded() || !this.hasFocus){
14918             return;
14919         }
14920         
14921         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14922         this.list.setWidth(lw);
14923         
14924         Roo.log('expand');
14925         
14926         this.list.show();
14927         
14928         this.restrictHeight();
14929         
14930         if(this.tickable){
14931             
14932             this.tickItems = Roo.apply([], this.item);
14933             
14934             this.okBtn.show();
14935             this.cancelBtn.show();
14936             this.trigger.hide();
14937             
14938             if(this.editable){
14939                 this.tickableInputEl().focus();
14940             }
14941             
14942         }
14943         
14944         Roo.get(document).on('mousedown', this.collapseIf, this);
14945         Roo.get(document).on('mousewheel', this.collapseIf, this);
14946         if (!this.editable) {
14947             Roo.get(document).on('keydown', this.listKeyPress, this);
14948         }
14949         
14950         this.fireEvent('expand', this);
14951     },
14952
14953     // private
14954     // Implements the default empty TriggerField.onTriggerClick function
14955     onTriggerClick : function(e)
14956     {
14957         Roo.log('trigger click');
14958         
14959         if(this.disabled || !this.triggerList){
14960             return;
14961         }
14962         
14963         this.page = 0;
14964         this.loadNext = false;
14965         
14966         if(this.isExpanded()){
14967             this.collapse();
14968             if (!this.blockFocus) {
14969                 this.inputEl().focus();
14970             }
14971             
14972         }else {
14973             this.hasFocus = true;
14974             if(this.triggerAction == 'all') {
14975                 this.doQuery(this.allQuery, true);
14976             } else {
14977                 this.doQuery(this.getRawValue());
14978             }
14979             if (!this.blockFocus) {
14980                 this.inputEl().focus();
14981             }
14982         }
14983     },
14984     
14985     onTickableTriggerClick : function(e)
14986     {
14987         if(this.disabled){
14988             return;
14989         }
14990         
14991         this.page = 0;
14992         this.loadNext = false;
14993         this.hasFocus = true;
14994         
14995         if(this.triggerAction == 'all') {
14996             this.doQuery(this.allQuery, true);
14997         } else {
14998             this.doQuery(this.getRawValue());
14999         }
15000     },
15001     
15002     onSearchFieldClick : function(e)
15003     {
15004         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15005             this.onTickableFooterButtonClick(e, false, false);
15006             return;
15007         }
15008         
15009         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15010             return;
15011         }
15012         
15013         this.page = 0;
15014         this.loadNext = false;
15015         this.hasFocus = true;
15016         
15017         if(this.triggerAction == 'all') {
15018             this.doQuery(this.allQuery, true);
15019         } else {
15020             this.doQuery(this.getRawValue());
15021         }
15022     },
15023     
15024     listKeyPress : function(e)
15025     {
15026         //Roo.log('listkeypress');
15027         // scroll to first matching element based on key pres..
15028         if (e.isSpecialKey()) {
15029             return false;
15030         }
15031         var k = String.fromCharCode(e.getKey()).toUpperCase();
15032         //Roo.log(k);
15033         var match  = false;
15034         var csel = this.view.getSelectedNodes();
15035         var cselitem = false;
15036         if (csel.length) {
15037             var ix = this.view.indexOf(csel[0]);
15038             cselitem  = this.store.getAt(ix);
15039             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15040                 cselitem = false;
15041             }
15042             
15043         }
15044         
15045         this.store.each(function(v) { 
15046             if (cselitem) {
15047                 // start at existing selection.
15048                 if (cselitem.id == v.id) {
15049                     cselitem = false;
15050                 }
15051                 return true;
15052             }
15053                 
15054             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15055                 match = this.store.indexOf(v);
15056                 return false;
15057             }
15058             return true;
15059         }, this);
15060         
15061         if (match === false) {
15062             return true; // no more action?
15063         }
15064         // scroll to?
15065         this.view.select(match);
15066         var sn = Roo.get(this.view.getSelectedNodes()[0]);
15067         sn.scrollIntoView(sn.dom.parentNode, false);
15068     },
15069     
15070     onViewScroll : function(e, t){
15071         
15072         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){
15073             return;
15074         }
15075         
15076         this.hasQuery = true;
15077         
15078         this.loading = this.list.select('.loading', true).first();
15079         
15080         if(this.loading === null){
15081             this.list.createChild({
15082                 tag: 'div',
15083                 cls: 'loading roo-select2-more-results roo-select2-active',
15084                 html: 'Loading more results...'
15085             });
15086             
15087             this.loading = this.list.select('.loading', true).first();
15088             
15089             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15090             
15091             this.loading.hide();
15092         }
15093         
15094         this.loading.show();
15095         
15096         var _combo = this;
15097         
15098         this.page++;
15099         this.loadNext = true;
15100         
15101         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15102         
15103         return;
15104     },
15105     
15106     addItem : function(o)
15107     {   
15108         var dv = ''; // display value
15109         
15110         if (this.displayField) {
15111             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15112         } else {
15113             // this is an error condition!!!
15114             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15115         }
15116         
15117         if(!dv.length){
15118             return;
15119         }
15120         
15121         var choice = this.choices.createChild({
15122             tag: 'li',
15123             cls: 'roo-select2-search-choice',
15124             cn: [
15125                 {
15126                     tag: 'div',
15127                     html: dv
15128                 },
15129                 {
15130                     tag: 'a',
15131                     href: '#',
15132                     cls: 'roo-select2-search-choice-close fa fa-times',
15133                     tabindex: '-1'
15134                 }
15135             ]
15136             
15137         }, this.searchField);
15138         
15139         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15140         
15141         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15142         
15143         this.item.push(o);
15144         
15145         this.lastData = o;
15146         
15147         this.syncValue();
15148         
15149         this.inputEl().dom.value = '';
15150         
15151         this.validate();
15152     },
15153     
15154     onRemoveItem : function(e, _self, o)
15155     {
15156         e.preventDefault();
15157         
15158         this.lastItem = Roo.apply([], this.item);
15159         
15160         var index = this.item.indexOf(o.data) * 1;
15161         
15162         if( index < 0){
15163             Roo.log('not this item?!');
15164             return;
15165         }
15166         
15167         this.item.splice(index, 1);
15168         o.item.remove();
15169         
15170         this.syncValue();
15171         
15172         this.fireEvent('remove', this, e);
15173         
15174         this.validate();
15175         
15176     },
15177     
15178     syncValue : function()
15179     {
15180         if(!this.item.length){
15181             this.clearValue();
15182             return;
15183         }
15184             
15185         var value = [];
15186         var _this = this;
15187         Roo.each(this.item, function(i){
15188             if(_this.valueField){
15189                 value.push(i[_this.valueField]);
15190                 return;
15191             }
15192
15193             value.push(i);
15194         });
15195
15196         this.value = value.join(',');
15197
15198         if(this.hiddenField){
15199             this.hiddenField.dom.value = this.value;
15200         }
15201         
15202         this.store.fireEvent("datachanged", this.store);
15203         
15204         this.validate();
15205     },
15206     
15207     clearItem : function()
15208     {
15209         if(!this.multiple){
15210             return;
15211         }
15212         
15213         this.item = [];
15214         
15215         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15216            c.remove();
15217         });
15218         
15219         this.syncValue();
15220         
15221         this.validate();
15222         
15223         if(this.tickable && !Roo.isTouch){
15224             this.view.refresh();
15225         }
15226     },
15227     
15228     inputEl: function ()
15229     {
15230         if(Roo.isIOS && this.useNativeIOS){
15231             return this.el.select('select.roo-ios-select', true).first();
15232         }
15233         
15234         if(Roo.isTouch && this.mobileTouchView){
15235             return this.el.select('input.form-control',true).first();
15236         }
15237         
15238         if(this.tickable){
15239             return this.searchField;
15240         }
15241         
15242         return this.el.select('input.form-control',true).first();
15243     },
15244     
15245     onTickableFooterButtonClick : function(e, btn, el)
15246     {
15247         e.preventDefault();
15248         
15249         this.lastItem = Roo.apply([], this.item);
15250         
15251         if(btn && btn.name == 'cancel'){
15252             this.tickItems = Roo.apply([], this.item);
15253             this.collapse();
15254             return;
15255         }
15256         
15257         this.clearItem();
15258         
15259         var _this = this;
15260         
15261         Roo.each(this.tickItems, function(o){
15262             _this.addItem(o);
15263         });
15264         
15265         this.collapse();
15266         
15267     },
15268     
15269     validate : function()
15270     {
15271         if(this.getVisibilityEl().hasClass('hidden')){
15272             return true;
15273         }
15274         
15275         var v = this.getRawValue();
15276         
15277         if(this.multiple){
15278             v = this.getValue();
15279         }
15280         
15281         if(this.disabled || this.allowBlank || v.length){
15282             this.markValid();
15283             return true;
15284         }
15285         
15286         this.markInvalid();
15287         return false;
15288     },
15289     
15290     tickableInputEl : function()
15291     {
15292         if(!this.tickable || !this.editable){
15293             return this.inputEl();
15294         }
15295         
15296         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15297     },
15298     
15299     
15300     getAutoCreateTouchView : function()
15301     {
15302         var id = Roo.id();
15303         
15304         var cfg = {
15305             cls: 'form-group' //input-group
15306         };
15307         
15308         var input =  {
15309             tag: 'input',
15310             id : id,
15311             type : this.inputType,
15312             cls : 'form-control x-combo-noedit',
15313             autocomplete: 'new-password',
15314             placeholder : this.placeholder || '',
15315             readonly : true
15316         };
15317         
15318         if (this.name) {
15319             input.name = this.name;
15320         }
15321         
15322         if (this.size) {
15323             input.cls += ' input-' + this.size;
15324         }
15325         
15326         if (this.disabled) {
15327             input.disabled = true;
15328         }
15329         
15330         var inputblock = {
15331             cls : '',
15332             cn : [
15333                 input
15334             ]
15335         };
15336         
15337         if(this.before){
15338             inputblock.cls += ' input-group';
15339             
15340             inputblock.cn.unshift({
15341                 tag :'span',
15342                 cls : 'input-group-addon input-group-prepend input-group-text',
15343                 html : this.before
15344             });
15345         }
15346         
15347         if(this.removable && !this.multiple){
15348             inputblock.cls += ' roo-removable';
15349             
15350             inputblock.cn.push({
15351                 tag: 'button',
15352                 html : 'x',
15353                 cls : 'roo-combo-removable-btn close'
15354             });
15355         }
15356
15357         if(this.hasFeedback && !this.allowBlank){
15358             
15359             inputblock.cls += ' has-feedback';
15360             
15361             inputblock.cn.push({
15362                 tag: 'span',
15363                 cls: 'glyphicon form-control-feedback'
15364             });
15365             
15366         }
15367         
15368         if (this.after) {
15369             
15370             inputblock.cls += (this.before) ? '' : ' input-group';
15371             
15372             inputblock.cn.push({
15373                 tag :'span',
15374                 cls : 'input-group-addon input-group-append input-group-text',
15375                 html : this.after
15376             });
15377         }
15378
15379         
15380         var ibwrap = inputblock;
15381         
15382         if(this.multiple){
15383             ibwrap = {
15384                 tag: 'ul',
15385                 cls: 'roo-select2-choices',
15386                 cn:[
15387                     {
15388                         tag: 'li',
15389                         cls: 'roo-select2-search-field',
15390                         cn: [
15391
15392                             inputblock
15393                         ]
15394                     }
15395                 ]
15396             };
15397         
15398             
15399         }
15400         
15401         var combobox = {
15402             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15403             cn: [
15404                 {
15405                     tag: 'input',
15406                     type : 'hidden',
15407                     cls: 'form-hidden-field'
15408                 },
15409                 ibwrap
15410             ]
15411         };
15412         
15413         if(!this.multiple && this.showToggleBtn){
15414             
15415             var caret = {
15416                 cls: 'caret'
15417             };
15418             
15419             if (this.caret != false) {
15420                 caret = {
15421                      tag: 'i',
15422                      cls: 'fa fa-' + this.caret
15423                 };
15424                 
15425             }
15426             
15427             combobox.cn.push({
15428                 tag :'span',
15429                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15430                 cn : [
15431                     Roo.bootstrap.version == 3 ? caret : '',
15432                     {
15433                         tag: 'span',
15434                         cls: 'combobox-clear',
15435                         cn  : [
15436                             {
15437                                 tag : 'i',
15438                                 cls: 'icon-remove'
15439                             }
15440                         ]
15441                     }
15442                 ]
15443
15444             })
15445         }
15446         
15447         if(this.multiple){
15448             combobox.cls += ' roo-select2-container-multi';
15449         }
15450         
15451         var align = this.labelAlign || this.parentLabelAlign();
15452         
15453         if (align ==='left' && this.fieldLabel.length) {
15454
15455             cfg.cn = [
15456                 {
15457                    tag : 'i',
15458                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15459                    tooltip : 'This field is required'
15460                 },
15461                 {
15462                     tag: 'label',
15463                     cls : 'control-label col-form-label',
15464                     html : this.fieldLabel
15465
15466                 },
15467                 {
15468                     cls : '', 
15469                     cn: [
15470                         combobox
15471                     ]
15472                 }
15473             ];
15474             
15475             var labelCfg = cfg.cn[1];
15476             var contentCfg = cfg.cn[2];
15477             
15478
15479             if(this.indicatorpos == 'right'){
15480                 cfg.cn = [
15481                     {
15482                         tag: 'label',
15483                         'for' :  id,
15484                         cls : 'control-label col-form-label',
15485                         cn : [
15486                             {
15487                                 tag : 'span',
15488                                 html : this.fieldLabel
15489                             },
15490                             {
15491                                 tag : 'i',
15492                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15493                                 tooltip : 'This field is required'
15494                             }
15495                         ]
15496                     },
15497                     {
15498                         cls : "",
15499                         cn: [
15500                             combobox
15501                         ]
15502                     }
15503
15504                 ];
15505                 
15506                 labelCfg = cfg.cn[0];
15507                 contentCfg = cfg.cn[1];
15508             }
15509             
15510            
15511             
15512             if(this.labelWidth > 12){
15513                 labelCfg.style = "width: " + this.labelWidth + 'px';
15514             }
15515             
15516             if(this.labelWidth < 13 && this.labelmd == 0){
15517                 this.labelmd = this.labelWidth;
15518             }
15519             
15520             if(this.labellg > 0){
15521                 labelCfg.cls += ' col-lg-' + this.labellg;
15522                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15523             }
15524             
15525             if(this.labelmd > 0){
15526                 labelCfg.cls += ' col-md-' + this.labelmd;
15527                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15528             }
15529             
15530             if(this.labelsm > 0){
15531                 labelCfg.cls += ' col-sm-' + this.labelsm;
15532                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15533             }
15534             
15535             if(this.labelxs > 0){
15536                 labelCfg.cls += ' col-xs-' + this.labelxs;
15537                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15538             }
15539                 
15540                 
15541         } else if ( this.fieldLabel.length) {
15542             cfg.cn = [
15543                 {
15544                    tag : 'i',
15545                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15546                    tooltip : 'This field is required'
15547                 },
15548                 {
15549                     tag: 'label',
15550                     cls : 'control-label',
15551                     html : this.fieldLabel
15552
15553                 },
15554                 {
15555                     cls : '', 
15556                     cn: [
15557                         combobox
15558                     ]
15559                 }
15560             ];
15561             
15562             if(this.indicatorpos == 'right'){
15563                 cfg.cn = [
15564                     {
15565                         tag: 'label',
15566                         cls : 'control-label',
15567                         html : this.fieldLabel,
15568                         cn : [
15569                             {
15570                                tag : 'i',
15571                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15572                                tooltip : 'This field is required'
15573                             }
15574                         ]
15575                     },
15576                     {
15577                         cls : '', 
15578                         cn: [
15579                             combobox
15580                         ]
15581                     }
15582                 ];
15583             }
15584         } else {
15585             cfg.cn = combobox;    
15586         }
15587         
15588         
15589         var settings = this;
15590         
15591         ['xs','sm','md','lg'].map(function(size){
15592             if (settings[size]) {
15593                 cfg.cls += ' col-' + size + '-' + settings[size];
15594             }
15595         });
15596         
15597         return cfg;
15598     },
15599     
15600     initTouchView : function()
15601     {
15602         this.renderTouchView();
15603         
15604         this.touchViewEl.on('scroll', function(){
15605             this.el.dom.scrollTop = 0;
15606         }, this);
15607         
15608         this.originalValue = this.getValue();
15609         
15610         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15611         
15612         this.inputEl().on("click", this.showTouchView, this);
15613         if (this.triggerEl) {
15614             this.triggerEl.on("click", this.showTouchView, this);
15615         }
15616         
15617         
15618         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15619         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15620         
15621         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15622         
15623         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15624         this.store.on('load', this.onTouchViewLoad, this);
15625         this.store.on('loadexception', this.onTouchViewLoadException, this);
15626         
15627         if(this.hiddenName){
15628             
15629             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15630             
15631             this.hiddenField.dom.value =
15632                 this.hiddenValue !== undefined ? this.hiddenValue :
15633                 this.value !== undefined ? this.value : '';
15634         
15635             this.el.dom.removeAttribute('name');
15636             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15637         }
15638         
15639         if(this.multiple){
15640             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15641             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15642         }
15643         
15644         if(this.removable && !this.multiple){
15645             var close = this.closeTriggerEl();
15646             if(close){
15647                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15648                 close.on('click', this.removeBtnClick, this, close);
15649             }
15650         }
15651         /*
15652          * fix the bug in Safari iOS8
15653          */
15654         this.inputEl().on("focus", function(e){
15655             document.activeElement.blur();
15656         }, this);
15657         
15658         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15659         
15660         return;
15661         
15662         
15663     },
15664     
15665     renderTouchView : function()
15666     {
15667         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15668         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15669         
15670         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15671         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15672         
15673         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15674         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15675         this.touchViewBodyEl.setStyle('overflow', 'auto');
15676         
15677         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15678         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15679         
15680         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15681         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15682         
15683     },
15684     
15685     showTouchView : function()
15686     {
15687         if(this.disabled){
15688             return;
15689         }
15690         
15691         this.touchViewHeaderEl.hide();
15692
15693         if(this.modalTitle.length){
15694             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15695             this.touchViewHeaderEl.show();
15696         }
15697
15698         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15699         this.touchViewEl.show();
15700
15701         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15702         
15703         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15704         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15705
15706         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15707
15708         if(this.modalTitle.length){
15709             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15710         }
15711         
15712         this.touchViewBodyEl.setHeight(bodyHeight);
15713
15714         if(this.animate){
15715             var _this = this;
15716             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15717         }else{
15718             this.touchViewEl.addClass('in');
15719         }
15720         
15721         if(this._touchViewMask){
15722             Roo.get(document.body).addClass("x-body-masked");
15723             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15724             this._touchViewMask.setStyle('z-index', 10000);
15725             this._touchViewMask.addClass('show');
15726         }
15727         
15728         this.doTouchViewQuery();
15729         
15730     },
15731     
15732     hideTouchView : function()
15733     {
15734         this.touchViewEl.removeClass('in');
15735
15736         if(this.animate){
15737             var _this = this;
15738             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15739         }else{
15740             this.touchViewEl.setStyle('display', 'none');
15741         }
15742         
15743         if(this._touchViewMask){
15744             this._touchViewMask.removeClass('show');
15745             Roo.get(document.body).removeClass("x-body-masked");
15746         }
15747     },
15748     
15749     setTouchViewValue : function()
15750     {
15751         if(this.multiple){
15752             this.clearItem();
15753         
15754             var _this = this;
15755
15756             Roo.each(this.tickItems, function(o){
15757                 this.addItem(o);
15758             }, this);
15759         }
15760         
15761         this.hideTouchView();
15762     },
15763     
15764     doTouchViewQuery : function()
15765     {
15766         var qe = {
15767             query: '',
15768             forceAll: true,
15769             combo: this,
15770             cancel:false
15771         };
15772         
15773         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15774             return false;
15775         }
15776         
15777         if(!this.alwaysQuery || this.mode == 'local'){
15778             this.onTouchViewLoad();
15779             return;
15780         }
15781         
15782         this.store.load();
15783     },
15784     
15785     onTouchViewBeforeLoad : function(combo,opts)
15786     {
15787         return;
15788     },
15789
15790     // private
15791     onTouchViewLoad : function()
15792     {
15793         if(this.store.getCount() < 1){
15794             this.onTouchViewEmptyResults();
15795             return;
15796         }
15797         
15798         this.clearTouchView();
15799         
15800         var rawValue = this.getRawValue();
15801         
15802         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15803         
15804         this.tickItems = [];
15805         
15806         this.store.data.each(function(d, rowIndex){
15807             var row = this.touchViewListGroup.createChild(template);
15808             
15809             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15810                 row.addClass(d.data.cls);
15811             }
15812             
15813             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15814                 var cfg = {
15815                     data : d.data,
15816                     html : d.data[this.displayField]
15817                 };
15818                 
15819                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15820                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15821                 }
15822             }
15823             row.removeClass('selected');
15824             if(!this.multiple && this.valueField &&
15825                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15826             {
15827                 // radio buttons..
15828                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15829                 row.addClass('selected');
15830             }
15831             
15832             if(this.multiple && this.valueField &&
15833                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15834             {
15835                 
15836                 // checkboxes...
15837                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15838                 this.tickItems.push(d.data);
15839             }
15840             
15841             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15842             
15843         }, this);
15844         
15845         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15846         
15847         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15848
15849         if(this.modalTitle.length){
15850             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15851         }
15852
15853         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15854         
15855         if(this.mobile_restrict_height && listHeight < bodyHeight){
15856             this.touchViewBodyEl.setHeight(listHeight);
15857         }
15858         
15859         var _this = this;
15860         
15861         if(firstChecked && listHeight > bodyHeight){
15862             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15863         }
15864         
15865     },
15866     
15867     onTouchViewLoadException : function()
15868     {
15869         this.hideTouchView();
15870     },
15871     
15872     onTouchViewEmptyResults : function()
15873     {
15874         this.clearTouchView();
15875         
15876         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15877         
15878         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15879         
15880     },
15881     
15882     clearTouchView : function()
15883     {
15884         this.touchViewListGroup.dom.innerHTML = '';
15885     },
15886     
15887     onTouchViewClick : function(e, el, o)
15888     {
15889         e.preventDefault();
15890         
15891         var row = o.row;
15892         var rowIndex = o.rowIndex;
15893         
15894         var r = this.store.getAt(rowIndex);
15895         
15896         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15897             
15898             if(!this.multiple){
15899                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15900                     c.dom.removeAttribute('checked');
15901                 }, this);
15902
15903                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15904
15905                 this.setFromData(r.data);
15906
15907                 var close = this.closeTriggerEl();
15908
15909                 if(close){
15910                     close.show();
15911                 }
15912
15913                 this.hideTouchView();
15914
15915                 this.fireEvent('select', this, r, rowIndex);
15916
15917                 return;
15918             }
15919
15920             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15921                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15922                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15923                 return;
15924             }
15925
15926             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15927             this.addItem(r.data);
15928             this.tickItems.push(r.data);
15929         }
15930     },
15931     
15932     getAutoCreateNativeIOS : function()
15933     {
15934         var cfg = {
15935             cls: 'form-group' //input-group,
15936         };
15937         
15938         var combobox =  {
15939             tag: 'select',
15940             cls : 'roo-ios-select'
15941         };
15942         
15943         if (this.name) {
15944             combobox.name = this.name;
15945         }
15946         
15947         if (this.disabled) {
15948             combobox.disabled = true;
15949         }
15950         
15951         var settings = this;
15952         
15953         ['xs','sm','md','lg'].map(function(size){
15954             if (settings[size]) {
15955                 cfg.cls += ' col-' + size + '-' + settings[size];
15956             }
15957         });
15958         
15959         cfg.cn = combobox;
15960         
15961         return cfg;
15962         
15963     },
15964     
15965     initIOSView : function()
15966     {
15967         this.store.on('load', this.onIOSViewLoad, this);
15968         
15969         return;
15970     },
15971     
15972     onIOSViewLoad : function()
15973     {
15974         if(this.store.getCount() < 1){
15975             return;
15976         }
15977         
15978         this.clearIOSView();
15979         
15980         if(this.allowBlank) {
15981             
15982             var default_text = '-- SELECT --';
15983             
15984             if(this.placeholder.length){
15985                 default_text = this.placeholder;
15986             }
15987             
15988             if(this.emptyTitle.length){
15989                 default_text += ' - ' + this.emptyTitle + ' -';
15990             }
15991             
15992             var opt = this.inputEl().createChild({
15993                 tag: 'option',
15994                 value : 0,
15995                 html : default_text
15996             });
15997             
15998             var o = {};
15999             o[this.valueField] = 0;
16000             o[this.displayField] = default_text;
16001             
16002             this.ios_options.push({
16003                 data : o,
16004                 el : opt
16005             });
16006             
16007         }
16008         
16009         this.store.data.each(function(d, rowIndex){
16010             
16011             var html = '';
16012             
16013             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16014                 html = d.data[this.displayField];
16015             }
16016             
16017             var value = '';
16018             
16019             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16020                 value = d.data[this.valueField];
16021             }
16022             
16023             var option = {
16024                 tag: 'option',
16025                 value : value,
16026                 html : html
16027             };
16028             
16029             if(this.value == d.data[this.valueField]){
16030                 option['selected'] = true;
16031             }
16032             
16033             var opt = this.inputEl().createChild(option);
16034             
16035             this.ios_options.push({
16036                 data : d.data,
16037                 el : opt
16038             });
16039             
16040         }, this);
16041         
16042         this.inputEl().on('change', function(){
16043            this.fireEvent('select', this);
16044         }, this);
16045         
16046     },
16047     
16048     clearIOSView: function()
16049     {
16050         this.inputEl().dom.innerHTML = '';
16051         
16052         this.ios_options = [];
16053     },
16054     
16055     setIOSValue: function(v)
16056     {
16057         this.value = v;
16058         
16059         if(!this.ios_options){
16060             return;
16061         }
16062         
16063         Roo.each(this.ios_options, function(opts){
16064            
16065            opts.el.dom.removeAttribute('selected');
16066            
16067            if(opts.data[this.valueField] != v){
16068                return;
16069            }
16070            
16071            opts.el.dom.setAttribute('selected', true);
16072            
16073         }, this);
16074     }
16075
16076     /** 
16077     * @cfg {Boolean} grow 
16078     * @hide 
16079     */
16080     /** 
16081     * @cfg {Number} growMin 
16082     * @hide 
16083     */
16084     /** 
16085     * @cfg {Number} growMax 
16086     * @hide 
16087     */
16088     /**
16089      * @hide
16090      * @method autoSize
16091      */
16092 });
16093
16094 Roo.apply(Roo.bootstrap.ComboBox,  {
16095     
16096     header : {
16097         tag: 'div',
16098         cls: 'modal-header',
16099         cn: [
16100             {
16101                 tag: 'h4',
16102                 cls: 'modal-title'
16103             }
16104         ]
16105     },
16106     
16107     body : {
16108         tag: 'div',
16109         cls: 'modal-body',
16110         cn: [
16111             {
16112                 tag: 'ul',
16113                 cls: 'list-group'
16114             }
16115         ]
16116     },
16117     
16118     listItemRadio : {
16119         tag: 'li',
16120         cls: 'list-group-item',
16121         cn: [
16122             {
16123                 tag: 'span',
16124                 cls: 'roo-combobox-list-group-item-value'
16125             },
16126             {
16127                 tag: 'div',
16128                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16129                 cn: [
16130                     {
16131                         tag: 'input',
16132                         type: 'radio'
16133                     },
16134                     {
16135                         tag: 'label'
16136                     }
16137                 ]
16138             }
16139         ]
16140     },
16141     
16142     listItemCheckbox : {
16143         tag: 'li',
16144         cls: 'list-group-item',
16145         cn: [
16146             {
16147                 tag: 'span',
16148                 cls: 'roo-combobox-list-group-item-value'
16149             },
16150             {
16151                 tag: 'div',
16152                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16153                 cn: [
16154                     {
16155                         tag: 'input',
16156                         type: 'checkbox'
16157                     },
16158                     {
16159                         tag: 'label'
16160                     }
16161                 ]
16162             }
16163         ]
16164     },
16165     
16166     emptyResult : {
16167         tag: 'div',
16168         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16169     },
16170     
16171     footer : {
16172         tag: 'div',
16173         cls: 'modal-footer',
16174         cn: [
16175             {
16176                 tag: 'div',
16177                 cls: 'row',
16178                 cn: [
16179                     {
16180                         tag: 'div',
16181                         cls: 'col-xs-6 text-left',
16182                         cn: {
16183                             tag: 'button',
16184                             cls: 'btn btn-danger roo-touch-view-cancel',
16185                             html: 'Cancel'
16186                         }
16187                     },
16188                     {
16189                         tag: 'div',
16190                         cls: 'col-xs-6 text-right',
16191                         cn: {
16192                             tag: 'button',
16193                             cls: 'btn btn-success roo-touch-view-ok',
16194                             html: 'OK'
16195                         }
16196                     }
16197                 ]
16198             }
16199         ]
16200         
16201     }
16202 });
16203
16204 Roo.apply(Roo.bootstrap.ComboBox,  {
16205     
16206     touchViewTemplate : {
16207         tag: 'div',
16208         cls: 'modal fade roo-combobox-touch-view',
16209         cn: [
16210             {
16211                 tag: 'div',
16212                 cls: 'modal-dialog',
16213                 style : 'position:fixed', // we have to fix position....
16214                 cn: [
16215                     {
16216                         tag: 'div',
16217                         cls: 'modal-content',
16218                         cn: [
16219                             Roo.bootstrap.ComboBox.header,
16220                             Roo.bootstrap.ComboBox.body,
16221                             Roo.bootstrap.ComboBox.footer
16222                         ]
16223                     }
16224                 ]
16225             }
16226         ]
16227     }
16228 });/*
16229  * Based on:
16230  * Ext JS Library 1.1.1
16231  * Copyright(c) 2006-2007, Ext JS, LLC.
16232  *
16233  * Originally Released Under LGPL - original licence link has changed is not relivant.
16234  *
16235  * Fork - LGPL
16236  * <script type="text/javascript">
16237  */
16238
16239 /**
16240  * @class Roo.View
16241  * @extends Roo.util.Observable
16242  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16243  * This class also supports single and multi selection modes. <br>
16244  * Create a data model bound view:
16245  <pre><code>
16246  var store = new Roo.data.Store(...);
16247
16248  var view = new Roo.View({
16249     el : "my-element",
16250     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16251  
16252     singleSelect: true,
16253     selectedClass: "ydataview-selected",
16254     store: store
16255  });
16256
16257  // listen for node click?
16258  view.on("click", function(vw, index, node, e){
16259  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16260  });
16261
16262  // load XML data
16263  dataModel.load("foobar.xml");
16264  </code></pre>
16265  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16266  * <br><br>
16267  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16268  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16269  * 
16270  * Note: old style constructor is still suported (container, template, config)
16271  * 
16272  * @constructor
16273  * Create a new View
16274  * @param {Object} config The config object
16275  * 
16276  */
16277 Roo.View = function(config, depreciated_tpl, depreciated_config){
16278     
16279     this.parent = false;
16280     
16281     if (typeof(depreciated_tpl) == 'undefined') {
16282         // new way.. - universal constructor.
16283         Roo.apply(this, config);
16284         this.el  = Roo.get(this.el);
16285     } else {
16286         // old format..
16287         this.el  = Roo.get(config);
16288         this.tpl = depreciated_tpl;
16289         Roo.apply(this, depreciated_config);
16290     }
16291     this.wrapEl  = this.el.wrap().wrap();
16292     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16293     
16294     
16295     if(typeof(this.tpl) == "string"){
16296         this.tpl = new Roo.Template(this.tpl);
16297     } else {
16298         // support xtype ctors..
16299         this.tpl = new Roo.factory(this.tpl, Roo);
16300     }
16301     
16302     
16303     this.tpl.compile();
16304     
16305     /** @private */
16306     this.addEvents({
16307         /**
16308          * @event beforeclick
16309          * Fires before a click is processed. Returns false to cancel the default action.
16310          * @param {Roo.View} this
16311          * @param {Number} index The index of the target node
16312          * @param {HTMLElement} node The target node
16313          * @param {Roo.EventObject} e The raw event object
16314          */
16315             "beforeclick" : true,
16316         /**
16317          * @event click
16318          * Fires when a template node is clicked.
16319          * @param {Roo.View} this
16320          * @param {Number} index The index of the target node
16321          * @param {HTMLElement} node The target node
16322          * @param {Roo.EventObject} e The raw event object
16323          */
16324             "click" : true,
16325         /**
16326          * @event dblclick
16327          * Fires when a template node is double clicked.
16328          * @param {Roo.View} this
16329          * @param {Number} index The index of the target node
16330          * @param {HTMLElement} node The target node
16331          * @param {Roo.EventObject} e The raw event object
16332          */
16333             "dblclick" : true,
16334         /**
16335          * @event contextmenu
16336          * Fires when a template node is right clicked.
16337          * @param {Roo.View} this
16338          * @param {Number} index The index of the target node
16339          * @param {HTMLElement} node The target node
16340          * @param {Roo.EventObject} e The raw event object
16341          */
16342             "contextmenu" : true,
16343         /**
16344          * @event selectionchange
16345          * Fires when the selected nodes change.
16346          * @param {Roo.View} this
16347          * @param {Array} selections Array of the selected nodes
16348          */
16349             "selectionchange" : true,
16350     
16351         /**
16352          * @event beforeselect
16353          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16354          * @param {Roo.View} this
16355          * @param {HTMLElement} node The node to be selected
16356          * @param {Array} selections Array of currently selected nodes
16357          */
16358             "beforeselect" : true,
16359         /**
16360          * @event preparedata
16361          * Fires on every row to render, to allow you to change the data.
16362          * @param {Roo.View} this
16363          * @param {Object} data to be rendered (change this)
16364          */
16365           "preparedata" : true
16366           
16367           
16368         });
16369
16370
16371
16372     this.el.on({
16373         "click": this.onClick,
16374         "dblclick": this.onDblClick,
16375         "contextmenu": this.onContextMenu,
16376         scope:this
16377     });
16378
16379     this.selections = [];
16380     this.nodes = [];
16381     this.cmp = new Roo.CompositeElementLite([]);
16382     if(this.store){
16383         this.store = Roo.factory(this.store, Roo.data);
16384         this.setStore(this.store, true);
16385     }
16386     
16387     if ( this.footer && this.footer.xtype) {
16388            
16389          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16390         
16391         this.footer.dataSource = this.store;
16392         this.footer.container = fctr;
16393         this.footer = Roo.factory(this.footer, Roo);
16394         fctr.insertFirst(this.el);
16395         
16396         // this is a bit insane - as the paging toolbar seems to detach the el..
16397 //        dom.parentNode.parentNode.parentNode
16398          // they get detached?
16399     }
16400     
16401     
16402     Roo.View.superclass.constructor.call(this);
16403     
16404     
16405 };
16406
16407 Roo.extend(Roo.View, Roo.util.Observable, {
16408     
16409      /**
16410      * @cfg {Roo.data.Store} store Data store to load data from.
16411      */
16412     store : false,
16413     
16414     /**
16415      * @cfg {String|Roo.Element} el The container element.
16416      */
16417     el : '',
16418     
16419     /**
16420      * @cfg {String|Roo.Template} tpl The template used by this View 
16421      */
16422     tpl : false,
16423     /**
16424      * @cfg {String} dataName the named area of the template to use as the data area
16425      *                          Works with domtemplates roo-name="name"
16426      */
16427     dataName: false,
16428     /**
16429      * @cfg {String} selectedClass The css class to add to selected nodes
16430      */
16431     selectedClass : "x-view-selected",
16432      /**
16433      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16434      */
16435     emptyText : "",
16436     
16437     /**
16438      * @cfg {String} text to display on mask (default Loading)
16439      */
16440     mask : false,
16441     /**
16442      * @cfg {Boolean} multiSelect Allow multiple selection
16443      */
16444     multiSelect : false,
16445     /**
16446      * @cfg {Boolean} singleSelect Allow single selection
16447      */
16448     singleSelect:  false,
16449     
16450     /**
16451      * @cfg {Boolean} toggleSelect - selecting 
16452      */
16453     toggleSelect : false,
16454     
16455     /**
16456      * @cfg {Boolean} tickable - selecting 
16457      */
16458     tickable : false,
16459     
16460     /**
16461      * Returns the element this view is bound to.
16462      * @return {Roo.Element}
16463      */
16464     getEl : function(){
16465         return this.wrapEl;
16466     },
16467     
16468     
16469
16470     /**
16471      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16472      */
16473     refresh : function(){
16474         //Roo.log('refresh');
16475         var t = this.tpl;
16476         
16477         // if we are using something like 'domtemplate', then
16478         // the what gets used is:
16479         // t.applySubtemplate(NAME, data, wrapping data..)
16480         // the outer template then get' applied with
16481         //     the store 'extra data'
16482         // and the body get's added to the
16483         //      roo-name="data" node?
16484         //      <span class='roo-tpl-{name}'></span> ?????
16485         
16486         
16487         
16488         this.clearSelections();
16489         this.el.update("");
16490         var html = [];
16491         var records = this.store.getRange();
16492         if(records.length < 1) {
16493             
16494             // is this valid??  = should it render a template??
16495             
16496             this.el.update(this.emptyText);
16497             return;
16498         }
16499         var el = this.el;
16500         if (this.dataName) {
16501             this.el.update(t.apply(this.store.meta)); //????
16502             el = this.el.child('.roo-tpl-' + this.dataName);
16503         }
16504         
16505         for(var i = 0, len = records.length; i < len; i++){
16506             var data = this.prepareData(records[i].data, i, records[i]);
16507             this.fireEvent("preparedata", this, data, i, records[i]);
16508             
16509             var d = Roo.apply({}, data);
16510             
16511             if(this.tickable){
16512                 Roo.apply(d, {'roo-id' : Roo.id()});
16513                 
16514                 var _this = this;
16515             
16516                 Roo.each(this.parent.item, function(item){
16517                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16518                         return;
16519                     }
16520                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16521                 });
16522             }
16523             
16524             html[html.length] = Roo.util.Format.trim(
16525                 this.dataName ?
16526                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16527                     t.apply(d)
16528             );
16529         }
16530         
16531         
16532         
16533         el.update(html.join(""));
16534         this.nodes = el.dom.childNodes;
16535         this.updateIndexes(0);
16536     },
16537     
16538
16539     /**
16540      * Function to override to reformat the data that is sent to
16541      * the template for each node.
16542      * DEPRICATED - use the preparedata event handler.
16543      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16544      * a JSON object for an UpdateManager bound view).
16545      */
16546     prepareData : function(data, index, record)
16547     {
16548         this.fireEvent("preparedata", this, data, index, record);
16549         return data;
16550     },
16551
16552     onUpdate : function(ds, record){
16553         // Roo.log('on update');   
16554         this.clearSelections();
16555         var index = this.store.indexOf(record);
16556         var n = this.nodes[index];
16557         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16558         n.parentNode.removeChild(n);
16559         this.updateIndexes(index, index);
16560     },
16561
16562     
16563     
16564 // --------- FIXME     
16565     onAdd : function(ds, records, index)
16566     {
16567         //Roo.log(['on Add', ds, records, index] );        
16568         this.clearSelections();
16569         if(this.nodes.length == 0){
16570             this.refresh();
16571             return;
16572         }
16573         var n = this.nodes[index];
16574         for(var i = 0, len = records.length; i < len; i++){
16575             var d = this.prepareData(records[i].data, i, records[i]);
16576             if(n){
16577                 this.tpl.insertBefore(n, d);
16578             }else{
16579                 
16580                 this.tpl.append(this.el, d);
16581             }
16582         }
16583         this.updateIndexes(index);
16584     },
16585
16586     onRemove : function(ds, record, index){
16587        // Roo.log('onRemove');
16588         this.clearSelections();
16589         var el = this.dataName  ?
16590             this.el.child('.roo-tpl-' + this.dataName) :
16591             this.el; 
16592         
16593         el.dom.removeChild(this.nodes[index]);
16594         this.updateIndexes(index);
16595     },
16596
16597     /**
16598      * Refresh an individual node.
16599      * @param {Number} index
16600      */
16601     refreshNode : function(index){
16602         this.onUpdate(this.store, this.store.getAt(index));
16603     },
16604
16605     updateIndexes : function(startIndex, endIndex){
16606         var ns = this.nodes;
16607         startIndex = startIndex || 0;
16608         endIndex = endIndex || ns.length - 1;
16609         for(var i = startIndex; i <= endIndex; i++){
16610             ns[i].nodeIndex = i;
16611         }
16612     },
16613
16614     /**
16615      * Changes the data store this view uses and refresh the view.
16616      * @param {Store} store
16617      */
16618     setStore : function(store, initial){
16619         if(!initial && this.store){
16620             this.store.un("datachanged", this.refresh);
16621             this.store.un("add", this.onAdd);
16622             this.store.un("remove", this.onRemove);
16623             this.store.un("update", this.onUpdate);
16624             this.store.un("clear", this.refresh);
16625             this.store.un("beforeload", this.onBeforeLoad);
16626             this.store.un("load", this.onLoad);
16627             this.store.un("loadexception", this.onLoad);
16628         }
16629         if(store){
16630           
16631             store.on("datachanged", this.refresh, this);
16632             store.on("add", this.onAdd, this);
16633             store.on("remove", this.onRemove, this);
16634             store.on("update", this.onUpdate, this);
16635             store.on("clear", this.refresh, this);
16636             store.on("beforeload", this.onBeforeLoad, this);
16637             store.on("load", this.onLoad, this);
16638             store.on("loadexception", this.onLoad, this);
16639         }
16640         
16641         if(store){
16642             this.refresh();
16643         }
16644     },
16645     /**
16646      * onbeforeLoad - masks the loading area.
16647      *
16648      */
16649     onBeforeLoad : function(store,opts)
16650     {
16651          //Roo.log('onBeforeLoad');   
16652         if (!opts.add) {
16653             this.el.update("");
16654         }
16655         this.el.mask(this.mask ? this.mask : "Loading" ); 
16656     },
16657     onLoad : function ()
16658     {
16659         this.el.unmask();
16660     },
16661     
16662
16663     /**
16664      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16665      * @param {HTMLElement} node
16666      * @return {HTMLElement} The template node
16667      */
16668     findItemFromChild : function(node){
16669         var el = this.dataName  ?
16670             this.el.child('.roo-tpl-' + this.dataName,true) :
16671             this.el.dom; 
16672         
16673         if(!node || node.parentNode == el){
16674                     return node;
16675             }
16676             var p = node.parentNode;
16677             while(p && p != el){
16678             if(p.parentNode == el){
16679                 return p;
16680             }
16681             p = p.parentNode;
16682         }
16683             return null;
16684     },
16685
16686     /** @ignore */
16687     onClick : function(e){
16688         var item = this.findItemFromChild(e.getTarget());
16689         if(item){
16690             var index = this.indexOf(item);
16691             if(this.onItemClick(item, index, e) !== false){
16692                 this.fireEvent("click", this, index, item, e);
16693             }
16694         }else{
16695             this.clearSelections();
16696         }
16697     },
16698
16699     /** @ignore */
16700     onContextMenu : function(e){
16701         var item = this.findItemFromChild(e.getTarget());
16702         if(item){
16703             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16704         }
16705     },
16706
16707     /** @ignore */
16708     onDblClick : function(e){
16709         var item = this.findItemFromChild(e.getTarget());
16710         if(item){
16711             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16712         }
16713     },
16714
16715     onItemClick : function(item, index, e)
16716     {
16717         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16718             return false;
16719         }
16720         if (this.toggleSelect) {
16721             var m = this.isSelected(item) ? 'unselect' : 'select';
16722             //Roo.log(m);
16723             var _t = this;
16724             _t[m](item, true, false);
16725             return true;
16726         }
16727         if(this.multiSelect || this.singleSelect){
16728             if(this.multiSelect && e.shiftKey && this.lastSelection){
16729                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16730             }else{
16731                 this.select(item, this.multiSelect && e.ctrlKey);
16732                 this.lastSelection = item;
16733             }
16734             
16735             if(!this.tickable){
16736                 e.preventDefault();
16737             }
16738             
16739         }
16740         return true;
16741     },
16742
16743     /**
16744      * Get the number of selected nodes.
16745      * @return {Number}
16746      */
16747     getSelectionCount : function(){
16748         return this.selections.length;
16749     },
16750
16751     /**
16752      * Get the currently selected nodes.
16753      * @return {Array} An array of HTMLElements
16754      */
16755     getSelectedNodes : function(){
16756         return this.selections;
16757     },
16758
16759     /**
16760      * Get the indexes of the selected nodes.
16761      * @return {Array}
16762      */
16763     getSelectedIndexes : function(){
16764         var indexes = [], s = this.selections;
16765         for(var i = 0, len = s.length; i < len; i++){
16766             indexes.push(s[i].nodeIndex);
16767         }
16768         return indexes;
16769     },
16770
16771     /**
16772      * Clear all selections
16773      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16774      */
16775     clearSelections : function(suppressEvent){
16776         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16777             this.cmp.elements = this.selections;
16778             this.cmp.removeClass(this.selectedClass);
16779             this.selections = [];
16780             if(!suppressEvent){
16781                 this.fireEvent("selectionchange", this, this.selections);
16782             }
16783         }
16784     },
16785
16786     /**
16787      * Returns true if the passed node is selected
16788      * @param {HTMLElement/Number} node The node or node index
16789      * @return {Boolean}
16790      */
16791     isSelected : function(node){
16792         var s = this.selections;
16793         if(s.length < 1){
16794             return false;
16795         }
16796         node = this.getNode(node);
16797         return s.indexOf(node) !== -1;
16798     },
16799
16800     /**
16801      * Selects nodes.
16802      * @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
16803      * @param {Boolean} keepExisting (optional) true to keep existing selections
16804      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16805      */
16806     select : function(nodeInfo, keepExisting, suppressEvent){
16807         if(nodeInfo instanceof Array){
16808             if(!keepExisting){
16809                 this.clearSelections(true);
16810             }
16811             for(var i = 0, len = nodeInfo.length; i < len; i++){
16812                 this.select(nodeInfo[i], true, true);
16813             }
16814             return;
16815         } 
16816         var node = this.getNode(nodeInfo);
16817         if(!node || this.isSelected(node)){
16818             return; // already selected.
16819         }
16820         if(!keepExisting){
16821             this.clearSelections(true);
16822         }
16823         
16824         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16825             Roo.fly(node).addClass(this.selectedClass);
16826             this.selections.push(node);
16827             if(!suppressEvent){
16828                 this.fireEvent("selectionchange", this, this.selections);
16829             }
16830         }
16831         
16832         
16833     },
16834       /**
16835      * Unselects nodes.
16836      * @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
16837      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16838      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16839      */
16840     unselect : function(nodeInfo, keepExisting, suppressEvent)
16841     {
16842         if(nodeInfo instanceof Array){
16843             Roo.each(this.selections, function(s) {
16844                 this.unselect(s, nodeInfo);
16845             }, this);
16846             return;
16847         }
16848         var node = this.getNode(nodeInfo);
16849         if(!node || !this.isSelected(node)){
16850             //Roo.log("not selected");
16851             return; // not selected.
16852         }
16853         // fireevent???
16854         var ns = [];
16855         Roo.each(this.selections, function(s) {
16856             if (s == node ) {
16857                 Roo.fly(node).removeClass(this.selectedClass);
16858
16859                 return;
16860             }
16861             ns.push(s);
16862         },this);
16863         
16864         this.selections= ns;
16865         this.fireEvent("selectionchange", this, this.selections);
16866     },
16867
16868     /**
16869      * Gets a template node.
16870      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16871      * @return {HTMLElement} The node or null if it wasn't found
16872      */
16873     getNode : function(nodeInfo){
16874         if(typeof nodeInfo == "string"){
16875             return document.getElementById(nodeInfo);
16876         }else if(typeof nodeInfo == "number"){
16877             return this.nodes[nodeInfo];
16878         }
16879         return nodeInfo;
16880     },
16881
16882     /**
16883      * Gets a range template nodes.
16884      * @param {Number} startIndex
16885      * @param {Number} endIndex
16886      * @return {Array} An array of nodes
16887      */
16888     getNodes : function(start, end){
16889         var ns = this.nodes;
16890         start = start || 0;
16891         end = typeof end == "undefined" ? ns.length - 1 : end;
16892         var nodes = [];
16893         if(start <= end){
16894             for(var i = start; i <= end; i++){
16895                 nodes.push(ns[i]);
16896             }
16897         } else{
16898             for(var i = start; i >= end; i--){
16899                 nodes.push(ns[i]);
16900             }
16901         }
16902         return nodes;
16903     },
16904
16905     /**
16906      * Finds the index of the passed node
16907      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16908      * @return {Number} The index of the node or -1
16909      */
16910     indexOf : function(node){
16911         node = this.getNode(node);
16912         if(typeof node.nodeIndex == "number"){
16913             return node.nodeIndex;
16914         }
16915         var ns = this.nodes;
16916         for(var i = 0, len = ns.length; i < len; i++){
16917             if(ns[i] == node){
16918                 return i;
16919             }
16920         }
16921         return -1;
16922     }
16923 });
16924 /*
16925  * - LGPL
16926  *
16927  * based on jquery fullcalendar
16928  * 
16929  */
16930
16931 Roo.bootstrap = Roo.bootstrap || {};
16932 /**
16933  * @class Roo.bootstrap.Calendar
16934  * @extends Roo.bootstrap.Component
16935  * Bootstrap Calendar class
16936  * @cfg {Boolean} loadMask (true|false) default false
16937  * @cfg {Object} header generate the user specific header of the calendar, default false
16938
16939  * @constructor
16940  * Create a new Container
16941  * @param {Object} config The config object
16942  */
16943
16944
16945
16946 Roo.bootstrap.Calendar = function(config){
16947     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16948      this.addEvents({
16949         /**
16950              * @event select
16951              * Fires when a date is selected
16952              * @param {DatePicker} this
16953              * @param {Date} date The selected date
16954              */
16955         'select': true,
16956         /**
16957              * @event monthchange
16958              * Fires when the displayed month changes 
16959              * @param {DatePicker} this
16960              * @param {Date} date The selected month
16961              */
16962         'monthchange': true,
16963         /**
16964              * @event evententer
16965              * Fires when mouse over an event
16966              * @param {Calendar} this
16967              * @param {event} Event
16968              */
16969         'evententer': true,
16970         /**
16971              * @event eventleave
16972              * Fires when the mouse leaves an
16973              * @param {Calendar} this
16974              * @param {event}
16975              */
16976         'eventleave': true,
16977         /**
16978              * @event eventclick
16979              * Fires when the mouse click an
16980              * @param {Calendar} this
16981              * @param {event}
16982              */
16983         'eventclick': true
16984         
16985     });
16986
16987 };
16988
16989 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16990     
16991      /**
16992      * @cfg {Number} startDay
16993      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16994      */
16995     startDay : 0,
16996     
16997     loadMask : false,
16998     
16999     header : false,
17000       
17001     getAutoCreate : function(){
17002         
17003         
17004         var fc_button = function(name, corner, style, content ) {
17005             return Roo.apply({},{
17006                 tag : 'span',
17007                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
17008                          (corner.length ?
17009                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17010                             ''
17011                         ),
17012                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17013                 unselectable: 'on'
17014             });
17015         };
17016         
17017         var header = {};
17018         
17019         if(!this.header){
17020             header = {
17021                 tag : 'table',
17022                 cls : 'fc-header',
17023                 style : 'width:100%',
17024                 cn : [
17025                     {
17026                         tag: 'tr',
17027                         cn : [
17028                             {
17029                                 tag : 'td',
17030                                 cls : 'fc-header-left',
17031                                 cn : [
17032                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
17033                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
17034                                     { tag: 'span', cls: 'fc-header-space' },
17035                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
17036
17037
17038                                 ]
17039                             },
17040
17041                             {
17042                                 tag : 'td',
17043                                 cls : 'fc-header-center',
17044                                 cn : [
17045                                     {
17046                                         tag: 'span',
17047                                         cls: 'fc-header-title',
17048                                         cn : {
17049                                             tag: 'H2',
17050                                             html : 'month / year'
17051                                         }
17052                                     }
17053
17054                                 ]
17055                             },
17056                             {
17057                                 tag : 'td',
17058                                 cls : 'fc-header-right',
17059                                 cn : [
17060                               /*      fc_button('month', 'left', '', 'month' ),
17061                                     fc_button('week', '', '', 'week' ),
17062                                     fc_button('day', 'right', '', 'day' )
17063                                 */    
17064
17065                                 ]
17066                             }
17067
17068                         ]
17069                     }
17070                 ]
17071             };
17072         }
17073         
17074         header = this.header;
17075         
17076        
17077         var cal_heads = function() {
17078             var ret = [];
17079             // fixme - handle this.
17080             
17081             for (var i =0; i < Date.dayNames.length; i++) {
17082                 var d = Date.dayNames[i];
17083                 ret.push({
17084                     tag: 'th',
17085                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17086                     html : d.substring(0,3)
17087                 });
17088                 
17089             }
17090             ret[0].cls += ' fc-first';
17091             ret[6].cls += ' fc-last';
17092             return ret;
17093         };
17094         var cal_cell = function(n) {
17095             return  {
17096                 tag: 'td',
17097                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17098                 cn : [
17099                     {
17100                         cn : [
17101                             {
17102                                 cls: 'fc-day-number',
17103                                 html: 'D'
17104                             },
17105                             {
17106                                 cls: 'fc-day-content',
17107                              
17108                                 cn : [
17109                                      {
17110                                         style: 'position: relative;' // height: 17px;
17111                                     }
17112                                 ]
17113                             }
17114                             
17115                             
17116                         ]
17117                     }
17118                 ]
17119                 
17120             }
17121         };
17122         var cal_rows = function() {
17123             
17124             var ret = [];
17125             for (var r = 0; r < 6; r++) {
17126                 var row= {
17127                     tag : 'tr',
17128                     cls : 'fc-week',
17129                     cn : []
17130                 };
17131                 
17132                 for (var i =0; i < Date.dayNames.length; i++) {
17133                     var d = Date.dayNames[i];
17134                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17135
17136                 }
17137                 row.cn[0].cls+=' fc-first';
17138                 row.cn[0].cn[0].style = 'min-height:90px';
17139                 row.cn[6].cls+=' fc-last';
17140                 ret.push(row);
17141                 
17142             }
17143             ret[0].cls += ' fc-first';
17144             ret[4].cls += ' fc-prev-last';
17145             ret[5].cls += ' fc-last';
17146             return ret;
17147             
17148         };
17149         
17150         var cal_table = {
17151             tag: 'table',
17152             cls: 'fc-border-separate',
17153             style : 'width:100%',
17154             cellspacing  : 0,
17155             cn : [
17156                 { 
17157                     tag: 'thead',
17158                     cn : [
17159                         { 
17160                             tag: 'tr',
17161                             cls : 'fc-first fc-last',
17162                             cn : cal_heads()
17163                         }
17164                     ]
17165                 },
17166                 { 
17167                     tag: 'tbody',
17168                     cn : cal_rows()
17169                 }
17170                   
17171             ]
17172         };
17173          
17174          var cfg = {
17175             cls : 'fc fc-ltr',
17176             cn : [
17177                 header,
17178                 {
17179                     cls : 'fc-content',
17180                     style : "position: relative;",
17181                     cn : [
17182                         {
17183                             cls : 'fc-view fc-view-month fc-grid',
17184                             style : 'position: relative',
17185                             unselectable : 'on',
17186                             cn : [
17187                                 {
17188                                     cls : 'fc-event-container',
17189                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17190                                 },
17191                                 cal_table
17192                             ]
17193                         }
17194                     ]
17195     
17196                 }
17197            ] 
17198             
17199         };
17200         
17201          
17202         
17203         return cfg;
17204     },
17205     
17206     
17207     initEvents : function()
17208     {
17209         if(!this.store){
17210             throw "can not find store for calendar";
17211         }
17212         
17213         var mark = {
17214             tag: "div",
17215             cls:"x-dlg-mask",
17216             style: "text-align:center",
17217             cn: [
17218                 {
17219                     tag: "div",
17220                     style: "background-color:white;width:50%;margin:250 auto",
17221                     cn: [
17222                         {
17223                             tag: "img",
17224                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17225                         },
17226                         {
17227                             tag: "span",
17228                             html: "Loading"
17229                         }
17230                         
17231                     ]
17232                 }
17233             ]
17234         };
17235         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17236         
17237         var size = this.el.select('.fc-content', true).first().getSize();
17238         this.maskEl.setSize(size.width, size.height);
17239         this.maskEl.enableDisplayMode("block");
17240         if(!this.loadMask){
17241             this.maskEl.hide();
17242         }
17243         
17244         this.store = Roo.factory(this.store, Roo.data);
17245         this.store.on('load', this.onLoad, this);
17246         this.store.on('beforeload', this.onBeforeLoad, this);
17247         
17248         this.resize();
17249         
17250         this.cells = this.el.select('.fc-day',true);
17251         //Roo.log(this.cells);
17252         this.textNodes = this.el.query('.fc-day-number');
17253         this.cells.addClassOnOver('fc-state-hover');
17254         
17255         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17256         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17257         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17258         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17259         
17260         this.on('monthchange', this.onMonthChange, this);
17261         
17262         this.update(new Date().clearTime());
17263     },
17264     
17265     resize : function() {
17266         var sz  = this.el.getSize();
17267         
17268         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17269         this.el.select('.fc-day-content div',true).setHeight(34);
17270     },
17271     
17272     
17273     // private
17274     showPrevMonth : function(e){
17275         this.update(this.activeDate.add("mo", -1));
17276     },
17277     showToday : function(e){
17278         this.update(new Date().clearTime());
17279     },
17280     // private
17281     showNextMonth : function(e){
17282         this.update(this.activeDate.add("mo", 1));
17283     },
17284
17285     // private
17286     showPrevYear : function(){
17287         this.update(this.activeDate.add("y", -1));
17288     },
17289
17290     // private
17291     showNextYear : function(){
17292         this.update(this.activeDate.add("y", 1));
17293     },
17294
17295     
17296    // private
17297     update : function(date)
17298     {
17299         var vd = this.activeDate;
17300         this.activeDate = date;
17301 //        if(vd && this.el){
17302 //            var t = date.getTime();
17303 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17304 //                Roo.log('using add remove');
17305 //                
17306 //                this.fireEvent('monthchange', this, date);
17307 //                
17308 //                this.cells.removeClass("fc-state-highlight");
17309 //                this.cells.each(function(c){
17310 //                   if(c.dateValue == t){
17311 //                       c.addClass("fc-state-highlight");
17312 //                       setTimeout(function(){
17313 //                            try{c.dom.firstChild.focus();}catch(e){}
17314 //                       }, 50);
17315 //                       return false;
17316 //                   }
17317 //                   return true;
17318 //                });
17319 //                return;
17320 //            }
17321 //        }
17322         
17323         var days = date.getDaysInMonth();
17324         
17325         var firstOfMonth = date.getFirstDateOfMonth();
17326         var startingPos = firstOfMonth.getDay()-this.startDay;
17327         
17328         if(startingPos < this.startDay){
17329             startingPos += 7;
17330         }
17331         
17332         var pm = date.add(Date.MONTH, -1);
17333         var prevStart = pm.getDaysInMonth()-startingPos;
17334 //        
17335         this.cells = this.el.select('.fc-day',true);
17336         this.textNodes = this.el.query('.fc-day-number');
17337         this.cells.addClassOnOver('fc-state-hover');
17338         
17339         var cells = this.cells.elements;
17340         var textEls = this.textNodes;
17341         
17342         Roo.each(cells, function(cell){
17343             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17344         });
17345         
17346         days += startingPos;
17347
17348         // convert everything to numbers so it's fast
17349         var day = 86400000;
17350         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17351         //Roo.log(d);
17352         //Roo.log(pm);
17353         //Roo.log(prevStart);
17354         
17355         var today = new Date().clearTime().getTime();
17356         var sel = date.clearTime().getTime();
17357         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17358         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17359         var ddMatch = this.disabledDatesRE;
17360         var ddText = this.disabledDatesText;
17361         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17362         var ddaysText = this.disabledDaysText;
17363         var format = this.format;
17364         
17365         var setCellClass = function(cal, cell){
17366             cell.row = 0;
17367             cell.events = [];
17368             cell.more = [];
17369             //Roo.log('set Cell Class');
17370             cell.title = "";
17371             var t = d.getTime();
17372             
17373             //Roo.log(d);
17374             
17375             cell.dateValue = t;
17376             if(t == today){
17377                 cell.className += " fc-today";
17378                 cell.className += " fc-state-highlight";
17379                 cell.title = cal.todayText;
17380             }
17381             if(t == sel){
17382                 // disable highlight in other month..
17383                 //cell.className += " fc-state-highlight";
17384                 
17385             }
17386             // disabling
17387             if(t < min) {
17388                 cell.className = " fc-state-disabled";
17389                 cell.title = cal.minText;
17390                 return;
17391             }
17392             if(t > max) {
17393                 cell.className = " fc-state-disabled";
17394                 cell.title = cal.maxText;
17395                 return;
17396             }
17397             if(ddays){
17398                 if(ddays.indexOf(d.getDay()) != -1){
17399                     cell.title = ddaysText;
17400                     cell.className = " fc-state-disabled";
17401                 }
17402             }
17403             if(ddMatch && format){
17404                 var fvalue = d.dateFormat(format);
17405                 if(ddMatch.test(fvalue)){
17406                     cell.title = ddText.replace("%0", fvalue);
17407                     cell.className = " fc-state-disabled";
17408                 }
17409             }
17410             
17411             if (!cell.initialClassName) {
17412                 cell.initialClassName = cell.dom.className;
17413             }
17414             
17415             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17416         };
17417
17418         var i = 0;
17419         
17420         for(; i < startingPos; i++) {
17421             textEls[i].innerHTML = (++prevStart);
17422             d.setDate(d.getDate()+1);
17423             
17424             cells[i].className = "fc-past fc-other-month";
17425             setCellClass(this, cells[i]);
17426         }
17427         
17428         var intDay = 0;
17429         
17430         for(; i < days; i++){
17431             intDay = i - startingPos + 1;
17432             textEls[i].innerHTML = (intDay);
17433             d.setDate(d.getDate()+1);
17434             
17435             cells[i].className = ''; // "x-date-active";
17436             setCellClass(this, cells[i]);
17437         }
17438         var extraDays = 0;
17439         
17440         for(; i < 42; i++) {
17441             textEls[i].innerHTML = (++extraDays);
17442             d.setDate(d.getDate()+1);
17443             
17444             cells[i].className = "fc-future fc-other-month";
17445             setCellClass(this, cells[i]);
17446         }
17447         
17448         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17449         
17450         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17451         
17452         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17453         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17454         
17455         if(totalRows != 6){
17456             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17457             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17458         }
17459         
17460         this.fireEvent('monthchange', this, date);
17461         
17462         
17463         /*
17464         if(!this.internalRender){
17465             var main = this.el.dom.firstChild;
17466             var w = main.offsetWidth;
17467             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17468             Roo.fly(main).setWidth(w);
17469             this.internalRender = true;
17470             // opera does not respect the auto grow header center column
17471             // then, after it gets a width opera refuses to recalculate
17472             // without a second pass
17473             if(Roo.isOpera && !this.secondPass){
17474                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17475                 this.secondPass = true;
17476                 this.update.defer(10, this, [date]);
17477             }
17478         }
17479         */
17480         
17481     },
17482     
17483     findCell : function(dt) {
17484         dt = dt.clearTime().getTime();
17485         var ret = false;
17486         this.cells.each(function(c){
17487             //Roo.log("check " +c.dateValue + '?=' + dt);
17488             if(c.dateValue == dt){
17489                 ret = c;
17490                 return false;
17491             }
17492             return true;
17493         });
17494         
17495         return ret;
17496     },
17497     
17498     findCells : function(ev) {
17499         var s = ev.start.clone().clearTime().getTime();
17500        // Roo.log(s);
17501         var e= ev.end.clone().clearTime().getTime();
17502        // Roo.log(e);
17503         var ret = [];
17504         this.cells.each(function(c){
17505              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17506             
17507             if(c.dateValue > e){
17508                 return ;
17509             }
17510             if(c.dateValue < s){
17511                 return ;
17512             }
17513             ret.push(c);
17514         });
17515         
17516         return ret;    
17517     },
17518     
17519 //    findBestRow: function(cells)
17520 //    {
17521 //        var ret = 0;
17522 //        
17523 //        for (var i =0 ; i < cells.length;i++) {
17524 //            ret  = Math.max(cells[i].rows || 0,ret);
17525 //        }
17526 //        return ret;
17527 //        
17528 //    },
17529     
17530     
17531     addItem : function(ev)
17532     {
17533         // look for vertical location slot in
17534         var cells = this.findCells(ev);
17535         
17536 //        ev.row = this.findBestRow(cells);
17537         
17538         // work out the location.
17539         
17540         var crow = false;
17541         var rows = [];
17542         for(var i =0; i < cells.length; i++) {
17543             
17544             cells[i].row = cells[0].row;
17545             
17546             if(i == 0){
17547                 cells[i].row = cells[i].row + 1;
17548             }
17549             
17550             if (!crow) {
17551                 crow = {
17552                     start : cells[i],
17553                     end :  cells[i]
17554                 };
17555                 continue;
17556             }
17557             if (crow.start.getY() == cells[i].getY()) {
17558                 // on same row.
17559                 crow.end = cells[i];
17560                 continue;
17561             }
17562             // different row.
17563             rows.push(crow);
17564             crow = {
17565                 start: cells[i],
17566                 end : cells[i]
17567             };
17568             
17569         }
17570         
17571         rows.push(crow);
17572         ev.els = [];
17573         ev.rows = rows;
17574         ev.cells = cells;
17575         
17576         cells[0].events.push(ev);
17577         
17578         this.calevents.push(ev);
17579     },
17580     
17581     clearEvents: function() {
17582         
17583         if(!this.calevents){
17584             return;
17585         }
17586         
17587         Roo.each(this.cells.elements, function(c){
17588             c.row = 0;
17589             c.events = [];
17590             c.more = [];
17591         });
17592         
17593         Roo.each(this.calevents, function(e) {
17594             Roo.each(e.els, function(el) {
17595                 el.un('mouseenter' ,this.onEventEnter, this);
17596                 el.un('mouseleave' ,this.onEventLeave, this);
17597                 el.remove();
17598             },this);
17599         },this);
17600         
17601         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17602             e.remove();
17603         });
17604         
17605     },
17606     
17607     renderEvents: function()
17608     {   
17609         var _this = this;
17610         
17611         this.cells.each(function(c) {
17612             
17613             if(c.row < 5){
17614                 return;
17615             }
17616             
17617             var ev = c.events;
17618             
17619             var r = 4;
17620             if(c.row != c.events.length){
17621                 r = 4 - (4 - (c.row - c.events.length));
17622             }
17623             
17624             c.events = ev.slice(0, r);
17625             c.more = ev.slice(r);
17626             
17627             if(c.more.length && c.more.length == 1){
17628                 c.events.push(c.more.pop());
17629             }
17630             
17631             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17632             
17633         });
17634             
17635         this.cells.each(function(c) {
17636             
17637             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17638             
17639             
17640             for (var e = 0; e < c.events.length; e++){
17641                 var ev = c.events[e];
17642                 var rows = ev.rows;
17643                 
17644                 for(var i = 0; i < rows.length; i++) {
17645                 
17646                     // how many rows should it span..
17647
17648                     var  cfg = {
17649                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17650                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17651
17652                         unselectable : "on",
17653                         cn : [
17654                             {
17655                                 cls: 'fc-event-inner',
17656                                 cn : [
17657     //                                {
17658     //                                  tag:'span',
17659     //                                  cls: 'fc-event-time',
17660     //                                  html : cells.length > 1 ? '' : ev.time
17661     //                                },
17662                                     {
17663                                       tag:'span',
17664                                       cls: 'fc-event-title',
17665                                       html : String.format('{0}', ev.title)
17666                                     }
17667
17668
17669                                 ]
17670                             },
17671                             {
17672                                 cls: 'ui-resizable-handle ui-resizable-e',
17673                                 html : '&nbsp;&nbsp;&nbsp'
17674                             }
17675
17676                         ]
17677                     };
17678
17679                     if (i == 0) {
17680                         cfg.cls += ' fc-event-start';
17681                     }
17682                     if ((i+1) == rows.length) {
17683                         cfg.cls += ' fc-event-end';
17684                     }
17685
17686                     var ctr = _this.el.select('.fc-event-container',true).first();
17687                     var cg = ctr.createChild(cfg);
17688
17689                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17690                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17691
17692                     var r = (c.more.length) ? 1 : 0;
17693                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17694                     cg.setWidth(ebox.right - sbox.x -2);
17695
17696                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17697                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17698                     cg.on('click', _this.onEventClick, _this, ev);
17699
17700                     ev.els.push(cg);
17701                     
17702                 }
17703                 
17704             }
17705             
17706             
17707             if(c.more.length){
17708                 var  cfg = {
17709                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17710                     style : 'position: absolute',
17711                     unselectable : "on",
17712                     cn : [
17713                         {
17714                             cls: 'fc-event-inner',
17715                             cn : [
17716                                 {
17717                                   tag:'span',
17718                                   cls: 'fc-event-title',
17719                                   html : 'More'
17720                                 }
17721
17722
17723                             ]
17724                         },
17725                         {
17726                             cls: 'ui-resizable-handle ui-resizable-e',
17727                             html : '&nbsp;&nbsp;&nbsp'
17728                         }
17729
17730                     ]
17731                 };
17732
17733                 var ctr = _this.el.select('.fc-event-container',true).first();
17734                 var cg = ctr.createChild(cfg);
17735
17736                 var sbox = c.select('.fc-day-content',true).first().getBox();
17737                 var ebox = c.select('.fc-day-content',true).first().getBox();
17738                 //Roo.log(cg);
17739                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17740                 cg.setWidth(ebox.right - sbox.x -2);
17741
17742                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17743                 
17744             }
17745             
17746         });
17747         
17748         
17749         
17750     },
17751     
17752     onEventEnter: function (e, el,event,d) {
17753         this.fireEvent('evententer', this, el, event);
17754     },
17755     
17756     onEventLeave: function (e, el,event,d) {
17757         this.fireEvent('eventleave', this, el, event);
17758     },
17759     
17760     onEventClick: function (e, el,event,d) {
17761         this.fireEvent('eventclick', this, el, event);
17762     },
17763     
17764     onMonthChange: function () {
17765         this.store.load();
17766     },
17767     
17768     onMoreEventClick: function(e, el, more)
17769     {
17770         var _this = this;
17771         
17772         this.calpopover.placement = 'right';
17773         this.calpopover.setTitle('More');
17774         
17775         this.calpopover.setContent('');
17776         
17777         var ctr = this.calpopover.el.select('.popover-content', true).first();
17778         
17779         Roo.each(more, function(m){
17780             var cfg = {
17781                 cls : 'fc-event-hori fc-event-draggable',
17782                 html : m.title
17783             };
17784             var cg = ctr.createChild(cfg);
17785             
17786             cg.on('click', _this.onEventClick, _this, m);
17787         });
17788         
17789         this.calpopover.show(el);
17790         
17791         
17792     },
17793     
17794     onLoad: function () 
17795     {   
17796         this.calevents = [];
17797         var cal = this;
17798         
17799         if(this.store.getCount() > 0){
17800             this.store.data.each(function(d){
17801                cal.addItem({
17802                     id : d.data.id,
17803                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17804                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17805                     time : d.data.start_time,
17806                     title : d.data.title,
17807                     description : d.data.description,
17808                     venue : d.data.venue
17809                 });
17810             });
17811         }
17812         
17813         this.renderEvents();
17814         
17815         if(this.calevents.length && this.loadMask){
17816             this.maskEl.hide();
17817         }
17818     },
17819     
17820     onBeforeLoad: function()
17821     {
17822         this.clearEvents();
17823         if(this.loadMask){
17824             this.maskEl.show();
17825         }
17826     }
17827 });
17828
17829  
17830  /*
17831  * - LGPL
17832  *
17833  * element
17834  * 
17835  */
17836
17837 /**
17838  * @class Roo.bootstrap.Popover
17839  * @extends Roo.bootstrap.Component
17840  * Bootstrap Popover class
17841  * @cfg {String} html contents of the popover   (or false to use children..)
17842  * @cfg {String} title of popover (or false to hide)
17843  * @cfg {String} placement how it is placed
17844  * @cfg {String} trigger click || hover (or false to trigger manually)
17845  * @cfg {String} over what (parent or false to trigger manually.)
17846  * @cfg {Number} delay - delay before showing
17847  
17848  * @constructor
17849  * Create a new Popover
17850  * @param {Object} config The config object
17851  */
17852
17853 Roo.bootstrap.Popover = function(config){
17854     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17855     
17856     this.addEvents({
17857         // raw events
17858          /**
17859          * @event show
17860          * After the popover show
17861          * 
17862          * @param {Roo.bootstrap.Popover} this
17863          */
17864         "show" : true,
17865         /**
17866          * @event hide
17867          * After the popover hide
17868          * 
17869          * @param {Roo.bootstrap.Popover} this
17870          */
17871         "hide" : true
17872     });
17873 };
17874
17875 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17876     
17877     title: 'Fill in a title',
17878     html: false,
17879     
17880     placement : 'right',
17881     trigger : 'hover', // hover
17882     
17883     delay : 0,
17884     
17885     over: 'parent',
17886     
17887     can_build_overlaid : false,
17888     
17889     getChildContainer : function()
17890     {
17891         return this.el.select('.popover-content',true).first();
17892     },
17893     
17894     getAutoCreate : function(){
17895          
17896         var cfg = {
17897            cls : 'popover roo-dynamic',
17898            style: 'display:block',
17899            cn : [
17900                 {
17901                     cls : 'arrow'
17902                 },
17903                 {
17904                     cls : 'popover-inner',
17905                     cn : [
17906                         {
17907                             tag: 'h3',
17908                             cls: 'popover-title popover-header',
17909                             html : this.title
17910                         },
17911                         {
17912                             cls : 'popover-content popover-body',
17913                             html : this.html
17914                         }
17915                     ]
17916                     
17917                 }
17918            ]
17919         };
17920         
17921         return cfg;
17922     },
17923     setTitle: function(str)
17924     {
17925         this.title = str;
17926         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17927     },
17928     setContent: function(str)
17929     {
17930         this.html = str;
17931         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17932     },
17933     // as it get's added to the bottom of the page.
17934     onRender : function(ct, position)
17935     {
17936         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17937         if(!this.el){
17938             var cfg = Roo.apply({},  this.getAutoCreate());
17939             cfg.id = Roo.id();
17940             
17941             if (this.cls) {
17942                 cfg.cls += ' ' + this.cls;
17943             }
17944             if (this.style) {
17945                 cfg.style = this.style;
17946             }
17947             //Roo.log("adding to ");
17948             this.el = Roo.get(document.body).createChild(cfg, position);
17949 //            Roo.log(this.el);
17950         }
17951         this.initEvents();
17952     },
17953     
17954     initEvents : function()
17955     {
17956         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17957         this.el.enableDisplayMode('block');
17958         this.el.hide();
17959         if (this.over === false) {
17960             return; 
17961         }
17962         if (this.triggers === false) {
17963             return;
17964         }
17965         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17966         var triggers = this.trigger ? this.trigger.split(' ') : [];
17967         Roo.each(triggers, function(trigger) {
17968         
17969             if (trigger == 'click') {
17970                 on_el.on('click', this.toggle, this);
17971             } else if (trigger != 'manual') {
17972                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17973                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17974       
17975                 on_el.on(eventIn  ,this.enter, this);
17976                 on_el.on(eventOut, this.leave, this);
17977             }
17978         }, this);
17979         
17980     },
17981     
17982     
17983     // private
17984     timeout : null,
17985     hoverState : null,
17986     
17987     toggle : function () {
17988         this.hoverState == 'in' ? this.leave() : this.enter();
17989     },
17990     
17991     enter : function () {
17992         
17993         clearTimeout(this.timeout);
17994     
17995         this.hoverState = 'in';
17996     
17997         if (!this.delay || !this.delay.show) {
17998             this.show();
17999             return;
18000         }
18001         var _t = this;
18002         this.timeout = setTimeout(function () {
18003             if (_t.hoverState == 'in') {
18004                 _t.show();
18005             }
18006         }, this.delay.show)
18007     },
18008     
18009     leave : function() {
18010         clearTimeout(this.timeout);
18011     
18012         this.hoverState = 'out';
18013     
18014         if (!this.delay || !this.delay.hide) {
18015             this.hide();
18016             return;
18017         }
18018         var _t = this;
18019         this.timeout = setTimeout(function () {
18020             if (_t.hoverState == 'out') {
18021                 _t.hide();
18022             }
18023         }, this.delay.hide)
18024     },
18025     
18026     show : function (on_el)
18027     {
18028         if (!on_el) {
18029             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18030         }
18031         
18032         // set content.
18033         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18034         if (this.html !== false) {
18035             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18036         }
18037         this.el.removeClass([
18038             'fade','top','bottom', 'left', 'right','in',
18039             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18040         ]);
18041         if (!this.title.length) {
18042             this.el.select('.popover-title',true).hide();
18043         }
18044         
18045         var placement = typeof this.placement == 'function' ?
18046             this.placement.call(this, this.el, on_el) :
18047             this.placement;
18048             
18049         var autoToken = /\s?auto?\s?/i;
18050         var autoPlace = autoToken.test(placement);
18051         if (autoPlace) {
18052             placement = placement.replace(autoToken, '') || 'top';
18053         }
18054         
18055         //this.el.detach()
18056         //this.el.setXY([0,0]);
18057         this.el.show();
18058         this.el.dom.style.display='block';
18059         this.el.addClass(placement);
18060         
18061         //this.el.appendTo(on_el);
18062         
18063         var p = this.getPosition();
18064         var box = this.el.getBox();
18065         
18066         if (autoPlace) {
18067             // fixme..
18068         }
18069         var align = Roo.bootstrap.Popover.alignment[placement];
18070         
18071 //        Roo.log(align);
18072         this.el.alignTo(on_el, align[0],align[1]);
18073         //var arrow = this.el.select('.arrow',true).first();
18074         //arrow.set(align[2], 
18075         
18076         this.el.addClass('in');
18077         
18078         
18079         if (this.el.hasClass('fade')) {
18080             // fade it?
18081         }
18082         
18083         this.hoverState = 'in';
18084         
18085         this.fireEvent('show', this);
18086         
18087     },
18088     hide : function()
18089     {
18090         this.el.setXY([0,0]);
18091         this.el.removeClass('in');
18092         this.el.hide();
18093         this.hoverState = null;
18094         
18095         this.fireEvent('hide', this);
18096     }
18097     
18098 });
18099
18100 Roo.bootstrap.Popover.alignment = {
18101     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18102     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18103     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18104     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18105 };
18106
18107  /*
18108  * - LGPL
18109  *
18110  * Progress
18111  * 
18112  */
18113
18114 /**
18115  * @class Roo.bootstrap.Progress
18116  * @extends Roo.bootstrap.Component
18117  * Bootstrap Progress class
18118  * @cfg {Boolean} striped striped of the progress bar
18119  * @cfg {Boolean} active animated of the progress bar
18120  * 
18121  * 
18122  * @constructor
18123  * Create a new Progress
18124  * @param {Object} config The config object
18125  */
18126
18127 Roo.bootstrap.Progress = function(config){
18128     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18129 };
18130
18131 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18132     
18133     striped : false,
18134     active: false,
18135     
18136     getAutoCreate : function(){
18137         var cfg = {
18138             tag: 'div',
18139             cls: 'progress'
18140         };
18141         
18142         
18143         if(this.striped){
18144             cfg.cls += ' progress-striped';
18145         }
18146       
18147         if(this.active){
18148             cfg.cls += ' active';
18149         }
18150         
18151         
18152         return cfg;
18153     }
18154    
18155 });
18156
18157  
18158
18159  /*
18160  * - LGPL
18161  *
18162  * ProgressBar
18163  * 
18164  */
18165
18166 /**
18167  * @class Roo.bootstrap.ProgressBar
18168  * @extends Roo.bootstrap.Component
18169  * Bootstrap ProgressBar class
18170  * @cfg {Number} aria_valuenow aria-value now
18171  * @cfg {Number} aria_valuemin aria-value min
18172  * @cfg {Number} aria_valuemax aria-value max
18173  * @cfg {String} label label for the progress bar
18174  * @cfg {String} panel (success | info | warning | danger )
18175  * @cfg {String} role role of the progress bar
18176  * @cfg {String} sr_only text
18177  * 
18178  * 
18179  * @constructor
18180  * Create a new ProgressBar
18181  * @param {Object} config The config object
18182  */
18183
18184 Roo.bootstrap.ProgressBar = function(config){
18185     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18186 };
18187
18188 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18189     
18190     aria_valuenow : 0,
18191     aria_valuemin : 0,
18192     aria_valuemax : 100,
18193     label : false,
18194     panel : false,
18195     role : false,
18196     sr_only: false,
18197     
18198     getAutoCreate : function()
18199     {
18200         
18201         var cfg = {
18202             tag: 'div',
18203             cls: 'progress-bar',
18204             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18205         };
18206         
18207         if(this.sr_only){
18208             cfg.cn = {
18209                 tag: 'span',
18210                 cls: 'sr-only',
18211                 html: this.sr_only
18212             }
18213         }
18214         
18215         if(this.role){
18216             cfg.role = this.role;
18217         }
18218         
18219         if(this.aria_valuenow){
18220             cfg['aria-valuenow'] = this.aria_valuenow;
18221         }
18222         
18223         if(this.aria_valuemin){
18224             cfg['aria-valuemin'] = this.aria_valuemin;
18225         }
18226         
18227         if(this.aria_valuemax){
18228             cfg['aria-valuemax'] = this.aria_valuemax;
18229         }
18230         
18231         if(this.label && !this.sr_only){
18232             cfg.html = this.label;
18233         }
18234         
18235         if(this.panel){
18236             cfg.cls += ' progress-bar-' + this.panel;
18237         }
18238         
18239         return cfg;
18240     },
18241     
18242     update : function(aria_valuenow)
18243     {
18244         this.aria_valuenow = aria_valuenow;
18245         
18246         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18247     }
18248    
18249 });
18250
18251  
18252
18253  /*
18254  * - LGPL
18255  *
18256  * column
18257  * 
18258  */
18259
18260 /**
18261  * @class Roo.bootstrap.TabGroup
18262  * @extends Roo.bootstrap.Column
18263  * Bootstrap Column class
18264  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18265  * @cfg {Boolean} carousel true to make the group behave like a carousel
18266  * @cfg {Boolean} bullets show bullets for the panels
18267  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18268  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18269  * @cfg {Boolean} showarrow (true|false) show arrow default true
18270  * 
18271  * @constructor
18272  * Create a new TabGroup
18273  * @param {Object} config The config object
18274  */
18275
18276 Roo.bootstrap.TabGroup = function(config){
18277     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18278     if (!this.navId) {
18279         this.navId = Roo.id();
18280     }
18281     this.tabs = [];
18282     Roo.bootstrap.TabGroup.register(this);
18283     
18284 };
18285
18286 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18287     
18288     carousel : false,
18289     transition : false,
18290     bullets : 0,
18291     timer : 0,
18292     autoslide : false,
18293     slideFn : false,
18294     slideOnTouch : false,
18295     showarrow : true,
18296     
18297     getAutoCreate : function()
18298     {
18299         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18300         
18301         cfg.cls += ' tab-content';
18302         
18303         if (this.carousel) {
18304             cfg.cls += ' carousel slide';
18305             
18306             cfg.cn = [{
18307                cls : 'carousel-inner',
18308                cn : []
18309             }];
18310         
18311             if(this.bullets  && !Roo.isTouch){
18312                 
18313                 var bullets = {
18314                     cls : 'carousel-bullets',
18315                     cn : []
18316                 };
18317                
18318                 if(this.bullets_cls){
18319                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18320                 }
18321                 
18322                 bullets.cn.push({
18323                     cls : 'clear'
18324                 });
18325                 
18326                 cfg.cn[0].cn.push(bullets);
18327             }
18328             
18329             if(this.showarrow){
18330                 cfg.cn[0].cn.push({
18331                     tag : 'div',
18332                     class : 'carousel-arrow',
18333                     cn : [
18334                         {
18335                             tag : 'div',
18336                             class : 'carousel-prev',
18337                             cn : [
18338                                 {
18339                                     tag : 'i',
18340                                     class : 'fa fa-chevron-left'
18341                                 }
18342                             ]
18343                         },
18344                         {
18345                             tag : 'div',
18346                             class : 'carousel-next',
18347                             cn : [
18348                                 {
18349                                     tag : 'i',
18350                                     class : 'fa fa-chevron-right'
18351                                 }
18352                             ]
18353                         }
18354                     ]
18355                 });
18356             }
18357             
18358         }
18359         
18360         return cfg;
18361     },
18362     
18363     initEvents:  function()
18364     {
18365 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18366 //            this.el.on("touchstart", this.onTouchStart, this);
18367 //        }
18368         
18369         if(this.autoslide){
18370             var _this = this;
18371             
18372             this.slideFn = window.setInterval(function() {
18373                 _this.showPanelNext();
18374             }, this.timer);
18375         }
18376         
18377         if(this.showarrow){
18378             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18379             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18380         }
18381         
18382         
18383     },
18384     
18385 //    onTouchStart : function(e, el, o)
18386 //    {
18387 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18388 //            return;
18389 //        }
18390 //        
18391 //        this.showPanelNext();
18392 //    },
18393     
18394     
18395     getChildContainer : function()
18396     {
18397         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18398     },
18399     
18400     /**
18401     * register a Navigation item
18402     * @param {Roo.bootstrap.NavItem} the navitem to add
18403     */
18404     register : function(item)
18405     {
18406         this.tabs.push( item);
18407         item.navId = this.navId; // not really needed..
18408         this.addBullet();
18409     
18410     },
18411     
18412     getActivePanel : function()
18413     {
18414         var r = false;
18415         Roo.each(this.tabs, function(t) {
18416             if (t.active) {
18417                 r = t;
18418                 return false;
18419             }
18420             return null;
18421         });
18422         return r;
18423         
18424     },
18425     getPanelByName : function(n)
18426     {
18427         var r = false;
18428         Roo.each(this.tabs, function(t) {
18429             if (t.tabId == n) {
18430                 r = t;
18431                 return false;
18432             }
18433             return null;
18434         });
18435         return r;
18436     },
18437     indexOfPanel : function(p)
18438     {
18439         var r = false;
18440         Roo.each(this.tabs, function(t,i) {
18441             if (t.tabId == p.tabId) {
18442                 r = i;
18443                 return false;
18444             }
18445             return null;
18446         });
18447         return r;
18448     },
18449     /**
18450      * show a specific panel
18451      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18452      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18453      */
18454     showPanel : function (pan)
18455     {
18456         if(this.transition || typeof(pan) == 'undefined'){
18457             Roo.log("waiting for the transitionend");
18458             return false;
18459         }
18460         
18461         if (typeof(pan) == 'number') {
18462             pan = this.tabs[pan];
18463         }
18464         
18465         if (typeof(pan) == 'string') {
18466             pan = this.getPanelByName(pan);
18467         }
18468         
18469         var cur = this.getActivePanel();
18470         
18471         if(!pan || !cur){
18472             Roo.log('pan or acitve pan is undefined');
18473             return false;
18474         }
18475         
18476         if (pan.tabId == this.getActivePanel().tabId) {
18477             return true;
18478         }
18479         
18480         if (false === cur.fireEvent('beforedeactivate')) {
18481             return false;
18482         }
18483         
18484         if(this.bullets > 0 && !Roo.isTouch){
18485             this.setActiveBullet(this.indexOfPanel(pan));
18486         }
18487         
18488         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18489             
18490             //class="carousel-item carousel-item-next carousel-item-left"
18491             
18492             this.transition = true;
18493             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18494             var lr = dir == 'next' ? 'left' : 'right';
18495             pan.el.addClass(dir); // or prev
18496             pan.el.addClass('carousel-item-' + dir); // or prev
18497             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18498             cur.el.addClass(lr); // or right
18499             pan.el.addClass(lr);
18500             cur.el.addClass('carousel-item-' +lr); // or right
18501             pan.el.addClass('carousel-item-' +lr);
18502             
18503             
18504             var _this = this;
18505             cur.el.on('transitionend', function() {
18506                 Roo.log("trans end?");
18507                 
18508                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18509                 pan.setActive(true);
18510                 
18511                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18512                 cur.setActive(false);
18513                 
18514                 _this.transition = false;
18515                 
18516             }, this, { single:  true } );
18517             
18518             return true;
18519         }
18520         
18521         cur.setActive(false);
18522         pan.setActive(true);
18523         
18524         return true;
18525         
18526     },
18527     showPanelNext : function()
18528     {
18529         var i = this.indexOfPanel(this.getActivePanel());
18530         
18531         if (i >= this.tabs.length - 1 && !this.autoslide) {
18532             return;
18533         }
18534         
18535         if (i >= this.tabs.length - 1 && this.autoslide) {
18536             i = -1;
18537         }
18538         
18539         this.showPanel(this.tabs[i+1]);
18540     },
18541     
18542     showPanelPrev : function()
18543     {
18544         var i = this.indexOfPanel(this.getActivePanel());
18545         
18546         if (i  < 1 && !this.autoslide) {
18547             return;
18548         }
18549         
18550         if (i < 1 && this.autoslide) {
18551             i = this.tabs.length;
18552         }
18553         
18554         this.showPanel(this.tabs[i-1]);
18555     },
18556     
18557     
18558     addBullet: function()
18559     {
18560         if(!this.bullets || Roo.isTouch){
18561             return;
18562         }
18563         var ctr = this.el.select('.carousel-bullets',true).first();
18564         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18565         var bullet = ctr.createChild({
18566             cls : 'bullet bullet-' + i
18567         },ctr.dom.lastChild);
18568         
18569         
18570         var _this = this;
18571         
18572         bullet.on('click', (function(e, el, o, ii, t){
18573
18574             e.preventDefault();
18575
18576             this.showPanel(ii);
18577
18578             if(this.autoslide && this.slideFn){
18579                 clearInterval(this.slideFn);
18580                 this.slideFn = window.setInterval(function() {
18581                     _this.showPanelNext();
18582                 }, this.timer);
18583             }
18584
18585         }).createDelegate(this, [i, bullet], true));
18586                 
18587         
18588     },
18589      
18590     setActiveBullet : function(i)
18591     {
18592         if(Roo.isTouch){
18593             return;
18594         }
18595         
18596         Roo.each(this.el.select('.bullet', true).elements, function(el){
18597             el.removeClass('selected');
18598         });
18599
18600         var bullet = this.el.select('.bullet-' + i, true).first();
18601         
18602         if(!bullet){
18603             return;
18604         }
18605         
18606         bullet.addClass('selected');
18607     }
18608     
18609     
18610   
18611 });
18612
18613  
18614
18615  
18616  
18617 Roo.apply(Roo.bootstrap.TabGroup, {
18618     
18619     groups: {},
18620      /**
18621     * register a Navigation Group
18622     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18623     */
18624     register : function(navgrp)
18625     {
18626         this.groups[navgrp.navId] = navgrp;
18627         
18628     },
18629     /**
18630     * fetch a Navigation Group based on the navigation ID
18631     * if one does not exist , it will get created.
18632     * @param {string} the navgroup to add
18633     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18634     */
18635     get: function(navId) {
18636         if (typeof(this.groups[navId]) == 'undefined') {
18637             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18638         }
18639         return this.groups[navId] ;
18640     }
18641     
18642     
18643     
18644 });
18645
18646  /*
18647  * - LGPL
18648  *
18649  * TabPanel
18650  * 
18651  */
18652
18653 /**
18654  * @class Roo.bootstrap.TabPanel
18655  * @extends Roo.bootstrap.Component
18656  * Bootstrap TabPanel class
18657  * @cfg {Boolean} active panel active
18658  * @cfg {String} html panel content
18659  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18660  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18661  * @cfg {String} href click to link..
18662  * 
18663  * 
18664  * @constructor
18665  * Create a new TabPanel
18666  * @param {Object} config The config object
18667  */
18668
18669 Roo.bootstrap.TabPanel = function(config){
18670     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18671     this.addEvents({
18672         /**
18673              * @event changed
18674              * Fires when the active status changes
18675              * @param {Roo.bootstrap.TabPanel} this
18676              * @param {Boolean} state the new state
18677             
18678          */
18679         'changed': true,
18680         /**
18681              * @event beforedeactivate
18682              * Fires before a tab is de-activated - can be used to do validation on a form.
18683              * @param {Roo.bootstrap.TabPanel} this
18684              * @return {Boolean} false if there is an error
18685             
18686          */
18687         'beforedeactivate': true
18688      });
18689     
18690     this.tabId = this.tabId || Roo.id();
18691   
18692 };
18693
18694 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18695     
18696     active: false,
18697     html: false,
18698     tabId: false,
18699     navId : false,
18700     href : '',
18701     
18702     getAutoCreate : function(){
18703         
18704         
18705         var cfg = {
18706             tag: 'div',
18707             // item is needed for carousel - not sure if it has any effect otherwise
18708             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18709             html: this.html || ''
18710         };
18711         
18712         if(this.active){
18713             cfg.cls += ' active';
18714         }
18715         
18716         if(this.tabId){
18717             cfg.tabId = this.tabId;
18718         }
18719         
18720         
18721         
18722         return cfg;
18723     },
18724     
18725     initEvents:  function()
18726     {
18727         var p = this.parent();
18728         
18729         this.navId = this.navId || p.navId;
18730         
18731         if (typeof(this.navId) != 'undefined') {
18732             // not really needed.. but just in case.. parent should be a NavGroup.
18733             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18734             
18735             tg.register(this);
18736             
18737             var i = tg.tabs.length - 1;
18738             
18739             if(this.active && tg.bullets > 0 && i < tg.bullets){
18740                 tg.setActiveBullet(i);
18741             }
18742         }
18743         
18744         this.el.on('click', this.onClick, this);
18745         
18746         if(Roo.isTouch){
18747             this.el.on("touchstart", this.onTouchStart, this);
18748             this.el.on("touchmove", this.onTouchMove, this);
18749             this.el.on("touchend", this.onTouchEnd, this);
18750         }
18751         
18752     },
18753     
18754     onRender : function(ct, position)
18755     {
18756         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18757     },
18758     
18759     setActive : function(state)
18760     {
18761         Roo.log("panel - set active " + this.tabId + "=" + state);
18762         
18763         this.active = state;
18764         if (!state) {
18765             this.el.removeClass('active');
18766             
18767         } else  if (!this.el.hasClass('active')) {
18768             this.el.addClass('active');
18769         }
18770         
18771         this.fireEvent('changed', this, state);
18772     },
18773     
18774     onClick : function(e)
18775     {
18776         e.preventDefault();
18777         
18778         if(!this.href.length){
18779             return;
18780         }
18781         
18782         window.location.href = this.href;
18783     },
18784     
18785     startX : 0,
18786     startY : 0,
18787     endX : 0,
18788     endY : 0,
18789     swiping : false,
18790     
18791     onTouchStart : function(e)
18792     {
18793         this.swiping = false;
18794         
18795         this.startX = e.browserEvent.touches[0].clientX;
18796         this.startY = e.browserEvent.touches[0].clientY;
18797     },
18798     
18799     onTouchMove : function(e)
18800     {
18801         this.swiping = true;
18802         
18803         this.endX = e.browserEvent.touches[0].clientX;
18804         this.endY = e.browserEvent.touches[0].clientY;
18805     },
18806     
18807     onTouchEnd : function(e)
18808     {
18809         if(!this.swiping){
18810             this.onClick(e);
18811             return;
18812         }
18813         
18814         var tabGroup = this.parent();
18815         
18816         if(this.endX > this.startX){ // swiping right
18817             tabGroup.showPanelPrev();
18818             return;
18819         }
18820         
18821         if(this.startX > this.endX){ // swiping left
18822             tabGroup.showPanelNext();
18823             return;
18824         }
18825     }
18826     
18827     
18828 });
18829  
18830
18831  
18832
18833  /*
18834  * - LGPL
18835  *
18836  * DateField
18837  * 
18838  */
18839
18840 /**
18841  * @class Roo.bootstrap.DateField
18842  * @extends Roo.bootstrap.Input
18843  * Bootstrap DateField class
18844  * @cfg {Number} weekStart default 0
18845  * @cfg {String} viewMode default empty, (months|years)
18846  * @cfg {String} minViewMode default empty, (months|years)
18847  * @cfg {Number} startDate default -Infinity
18848  * @cfg {Number} endDate default Infinity
18849  * @cfg {Boolean} todayHighlight default false
18850  * @cfg {Boolean} todayBtn default false
18851  * @cfg {Boolean} calendarWeeks default false
18852  * @cfg {Object} daysOfWeekDisabled default empty
18853  * @cfg {Boolean} singleMode default false (true | false)
18854  * 
18855  * @cfg {Boolean} keyboardNavigation default true
18856  * @cfg {String} language default en
18857  * 
18858  * @constructor
18859  * Create a new DateField
18860  * @param {Object} config The config object
18861  */
18862
18863 Roo.bootstrap.DateField = function(config){
18864     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18865      this.addEvents({
18866             /**
18867              * @event show
18868              * Fires when this field show.
18869              * @param {Roo.bootstrap.DateField} this
18870              * @param {Mixed} date The date value
18871              */
18872             show : true,
18873             /**
18874              * @event show
18875              * Fires when this field hide.
18876              * @param {Roo.bootstrap.DateField} this
18877              * @param {Mixed} date The date value
18878              */
18879             hide : true,
18880             /**
18881              * @event select
18882              * Fires when select a date.
18883              * @param {Roo.bootstrap.DateField} this
18884              * @param {Mixed} date The date value
18885              */
18886             select : true,
18887             /**
18888              * @event beforeselect
18889              * Fires when before select a date.
18890              * @param {Roo.bootstrap.DateField} this
18891              * @param {Mixed} date The date value
18892              */
18893             beforeselect : true
18894         });
18895 };
18896
18897 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18898     
18899     /**
18900      * @cfg {String} format
18901      * The default date format string which can be overriden for localization support.  The format must be
18902      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18903      */
18904     format : "m/d/y",
18905     /**
18906      * @cfg {String} altFormats
18907      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18908      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18909      */
18910     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18911     
18912     weekStart : 0,
18913     
18914     viewMode : '',
18915     
18916     minViewMode : '',
18917     
18918     todayHighlight : false,
18919     
18920     todayBtn: false,
18921     
18922     language: 'en',
18923     
18924     keyboardNavigation: true,
18925     
18926     calendarWeeks: false,
18927     
18928     startDate: -Infinity,
18929     
18930     endDate: Infinity,
18931     
18932     daysOfWeekDisabled: [],
18933     
18934     _events: [],
18935     
18936     singleMode : false,
18937     
18938     UTCDate: function()
18939     {
18940         return new Date(Date.UTC.apply(Date, arguments));
18941     },
18942     
18943     UTCToday: function()
18944     {
18945         var today = new Date();
18946         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18947     },
18948     
18949     getDate: function() {
18950             var d = this.getUTCDate();
18951             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18952     },
18953     
18954     getUTCDate: function() {
18955             return this.date;
18956     },
18957     
18958     setDate: function(d) {
18959             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18960     },
18961     
18962     setUTCDate: function(d) {
18963             this.date = d;
18964             this.setValue(this.formatDate(this.date));
18965     },
18966         
18967     onRender: function(ct, position)
18968     {
18969         
18970         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18971         
18972         this.language = this.language || 'en';
18973         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18974         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18975         
18976         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18977         this.format = this.format || 'm/d/y';
18978         this.isInline = false;
18979         this.isInput = true;
18980         this.component = this.el.select('.add-on', true).first() || false;
18981         this.component = (this.component && this.component.length === 0) ? false : this.component;
18982         this.hasInput = this.component && this.inputEl().length;
18983         
18984         if (typeof(this.minViewMode === 'string')) {
18985             switch (this.minViewMode) {
18986                 case 'months':
18987                     this.minViewMode = 1;
18988                     break;
18989                 case 'years':
18990                     this.minViewMode = 2;
18991                     break;
18992                 default:
18993                     this.minViewMode = 0;
18994                     break;
18995             }
18996         }
18997         
18998         if (typeof(this.viewMode === 'string')) {
18999             switch (this.viewMode) {
19000                 case 'months':
19001                     this.viewMode = 1;
19002                     break;
19003                 case 'years':
19004                     this.viewMode = 2;
19005                     break;
19006                 default:
19007                     this.viewMode = 0;
19008                     break;
19009             }
19010         }
19011                 
19012         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19013         
19014 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19015         
19016         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19017         
19018         this.picker().on('mousedown', this.onMousedown, this);
19019         this.picker().on('click', this.onClick, this);
19020         
19021         this.picker().addClass('datepicker-dropdown');
19022         
19023         this.startViewMode = this.viewMode;
19024         
19025         if(this.singleMode){
19026             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19027                 v.setVisibilityMode(Roo.Element.DISPLAY);
19028                 v.hide();
19029             });
19030             
19031             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19032                 v.setStyle('width', '189px');
19033             });
19034         }
19035         
19036         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19037             if(!this.calendarWeeks){
19038                 v.remove();
19039                 return;
19040             }
19041             
19042             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19043             v.attr('colspan', function(i, val){
19044                 return parseInt(val) + 1;
19045             });
19046         });
19047                         
19048         
19049         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19050         
19051         this.setStartDate(this.startDate);
19052         this.setEndDate(this.endDate);
19053         
19054         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19055         
19056         this.fillDow();
19057         this.fillMonths();
19058         this.update();
19059         this.showMode();
19060         
19061         if(this.isInline) {
19062             this.showPopup();
19063         }
19064     },
19065     
19066     picker : function()
19067     {
19068         return this.pickerEl;
19069 //        return this.el.select('.datepicker', true).first();
19070     },
19071     
19072     fillDow: function()
19073     {
19074         var dowCnt = this.weekStart;
19075         
19076         var dow = {
19077             tag: 'tr',
19078             cn: [
19079                 
19080             ]
19081         };
19082         
19083         if(this.calendarWeeks){
19084             dow.cn.push({
19085                 tag: 'th',
19086                 cls: 'cw',
19087                 html: '&nbsp;'
19088             })
19089         }
19090         
19091         while (dowCnt < this.weekStart + 7) {
19092             dow.cn.push({
19093                 tag: 'th',
19094                 cls: 'dow',
19095                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19096             });
19097         }
19098         
19099         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19100     },
19101     
19102     fillMonths: function()
19103     {    
19104         var i = 0;
19105         var months = this.picker().select('>.datepicker-months td', true).first();
19106         
19107         months.dom.innerHTML = '';
19108         
19109         while (i < 12) {
19110             var month = {
19111                 tag: 'span',
19112                 cls: 'month',
19113                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19114             };
19115             
19116             months.createChild(month);
19117         }
19118         
19119     },
19120     
19121     update: function()
19122     {
19123         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;
19124         
19125         if (this.date < this.startDate) {
19126             this.viewDate = new Date(this.startDate);
19127         } else if (this.date > this.endDate) {
19128             this.viewDate = new Date(this.endDate);
19129         } else {
19130             this.viewDate = new Date(this.date);
19131         }
19132         
19133         this.fill();
19134     },
19135     
19136     fill: function() 
19137     {
19138         var d = new Date(this.viewDate),
19139                 year = d.getUTCFullYear(),
19140                 month = d.getUTCMonth(),
19141                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19142                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19143                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19144                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19145                 currentDate = this.date && this.date.valueOf(),
19146                 today = this.UTCToday();
19147         
19148         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19149         
19150 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19151         
19152 //        this.picker.select('>tfoot th.today').
19153 //                                              .text(dates[this.language].today)
19154 //                                              .toggle(this.todayBtn !== false);
19155     
19156         this.updateNavArrows();
19157         this.fillMonths();
19158                                                 
19159         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19160         
19161         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19162          
19163         prevMonth.setUTCDate(day);
19164         
19165         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19166         
19167         var nextMonth = new Date(prevMonth);
19168         
19169         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19170         
19171         nextMonth = nextMonth.valueOf();
19172         
19173         var fillMonths = false;
19174         
19175         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19176         
19177         while(prevMonth.valueOf() <= nextMonth) {
19178             var clsName = '';
19179             
19180             if (prevMonth.getUTCDay() === this.weekStart) {
19181                 if(fillMonths){
19182                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19183                 }
19184                     
19185                 fillMonths = {
19186                     tag: 'tr',
19187                     cn: []
19188                 };
19189                 
19190                 if(this.calendarWeeks){
19191                     // ISO 8601: First week contains first thursday.
19192                     // ISO also states week starts on Monday, but we can be more abstract here.
19193                     var
19194                     // Start of current week: based on weekstart/current date
19195                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19196                     // Thursday of this week
19197                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19198                     // First Thursday of year, year from thursday
19199                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19200                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19201                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19202                     
19203                     fillMonths.cn.push({
19204                         tag: 'td',
19205                         cls: 'cw',
19206                         html: calWeek
19207                     });
19208                 }
19209             }
19210             
19211             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19212                 clsName += ' old';
19213             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19214                 clsName += ' new';
19215             }
19216             if (this.todayHighlight &&
19217                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19218                 prevMonth.getUTCMonth() == today.getMonth() &&
19219                 prevMonth.getUTCDate() == today.getDate()) {
19220                 clsName += ' today';
19221             }
19222             
19223             if (currentDate && prevMonth.valueOf() === currentDate) {
19224                 clsName += ' active';
19225             }
19226             
19227             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19228                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19229                     clsName += ' disabled';
19230             }
19231             
19232             fillMonths.cn.push({
19233                 tag: 'td',
19234                 cls: 'day ' + clsName,
19235                 html: prevMonth.getDate()
19236             });
19237             
19238             prevMonth.setDate(prevMonth.getDate()+1);
19239         }
19240           
19241         var currentYear = this.date && this.date.getUTCFullYear();
19242         var currentMonth = this.date && this.date.getUTCMonth();
19243         
19244         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19245         
19246         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19247             v.removeClass('active');
19248             
19249             if(currentYear === year && k === currentMonth){
19250                 v.addClass('active');
19251             }
19252             
19253             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19254                 v.addClass('disabled');
19255             }
19256             
19257         });
19258         
19259         
19260         year = parseInt(year/10, 10) * 10;
19261         
19262         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19263         
19264         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19265         
19266         year -= 1;
19267         for (var i = -1; i < 11; i++) {
19268             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19269                 tag: 'span',
19270                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19271                 html: year
19272             });
19273             
19274             year += 1;
19275         }
19276     },
19277     
19278     showMode: function(dir) 
19279     {
19280         if (dir) {
19281             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19282         }
19283         
19284         Roo.each(this.picker().select('>div',true).elements, function(v){
19285             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19286             v.hide();
19287         });
19288         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19289     },
19290     
19291     place: function()
19292     {
19293         if(this.isInline) {
19294             return;
19295         }
19296         
19297         this.picker().removeClass(['bottom', 'top']);
19298         
19299         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19300             /*
19301              * place to the top of element!
19302              *
19303              */
19304             
19305             this.picker().addClass('top');
19306             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19307             
19308             return;
19309         }
19310         
19311         this.picker().addClass('bottom');
19312         
19313         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19314     },
19315     
19316     parseDate : function(value)
19317     {
19318         if(!value || value instanceof Date){
19319             return value;
19320         }
19321         var v = Date.parseDate(value, this.format);
19322         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19323             v = Date.parseDate(value, 'Y-m-d');
19324         }
19325         if(!v && this.altFormats){
19326             if(!this.altFormatsArray){
19327                 this.altFormatsArray = this.altFormats.split("|");
19328             }
19329             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19330                 v = Date.parseDate(value, this.altFormatsArray[i]);
19331             }
19332         }
19333         return v;
19334     },
19335     
19336     formatDate : function(date, fmt)
19337     {   
19338         return (!date || !(date instanceof Date)) ?
19339         date : date.dateFormat(fmt || this.format);
19340     },
19341     
19342     onFocus : function()
19343     {
19344         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19345         this.showPopup();
19346     },
19347     
19348     onBlur : function()
19349     {
19350         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19351         
19352         var d = this.inputEl().getValue();
19353         
19354         this.setValue(d);
19355                 
19356         this.hidePopup();
19357     },
19358     
19359     showPopup : function()
19360     {
19361         this.picker().show();
19362         this.update();
19363         this.place();
19364         
19365         this.fireEvent('showpopup', this, this.date);
19366     },
19367     
19368     hidePopup : function()
19369     {
19370         if(this.isInline) {
19371             return;
19372         }
19373         this.picker().hide();
19374         this.viewMode = this.startViewMode;
19375         this.showMode();
19376         
19377         this.fireEvent('hidepopup', this, this.date);
19378         
19379     },
19380     
19381     onMousedown: function(e)
19382     {
19383         e.stopPropagation();
19384         e.preventDefault();
19385     },
19386     
19387     keyup: function(e)
19388     {
19389         Roo.bootstrap.DateField.superclass.keyup.call(this);
19390         this.update();
19391     },
19392
19393     setValue: function(v)
19394     {
19395         if(this.fireEvent('beforeselect', this, v) !== false){
19396             var d = new Date(this.parseDate(v) ).clearTime();
19397         
19398             if(isNaN(d.getTime())){
19399                 this.date = this.viewDate = '';
19400                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19401                 return;
19402             }
19403
19404             v = this.formatDate(d);
19405
19406             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19407
19408             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19409
19410             this.update();
19411
19412             this.fireEvent('select', this, this.date);
19413         }
19414     },
19415     
19416     getValue: function()
19417     {
19418         return this.formatDate(this.date);
19419     },
19420     
19421     fireKey: function(e)
19422     {
19423         if (!this.picker().isVisible()){
19424             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19425                 this.showPopup();
19426             }
19427             return;
19428         }
19429         
19430         var dateChanged = false,
19431         dir, day, month,
19432         newDate, newViewDate;
19433         
19434         switch(e.keyCode){
19435             case 27: // escape
19436                 this.hidePopup();
19437                 e.preventDefault();
19438                 break;
19439             case 37: // left
19440             case 39: // right
19441                 if (!this.keyboardNavigation) {
19442                     break;
19443                 }
19444                 dir = e.keyCode == 37 ? -1 : 1;
19445                 
19446                 if (e.ctrlKey){
19447                     newDate = this.moveYear(this.date, dir);
19448                     newViewDate = this.moveYear(this.viewDate, dir);
19449                 } else if (e.shiftKey){
19450                     newDate = this.moveMonth(this.date, dir);
19451                     newViewDate = this.moveMonth(this.viewDate, dir);
19452                 } else {
19453                     newDate = new Date(this.date);
19454                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19455                     newViewDate = new Date(this.viewDate);
19456                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19457                 }
19458                 if (this.dateWithinRange(newDate)){
19459                     this.date = newDate;
19460                     this.viewDate = newViewDate;
19461                     this.setValue(this.formatDate(this.date));
19462 //                    this.update();
19463                     e.preventDefault();
19464                     dateChanged = true;
19465                 }
19466                 break;
19467             case 38: // up
19468             case 40: // down
19469                 if (!this.keyboardNavigation) {
19470                     break;
19471                 }
19472                 dir = e.keyCode == 38 ? -1 : 1;
19473                 if (e.ctrlKey){
19474                     newDate = this.moveYear(this.date, dir);
19475                     newViewDate = this.moveYear(this.viewDate, dir);
19476                 } else if (e.shiftKey){
19477                     newDate = this.moveMonth(this.date, dir);
19478                     newViewDate = this.moveMonth(this.viewDate, dir);
19479                 } else {
19480                     newDate = new Date(this.date);
19481                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19482                     newViewDate = new Date(this.viewDate);
19483                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19484                 }
19485                 if (this.dateWithinRange(newDate)){
19486                     this.date = newDate;
19487                     this.viewDate = newViewDate;
19488                     this.setValue(this.formatDate(this.date));
19489 //                    this.update();
19490                     e.preventDefault();
19491                     dateChanged = true;
19492                 }
19493                 break;
19494             case 13: // enter
19495                 this.setValue(this.formatDate(this.date));
19496                 this.hidePopup();
19497                 e.preventDefault();
19498                 break;
19499             case 9: // tab
19500                 this.setValue(this.formatDate(this.date));
19501                 this.hidePopup();
19502                 break;
19503             case 16: // shift
19504             case 17: // ctrl
19505             case 18: // alt
19506                 break;
19507             default :
19508                 this.hidePopup();
19509                 
19510         }
19511     },
19512     
19513     
19514     onClick: function(e) 
19515     {
19516         e.stopPropagation();
19517         e.preventDefault();
19518         
19519         var target = e.getTarget();
19520         
19521         if(target.nodeName.toLowerCase() === 'i'){
19522             target = Roo.get(target).dom.parentNode;
19523         }
19524         
19525         var nodeName = target.nodeName;
19526         var className = target.className;
19527         var html = target.innerHTML;
19528         //Roo.log(nodeName);
19529         
19530         switch(nodeName.toLowerCase()) {
19531             case 'th':
19532                 switch(className) {
19533                     case 'switch':
19534                         this.showMode(1);
19535                         break;
19536                     case 'prev':
19537                     case 'next':
19538                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19539                         switch(this.viewMode){
19540                                 case 0:
19541                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19542                                         break;
19543                                 case 1:
19544                                 case 2:
19545                                         this.viewDate = this.moveYear(this.viewDate, dir);
19546                                         break;
19547                         }
19548                         this.fill();
19549                         break;
19550                     case 'today':
19551                         var date = new Date();
19552                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19553 //                        this.fill()
19554                         this.setValue(this.formatDate(this.date));
19555                         
19556                         this.hidePopup();
19557                         break;
19558                 }
19559                 break;
19560             case 'span':
19561                 if (className.indexOf('disabled') < 0) {
19562                     this.viewDate.setUTCDate(1);
19563                     if (className.indexOf('month') > -1) {
19564                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19565                     } else {
19566                         var year = parseInt(html, 10) || 0;
19567                         this.viewDate.setUTCFullYear(year);
19568                         
19569                     }
19570                     
19571                     if(this.singleMode){
19572                         this.setValue(this.formatDate(this.viewDate));
19573                         this.hidePopup();
19574                         return;
19575                     }
19576                     
19577                     this.showMode(-1);
19578                     this.fill();
19579                 }
19580                 break;
19581                 
19582             case 'td':
19583                 //Roo.log(className);
19584                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19585                     var day = parseInt(html, 10) || 1;
19586                     var year = this.viewDate.getUTCFullYear(),
19587                         month = this.viewDate.getUTCMonth();
19588
19589                     if (className.indexOf('old') > -1) {
19590                         if(month === 0 ){
19591                             month = 11;
19592                             year -= 1;
19593                         }else{
19594                             month -= 1;
19595                         }
19596                     } else if (className.indexOf('new') > -1) {
19597                         if (month == 11) {
19598                             month = 0;
19599                             year += 1;
19600                         } else {
19601                             month += 1;
19602                         }
19603                     }
19604                     //Roo.log([year,month,day]);
19605                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19606                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19607 //                    this.fill();
19608                     //Roo.log(this.formatDate(this.date));
19609                     this.setValue(this.formatDate(this.date));
19610                     this.hidePopup();
19611                 }
19612                 break;
19613         }
19614     },
19615     
19616     setStartDate: function(startDate)
19617     {
19618         this.startDate = startDate || -Infinity;
19619         if (this.startDate !== -Infinity) {
19620             this.startDate = this.parseDate(this.startDate);
19621         }
19622         this.update();
19623         this.updateNavArrows();
19624     },
19625
19626     setEndDate: function(endDate)
19627     {
19628         this.endDate = endDate || Infinity;
19629         if (this.endDate !== Infinity) {
19630             this.endDate = this.parseDate(this.endDate);
19631         }
19632         this.update();
19633         this.updateNavArrows();
19634     },
19635     
19636     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19637     {
19638         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19639         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19640             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19641         }
19642         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19643             return parseInt(d, 10);
19644         });
19645         this.update();
19646         this.updateNavArrows();
19647     },
19648     
19649     updateNavArrows: function() 
19650     {
19651         if(this.singleMode){
19652             return;
19653         }
19654         
19655         var d = new Date(this.viewDate),
19656         year = d.getUTCFullYear(),
19657         month = d.getUTCMonth();
19658         
19659         Roo.each(this.picker().select('.prev', true).elements, function(v){
19660             v.show();
19661             switch (this.viewMode) {
19662                 case 0:
19663
19664                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19665                         v.hide();
19666                     }
19667                     break;
19668                 case 1:
19669                 case 2:
19670                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19671                         v.hide();
19672                     }
19673                     break;
19674             }
19675         });
19676         
19677         Roo.each(this.picker().select('.next', true).elements, function(v){
19678             v.show();
19679             switch (this.viewMode) {
19680                 case 0:
19681
19682                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19683                         v.hide();
19684                     }
19685                     break;
19686                 case 1:
19687                 case 2:
19688                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19689                         v.hide();
19690                     }
19691                     break;
19692             }
19693         })
19694     },
19695     
19696     moveMonth: function(date, dir)
19697     {
19698         if (!dir) {
19699             return date;
19700         }
19701         var new_date = new Date(date.valueOf()),
19702         day = new_date.getUTCDate(),
19703         month = new_date.getUTCMonth(),
19704         mag = Math.abs(dir),
19705         new_month, test;
19706         dir = dir > 0 ? 1 : -1;
19707         if (mag == 1){
19708             test = dir == -1
19709             // If going back one month, make sure month is not current month
19710             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19711             ? function(){
19712                 return new_date.getUTCMonth() == month;
19713             }
19714             // If going forward one month, make sure month is as expected
19715             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19716             : function(){
19717                 return new_date.getUTCMonth() != new_month;
19718             };
19719             new_month = month + dir;
19720             new_date.setUTCMonth(new_month);
19721             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19722             if (new_month < 0 || new_month > 11) {
19723                 new_month = (new_month + 12) % 12;
19724             }
19725         } else {
19726             // For magnitudes >1, move one month at a time...
19727             for (var i=0; i<mag; i++) {
19728                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19729                 new_date = this.moveMonth(new_date, dir);
19730             }
19731             // ...then reset the day, keeping it in the new month
19732             new_month = new_date.getUTCMonth();
19733             new_date.setUTCDate(day);
19734             test = function(){
19735                 return new_month != new_date.getUTCMonth();
19736             };
19737         }
19738         // Common date-resetting loop -- if date is beyond end of month, make it
19739         // end of month
19740         while (test()){
19741             new_date.setUTCDate(--day);
19742             new_date.setUTCMonth(new_month);
19743         }
19744         return new_date;
19745     },
19746
19747     moveYear: function(date, dir)
19748     {
19749         return this.moveMonth(date, dir*12);
19750     },
19751
19752     dateWithinRange: function(date)
19753     {
19754         return date >= this.startDate && date <= this.endDate;
19755     },
19756
19757     
19758     remove: function() 
19759     {
19760         this.picker().remove();
19761     },
19762     
19763     validateValue : function(value)
19764     {
19765         if(this.getVisibilityEl().hasClass('hidden')){
19766             return true;
19767         }
19768         
19769         if(value.length < 1)  {
19770             if(this.allowBlank){
19771                 return true;
19772             }
19773             return false;
19774         }
19775         
19776         if(value.length < this.minLength){
19777             return false;
19778         }
19779         if(value.length > this.maxLength){
19780             return false;
19781         }
19782         if(this.vtype){
19783             var vt = Roo.form.VTypes;
19784             if(!vt[this.vtype](value, this)){
19785                 return false;
19786             }
19787         }
19788         if(typeof this.validator == "function"){
19789             var msg = this.validator(value);
19790             if(msg !== true){
19791                 return false;
19792             }
19793         }
19794         
19795         if(this.regex && !this.regex.test(value)){
19796             return false;
19797         }
19798         
19799         if(typeof(this.parseDate(value)) == 'undefined'){
19800             return false;
19801         }
19802         
19803         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19804             return false;
19805         }      
19806         
19807         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19808             return false;
19809         } 
19810         
19811         
19812         return true;
19813     },
19814     
19815     reset : function()
19816     {
19817         this.date = this.viewDate = '';
19818         
19819         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19820     }
19821    
19822 });
19823
19824 Roo.apply(Roo.bootstrap.DateField,  {
19825     
19826     head : {
19827         tag: 'thead',
19828         cn: [
19829         {
19830             tag: 'tr',
19831             cn: [
19832             {
19833                 tag: 'th',
19834                 cls: 'prev',
19835                 html: '<i class="fa fa-arrow-left"/>'
19836             },
19837             {
19838                 tag: 'th',
19839                 cls: 'switch',
19840                 colspan: '5'
19841             },
19842             {
19843                 tag: 'th',
19844                 cls: 'next',
19845                 html: '<i class="fa fa-arrow-right"/>'
19846             }
19847
19848             ]
19849         }
19850         ]
19851     },
19852     
19853     content : {
19854         tag: 'tbody',
19855         cn: [
19856         {
19857             tag: 'tr',
19858             cn: [
19859             {
19860                 tag: 'td',
19861                 colspan: '7'
19862             }
19863             ]
19864         }
19865         ]
19866     },
19867     
19868     footer : {
19869         tag: 'tfoot',
19870         cn: [
19871         {
19872             tag: 'tr',
19873             cn: [
19874             {
19875                 tag: 'th',
19876                 colspan: '7',
19877                 cls: 'today'
19878             }
19879                     
19880             ]
19881         }
19882         ]
19883     },
19884     
19885     dates:{
19886         en: {
19887             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19888             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19889             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19890             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19891             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19892             today: "Today"
19893         }
19894     },
19895     
19896     modes: [
19897     {
19898         clsName: 'days',
19899         navFnc: 'Month',
19900         navStep: 1
19901     },
19902     {
19903         clsName: 'months',
19904         navFnc: 'FullYear',
19905         navStep: 1
19906     },
19907     {
19908         clsName: 'years',
19909         navFnc: 'FullYear',
19910         navStep: 10
19911     }]
19912 });
19913
19914 Roo.apply(Roo.bootstrap.DateField,  {
19915   
19916     template : {
19917         tag: 'div',
19918         cls: 'datepicker dropdown-menu roo-dynamic',
19919         cn: [
19920         {
19921             tag: 'div',
19922             cls: 'datepicker-days',
19923             cn: [
19924             {
19925                 tag: 'table',
19926                 cls: 'table-condensed',
19927                 cn:[
19928                 Roo.bootstrap.DateField.head,
19929                 {
19930                     tag: 'tbody'
19931                 },
19932                 Roo.bootstrap.DateField.footer
19933                 ]
19934             }
19935             ]
19936         },
19937         {
19938             tag: 'div',
19939             cls: 'datepicker-months',
19940             cn: [
19941             {
19942                 tag: 'table',
19943                 cls: 'table-condensed',
19944                 cn:[
19945                 Roo.bootstrap.DateField.head,
19946                 Roo.bootstrap.DateField.content,
19947                 Roo.bootstrap.DateField.footer
19948                 ]
19949             }
19950             ]
19951         },
19952         {
19953             tag: 'div',
19954             cls: 'datepicker-years',
19955             cn: [
19956             {
19957                 tag: 'table',
19958                 cls: 'table-condensed',
19959                 cn:[
19960                 Roo.bootstrap.DateField.head,
19961                 Roo.bootstrap.DateField.content,
19962                 Roo.bootstrap.DateField.footer
19963                 ]
19964             }
19965             ]
19966         }
19967         ]
19968     }
19969 });
19970
19971  
19972
19973  /*
19974  * - LGPL
19975  *
19976  * TimeField
19977  * 
19978  */
19979
19980 /**
19981  * @class Roo.bootstrap.TimeField
19982  * @extends Roo.bootstrap.Input
19983  * Bootstrap DateField class
19984  * 
19985  * 
19986  * @constructor
19987  * Create a new TimeField
19988  * @param {Object} config The config object
19989  */
19990
19991 Roo.bootstrap.TimeField = function(config){
19992     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19993     this.addEvents({
19994             /**
19995              * @event show
19996              * Fires when this field show.
19997              * @param {Roo.bootstrap.DateField} thisthis
19998              * @param {Mixed} date The date value
19999              */
20000             show : true,
20001             /**
20002              * @event show
20003              * Fires when this field hide.
20004              * @param {Roo.bootstrap.DateField} this
20005              * @param {Mixed} date The date value
20006              */
20007             hide : true,
20008             /**
20009              * @event select
20010              * Fires when select a date.
20011              * @param {Roo.bootstrap.DateField} this
20012              * @param {Mixed} date The date value
20013              */
20014             select : true
20015         });
20016 };
20017
20018 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
20019     
20020     /**
20021      * @cfg {String} format
20022      * The default time format string which can be overriden for localization support.  The format must be
20023      * valid according to {@link Date#parseDate} (defaults to 'H:i').
20024      */
20025     format : "H:i",
20026        
20027     onRender: function(ct, position)
20028     {
20029         
20030         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20031                 
20032         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20033         
20034         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20035         
20036         this.pop = this.picker().select('>.datepicker-time',true).first();
20037         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20038         
20039         this.picker().on('mousedown', this.onMousedown, this);
20040         this.picker().on('click', this.onClick, this);
20041         
20042         this.picker().addClass('datepicker-dropdown');
20043     
20044         this.fillTime();
20045         this.update();
20046             
20047         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20048         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20049         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20050         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20051         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20052         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20053
20054     },
20055     
20056     fireKey: function(e){
20057         if (!this.picker().isVisible()){
20058             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20059                 this.show();
20060             }
20061             return;
20062         }
20063
20064         e.preventDefault();
20065         
20066         switch(e.keyCode){
20067             case 27: // escape
20068                 this.hide();
20069                 break;
20070             case 37: // left
20071             case 39: // right
20072                 this.onTogglePeriod();
20073                 break;
20074             case 38: // up
20075                 this.onIncrementMinutes();
20076                 break;
20077             case 40: // down
20078                 this.onDecrementMinutes();
20079                 break;
20080             case 13: // enter
20081             case 9: // tab
20082                 this.setTime();
20083                 break;
20084         }
20085     },
20086     
20087     onClick: function(e) {
20088         e.stopPropagation();
20089         e.preventDefault();
20090     },
20091     
20092     picker : function()
20093     {
20094         return this.el.select('.datepicker', true).first();
20095     },
20096     
20097     fillTime: function()
20098     {    
20099         var time = this.pop.select('tbody', true).first();
20100         
20101         time.dom.innerHTML = '';
20102         
20103         time.createChild({
20104             tag: 'tr',
20105             cn: [
20106                 {
20107                     tag: 'td',
20108                     cn: [
20109                         {
20110                             tag: 'a',
20111                             href: '#',
20112                             cls: 'btn',
20113                             cn: [
20114                                 {
20115                                     tag: 'span',
20116                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20117                                 }
20118                             ]
20119                         } 
20120                     ]
20121                 },
20122                 {
20123                     tag: 'td',
20124                     cls: 'separator'
20125                 },
20126                 {
20127                     tag: 'td',
20128                     cn: [
20129                         {
20130                             tag: 'a',
20131                             href: '#',
20132                             cls: 'btn',
20133                             cn: [
20134                                 {
20135                                     tag: 'span',
20136                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20137                                 }
20138                             ]
20139                         }
20140                     ]
20141                 },
20142                 {
20143                     tag: 'td',
20144                     cls: 'separator'
20145                 }
20146             ]
20147         });
20148         
20149         time.createChild({
20150             tag: 'tr',
20151             cn: [
20152                 {
20153                     tag: 'td',
20154                     cn: [
20155                         {
20156                             tag: 'span',
20157                             cls: 'timepicker-hour',
20158                             html: '00'
20159                         }  
20160                     ]
20161                 },
20162                 {
20163                     tag: 'td',
20164                     cls: 'separator',
20165                     html: ':'
20166                 },
20167                 {
20168                     tag: 'td',
20169                     cn: [
20170                         {
20171                             tag: 'span',
20172                             cls: 'timepicker-minute',
20173                             html: '00'
20174                         }  
20175                     ]
20176                 },
20177                 {
20178                     tag: 'td',
20179                     cls: 'separator'
20180                 },
20181                 {
20182                     tag: 'td',
20183                     cn: [
20184                         {
20185                             tag: 'button',
20186                             type: 'button',
20187                             cls: 'btn btn-primary period',
20188                             html: 'AM'
20189                             
20190                         }
20191                     ]
20192                 }
20193             ]
20194         });
20195         
20196         time.createChild({
20197             tag: 'tr',
20198             cn: [
20199                 {
20200                     tag: 'td',
20201                     cn: [
20202                         {
20203                             tag: 'a',
20204                             href: '#',
20205                             cls: 'btn',
20206                             cn: [
20207                                 {
20208                                     tag: 'span',
20209                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20210                                 }
20211                             ]
20212                         }
20213                     ]
20214                 },
20215                 {
20216                     tag: 'td',
20217                     cls: 'separator'
20218                 },
20219                 {
20220                     tag: 'td',
20221                     cn: [
20222                         {
20223                             tag: 'a',
20224                             href: '#',
20225                             cls: 'btn',
20226                             cn: [
20227                                 {
20228                                     tag: 'span',
20229                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20230                                 }
20231                             ]
20232                         }
20233                     ]
20234                 },
20235                 {
20236                     tag: 'td',
20237                     cls: 'separator'
20238                 }
20239             ]
20240         });
20241         
20242     },
20243     
20244     update: function()
20245     {
20246         
20247         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20248         
20249         this.fill();
20250     },
20251     
20252     fill: function() 
20253     {
20254         var hours = this.time.getHours();
20255         var minutes = this.time.getMinutes();
20256         var period = 'AM';
20257         
20258         if(hours > 11){
20259             period = 'PM';
20260         }
20261         
20262         if(hours == 0){
20263             hours = 12;
20264         }
20265         
20266         
20267         if(hours > 12){
20268             hours = hours - 12;
20269         }
20270         
20271         if(hours < 10){
20272             hours = '0' + hours;
20273         }
20274         
20275         if(minutes < 10){
20276             minutes = '0' + minutes;
20277         }
20278         
20279         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20280         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20281         this.pop.select('button', true).first().dom.innerHTML = period;
20282         
20283     },
20284     
20285     place: function()
20286     {   
20287         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20288         
20289         var cls = ['bottom'];
20290         
20291         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20292             cls.pop();
20293             cls.push('top');
20294         }
20295         
20296         cls.push('right');
20297         
20298         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20299             cls.pop();
20300             cls.push('left');
20301         }
20302         
20303         this.picker().addClass(cls.join('-'));
20304         
20305         var _this = this;
20306         
20307         Roo.each(cls, function(c){
20308             if(c == 'bottom'){
20309                 _this.picker().setTop(_this.inputEl().getHeight());
20310                 return;
20311             }
20312             if(c == 'top'){
20313                 _this.picker().setTop(0 - _this.picker().getHeight());
20314                 return;
20315             }
20316             
20317             if(c == 'left'){
20318                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20319                 return;
20320             }
20321             if(c == 'right'){
20322                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20323                 return;
20324             }
20325         });
20326         
20327     },
20328   
20329     onFocus : function()
20330     {
20331         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20332         this.show();
20333     },
20334     
20335     onBlur : function()
20336     {
20337         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20338         this.hide();
20339     },
20340     
20341     show : function()
20342     {
20343         this.picker().show();
20344         this.pop.show();
20345         this.update();
20346         this.place();
20347         
20348         this.fireEvent('show', this, this.date);
20349     },
20350     
20351     hide : function()
20352     {
20353         this.picker().hide();
20354         this.pop.hide();
20355         
20356         this.fireEvent('hide', this, this.date);
20357     },
20358     
20359     setTime : function()
20360     {
20361         this.hide();
20362         this.setValue(this.time.format(this.format));
20363         
20364         this.fireEvent('select', this, this.date);
20365         
20366         
20367     },
20368     
20369     onMousedown: function(e){
20370         e.stopPropagation();
20371         e.preventDefault();
20372     },
20373     
20374     onIncrementHours: function()
20375     {
20376         Roo.log('onIncrementHours');
20377         this.time = this.time.add(Date.HOUR, 1);
20378         this.update();
20379         
20380     },
20381     
20382     onDecrementHours: function()
20383     {
20384         Roo.log('onDecrementHours');
20385         this.time = this.time.add(Date.HOUR, -1);
20386         this.update();
20387     },
20388     
20389     onIncrementMinutes: function()
20390     {
20391         Roo.log('onIncrementMinutes');
20392         this.time = this.time.add(Date.MINUTE, 1);
20393         this.update();
20394     },
20395     
20396     onDecrementMinutes: function()
20397     {
20398         Roo.log('onDecrementMinutes');
20399         this.time = this.time.add(Date.MINUTE, -1);
20400         this.update();
20401     },
20402     
20403     onTogglePeriod: function()
20404     {
20405         Roo.log('onTogglePeriod');
20406         this.time = this.time.add(Date.HOUR, 12);
20407         this.update();
20408     }
20409     
20410    
20411 });
20412
20413 Roo.apply(Roo.bootstrap.TimeField,  {
20414     
20415     content : {
20416         tag: 'tbody',
20417         cn: [
20418             {
20419                 tag: 'tr',
20420                 cn: [
20421                 {
20422                     tag: 'td',
20423                     colspan: '7'
20424                 }
20425                 ]
20426             }
20427         ]
20428     },
20429     
20430     footer : {
20431         tag: 'tfoot',
20432         cn: [
20433             {
20434                 tag: 'tr',
20435                 cn: [
20436                 {
20437                     tag: 'th',
20438                     colspan: '7',
20439                     cls: '',
20440                     cn: [
20441                         {
20442                             tag: 'button',
20443                             cls: 'btn btn-info ok',
20444                             html: 'OK'
20445                         }
20446                     ]
20447                 }
20448
20449                 ]
20450             }
20451         ]
20452     }
20453 });
20454
20455 Roo.apply(Roo.bootstrap.TimeField,  {
20456   
20457     template : {
20458         tag: 'div',
20459         cls: 'datepicker dropdown-menu',
20460         cn: [
20461             {
20462                 tag: 'div',
20463                 cls: 'datepicker-time',
20464                 cn: [
20465                 {
20466                     tag: 'table',
20467                     cls: 'table-condensed',
20468                     cn:[
20469                     Roo.bootstrap.TimeField.content,
20470                     Roo.bootstrap.TimeField.footer
20471                     ]
20472                 }
20473                 ]
20474             }
20475         ]
20476     }
20477 });
20478
20479  
20480
20481  /*
20482  * - LGPL
20483  *
20484  * MonthField
20485  * 
20486  */
20487
20488 /**
20489  * @class Roo.bootstrap.MonthField
20490  * @extends Roo.bootstrap.Input
20491  * Bootstrap MonthField class
20492  * 
20493  * @cfg {String} language default en
20494  * 
20495  * @constructor
20496  * Create a new MonthField
20497  * @param {Object} config The config object
20498  */
20499
20500 Roo.bootstrap.MonthField = function(config){
20501     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20502     
20503     this.addEvents({
20504         /**
20505          * @event show
20506          * Fires when this field show.
20507          * @param {Roo.bootstrap.MonthField} this
20508          * @param {Mixed} date The date value
20509          */
20510         show : true,
20511         /**
20512          * @event show
20513          * Fires when this field hide.
20514          * @param {Roo.bootstrap.MonthField} this
20515          * @param {Mixed} date The date value
20516          */
20517         hide : true,
20518         /**
20519          * @event select
20520          * Fires when select a date.
20521          * @param {Roo.bootstrap.MonthField} this
20522          * @param {String} oldvalue The old value
20523          * @param {String} newvalue The new value
20524          */
20525         select : true
20526     });
20527 };
20528
20529 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20530     
20531     onRender: function(ct, position)
20532     {
20533         
20534         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20535         
20536         this.language = this.language || 'en';
20537         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20538         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20539         
20540         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20541         this.isInline = false;
20542         this.isInput = true;
20543         this.component = this.el.select('.add-on', true).first() || false;
20544         this.component = (this.component && this.component.length === 0) ? false : this.component;
20545         this.hasInput = this.component && this.inputEL().length;
20546         
20547         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20548         
20549         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20550         
20551         this.picker().on('mousedown', this.onMousedown, this);
20552         this.picker().on('click', this.onClick, this);
20553         
20554         this.picker().addClass('datepicker-dropdown');
20555         
20556         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20557             v.setStyle('width', '189px');
20558         });
20559         
20560         this.fillMonths();
20561         
20562         this.update();
20563         
20564         if(this.isInline) {
20565             this.show();
20566         }
20567         
20568     },
20569     
20570     setValue: function(v, suppressEvent)
20571     {   
20572         var o = this.getValue();
20573         
20574         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20575         
20576         this.update();
20577
20578         if(suppressEvent !== true){
20579             this.fireEvent('select', this, o, v);
20580         }
20581         
20582     },
20583     
20584     getValue: function()
20585     {
20586         return this.value;
20587     },
20588     
20589     onClick: function(e) 
20590     {
20591         e.stopPropagation();
20592         e.preventDefault();
20593         
20594         var target = e.getTarget();
20595         
20596         if(target.nodeName.toLowerCase() === 'i'){
20597             target = Roo.get(target).dom.parentNode;
20598         }
20599         
20600         var nodeName = target.nodeName;
20601         var className = target.className;
20602         var html = target.innerHTML;
20603         
20604         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20605             return;
20606         }
20607         
20608         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20609         
20610         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20611         
20612         this.hide();
20613                         
20614     },
20615     
20616     picker : function()
20617     {
20618         return this.pickerEl;
20619     },
20620     
20621     fillMonths: function()
20622     {    
20623         var i = 0;
20624         var months = this.picker().select('>.datepicker-months td', true).first();
20625         
20626         months.dom.innerHTML = '';
20627         
20628         while (i < 12) {
20629             var month = {
20630                 tag: 'span',
20631                 cls: 'month',
20632                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20633             };
20634             
20635             months.createChild(month);
20636         }
20637         
20638     },
20639     
20640     update: function()
20641     {
20642         var _this = this;
20643         
20644         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20645             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20646         }
20647         
20648         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20649             e.removeClass('active');
20650             
20651             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20652                 e.addClass('active');
20653             }
20654         })
20655     },
20656     
20657     place: function()
20658     {
20659         if(this.isInline) {
20660             return;
20661         }
20662         
20663         this.picker().removeClass(['bottom', 'top']);
20664         
20665         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20666             /*
20667              * place to the top of element!
20668              *
20669              */
20670             
20671             this.picker().addClass('top');
20672             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20673             
20674             return;
20675         }
20676         
20677         this.picker().addClass('bottom');
20678         
20679         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20680     },
20681     
20682     onFocus : function()
20683     {
20684         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20685         this.show();
20686     },
20687     
20688     onBlur : function()
20689     {
20690         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20691         
20692         var d = this.inputEl().getValue();
20693         
20694         this.setValue(d);
20695                 
20696         this.hide();
20697     },
20698     
20699     show : function()
20700     {
20701         this.picker().show();
20702         this.picker().select('>.datepicker-months', true).first().show();
20703         this.update();
20704         this.place();
20705         
20706         this.fireEvent('show', this, this.date);
20707     },
20708     
20709     hide : function()
20710     {
20711         if(this.isInline) {
20712             return;
20713         }
20714         this.picker().hide();
20715         this.fireEvent('hide', this, this.date);
20716         
20717     },
20718     
20719     onMousedown: function(e)
20720     {
20721         e.stopPropagation();
20722         e.preventDefault();
20723     },
20724     
20725     keyup: function(e)
20726     {
20727         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20728         this.update();
20729     },
20730
20731     fireKey: function(e)
20732     {
20733         if (!this.picker().isVisible()){
20734             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20735                 this.show();
20736             }
20737             return;
20738         }
20739         
20740         var dir;
20741         
20742         switch(e.keyCode){
20743             case 27: // escape
20744                 this.hide();
20745                 e.preventDefault();
20746                 break;
20747             case 37: // left
20748             case 39: // right
20749                 dir = e.keyCode == 37 ? -1 : 1;
20750                 
20751                 this.vIndex = this.vIndex + dir;
20752                 
20753                 if(this.vIndex < 0){
20754                     this.vIndex = 0;
20755                 }
20756                 
20757                 if(this.vIndex > 11){
20758                     this.vIndex = 11;
20759                 }
20760                 
20761                 if(isNaN(this.vIndex)){
20762                     this.vIndex = 0;
20763                 }
20764                 
20765                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20766                 
20767                 break;
20768             case 38: // up
20769             case 40: // down
20770                 
20771                 dir = e.keyCode == 38 ? -1 : 1;
20772                 
20773                 this.vIndex = this.vIndex + dir * 4;
20774                 
20775                 if(this.vIndex < 0){
20776                     this.vIndex = 0;
20777                 }
20778                 
20779                 if(this.vIndex > 11){
20780                     this.vIndex = 11;
20781                 }
20782                 
20783                 if(isNaN(this.vIndex)){
20784                     this.vIndex = 0;
20785                 }
20786                 
20787                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20788                 break;
20789                 
20790             case 13: // enter
20791                 
20792                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20793                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20794                 }
20795                 
20796                 this.hide();
20797                 e.preventDefault();
20798                 break;
20799             case 9: // tab
20800                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20801                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20802                 }
20803                 this.hide();
20804                 break;
20805             case 16: // shift
20806             case 17: // ctrl
20807             case 18: // alt
20808                 break;
20809             default :
20810                 this.hide();
20811                 
20812         }
20813     },
20814     
20815     remove: function() 
20816     {
20817         this.picker().remove();
20818     }
20819    
20820 });
20821
20822 Roo.apply(Roo.bootstrap.MonthField,  {
20823     
20824     content : {
20825         tag: 'tbody',
20826         cn: [
20827         {
20828             tag: 'tr',
20829             cn: [
20830             {
20831                 tag: 'td',
20832                 colspan: '7'
20833             }
20834             ]
20835         }
20836         ]
20837     },
20838     
20839     dates:{
20840         en: {
20841             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20842             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20843         }
20844     }
20845 });
20846
20847 Roo.apply(Roo.bootstrap.MonthField,  {
20848   
20849     template : {
20850         tag: 'div',
20851         cls: 'datepicker dropdown-menu roo-dynamic',
20852         cn: [
20853             {
20854                 tag: 'div',
20855                 cls: 'datepicker-months',
20856                 cn: [
20857                 {
20858                     tag: 'table',
20859                     cls: 'table-condensed',
20860                     cn:[
20861                         Roo.bootstrap.DateField.content
20862                     ]
20863                 }
20864                 ]
20865             }
20866         ]
20867     }
20868 });
20869
20870  
20871
20872  
20873  /*
20874  * - LGPL
20875  *
20876  * CheckBox
20877  * 
20878  */
20879
20880 /**
20881  * @class Roo.bootstrap.CheckBox
20882  * @extends Roo.bootstrap.Input
20883  * Bootstrap CheckBox class
20884  * 
20885  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20886  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20887  * @cfg {String} boxLabel The text that appears beside the checkbox
20888  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20889  * @cfg {Boolean} checked initnal the element
20890  * @cfg {Boolean} inline inline the element (default false)
20891  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20892  * @cfg {String} tooltip label tooltip
20893  * 
20894  * @constructor
20895  * Create a new CheckBox
20896  * @param {Object} config The config object
20897  */
20898
20899 Roo.bootstrap.CheckBox = function(config){
20900     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20901    
20902     this.addEvents({
20903         /**
20904         * @event check
20905         * Fires when the element is checked or unchecked.
20906         * @param {Roo.bootstrap.CheckBox} this This input
20907         * @param {Boolean} checked The new checked value
20908         */
20909        check : true,
20910        /**
20911         * @event click
20912         * Fires when the element is click.
20913         * @param {Roo.bootstrap.CheckBox} this This input
20914         */
20915        click : true
20916     });
20917     
20918 };
20919
20920 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20921   
20922     inputType: 'checkbox',
20923     inputValue: 1,
20924     valueOff: 0,
20925     boxLabel: false,
20926     checked: false,
20927     weight : false,
20928     inline: false,
20929     tooltip : '',
20930     
20931     getAutoCreate : function()
20932     {
20933         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20934         
20935         var id = Roo.id();
20936         
20937         var cfg = {};
20938         
20939         cfg.cls = 'form-group ' + this.inputType; //input-group
20940         
20941         if(this.inline){
20942             cfg.cls += ' ' + this.inputType + '-inline';
20943         }
20944         
20945         var input =  {
20946             tag: 'input',
20947             id : id,
20948             type : this.inputType,
20949             value : this.inputValue,
20950             cls : 'roo-' + this.inputType, //'form-box',
20951             placeholder : this.placeholder || ''
20952             
20953         };
20954         
20955         if(this.inputType != 'radio'){
20956             var hidden =  {
20957                 tag: 'input',
20958                 type : 'hidden',
20959                 cls : 'roo-hidden-value',
20960                 value : this.checked ? this.inputValue : this.valueOff
20961             };
20962         }
20963         
20964             
20965         if (this.weight) { // Validity check?
20966             cfg.cls += " " + this.inputType + "-" + this.weight;
20967         }
20968         
20969         if (this.disabled) {
20970             input.disabled=true;
20971         }
20972         
20973         if(this.checked){
20974             input.checked = this.checked;
20975         }
20976         
20977         if (this.name) {
20978             
20979             input.name = this.name;
20980             
20981             if(this.inputType != 'radio'){
20982                 hidden.name = this.name;
20983                 input.name = '_hidden_' + this.name;
20984             }
20985         }
20986         
20987         if (this.size) {
20988             input.cls += ' input-' + this.size;
20989         }
20990         
20991         var settings=this;
20992         
20993         ['xs','sm','md','lg'].map(function(size){
20994             if (settings[size]) {
20995                 cfg.cls += ' col-' + size + '-' + settings[size];
20996             }
20997         });
20998         
20999         var inputblock = input;
21000          
21001         if (this.before || this.after) {
21002             
21003             inputblock = {
21004                 cls : 'input-group',
21005                 cn :  [] 
21006             };
21007             
21008             if (this.before) {
21009                 inputblock.cn.push({
21010                     tag :'span',
21011                     cls : 'input-group-addon',
21012                     html : this.before
21013                 });
21014             }
21015             
21016             inputblock.cn.push(input);
21017             
21018             if(this.inputType != 'radio'){
21019                 inputblock.cn.push(hidden);
21020             }
21021             
21022             if (this.after) {
21023                 inputblock.cn.push({
21024                     tag :'span',
21025                     cls : 'input-group-addon',
21026                     html : this.after
21027                 });
21028             }
21029             
21030         }
21031         
21032         if (align ==='left' && this.fieldLabel.length) {
21033 //                Roo.log("left and has label");
21034             cfg.cn = [
21035                 {
21036                     tag: 'label',
21037                     'for' :  id,
21038                     cls : 'control-label',
21039                     html : this.fieldLabel
21040                 },
21041                 {
21042                     cls : "", 
21043                     cn: [
21044                         inputblock
21045                     ]
21046                 }
21047             ];
21048             
21049             if(this.labelWidth > 12){
21050                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21051             }
21052             
21053             if(this.labelWidth < 13 && this.labelmd == 0){
21054                 this.labelmd = this.labelWidth;
21055             }
21056             
21057             if(this.labellg > 0){
21058                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21059                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21060             }
21061             
21062             if(this.labelmd > 0){
21063                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21064                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21065             }
21066             
21067             if(this.labelsm > 0){
21068                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21069                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21070             }
21071             
21072             if(this.labelxs > 0){
21073                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21074                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21075             }
21076             
21077         } else if ( this.fieldLabel.length) {
21078 //                Roo.log(" label");
21079                 cfg.cn = [
21080                    
21081                     {
21082                         tag: this.boxLabel ? 'span' : 'label',
21083                         'for': id,
21084                         cls: 'control-label box-input-label',
21085                         //cls : 'input-group-addon',
21086                         html : this.fieldLabel
21087                     },
21088                     
21089                     inputblock
21090                     
21091                 ];
21092
21093         } else {
21094             
21095 //                Roo.log(" no label && no align");
21096                 cfg.cn = [  inputblock ] ;
21097                 
21098                 
21099         }
21100         
21101         if(this.boxLabel){
21102              var boxLabelCfg = {
21103                 tag: 'label',
21104                 //'for': id, // box label is handled by onclick - so no for...
21105                 cls: 'box-label',
21106                 html: this.boxLabel
21107             };
21108             
21109             if(this.tooltip){
21110                 boxLabelCfg.tooltip = this.tooltip;
21111             }
21112              
21113             cfg.cn.push(boxLabelCfg);
21114         }
21115         
21116         if(this.inputType != 'radio'){
21117             cfg.cn.push(hidden);
21118         }
21119         
21120         return cfg;
21121         
21122     },
21123     
21124     /**
21125      * return the real input element.
21126      */
21127     inputEl: function ()
21128     {
21129         return this.el.select('input.roo-' + this.inputType,true).first();
21130     },
21131     hiddenEl: function ()
21132     {
21133         return this.el.select('input.roo-hidden-value',true).first();
21134     },
21135     
21136     labelEl: function()
21137     {
21138         return this.el.select('label.control-label',true).first();
21139     },
21140     /* depricated... */
21141     
21142     label: function()
21143     {
21144         return this.labelEl();
21145     },
21146     
21147     boxLabelEl: function()
21148     {
21149         return this.el.select('label.box-label',true).first();
21150     },
21151     
21152     initEvents : function()
21153     {
21154 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21155         
21156         this.inputEl().on('click', this.onClick,  this);
21157         
21158         if (this.boxLabel) { 
21159             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21160         }
21161         
21162         this.startValue = this.getValue();
21163         
21164         if(this.groupId){
21165             Roo.bootstrap.CheckBox.register(this);
21166         }
21167     },
21168     
21169     onClick : function(e)
21170     {   
21171         if(this.fireEvent('click', this, e) !== false){
21172             this.setChecked(!this.checked);
21173         }
21174         
21175     },
21176     
21177     setChecked : function(state,suppressEvent)
21178     {
21179         this.startValue = this.getValue();
21180
21181         if(this.inputType == 'radio'){
21182             
21183             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21184                 e.dom.checked = false;
21185             });
21186             
21187             this.inputEl().dom.checked = true;
21188             
21189             this.inputEl().dom.value = this.inputValue;
21190             
21191             if(suppressEvent !== true){
21192                 this.fireEvent('check', this, true);
21193             }
21194             
21195             this.validate();
21196             
21197             return;
21198         }
21199         
21200         this.checked = state;
21201         
21202         this.inputEl().dom.checked = state;
21203         
21204         
21205         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21206         
21207         if(suppressEvent !== true){
21208             this.fireEvent('check', this, state);
21209         }
21210         
21211         this.validate();
21212     },
21213     
21214     getValue : function()
21215     {
21216         if(this.inputType == 'radio'){
21217             return this.getGroupValue();
21218         }
21219         
21220         return this.hiddenEl().dom.value;
21221         
21222     },
21223     
21224     getGroupValue : function()
21225     {
21226         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21227             return '';
21228         }
21229         
21230         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21231     },
21232     
21233     setValue : function(v,suppressEvent)
21234     {
21235         if(this.inputType == 'radio'){
21236             this.setGroupValue(v, suppressEvent);
21237             return;
21238         }
21239         
21240         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21241         
21242         this.validate();
21243     },
21244     
21245     setGroupValue : function(v, suppressEvent)
21246     {
21247         this.startValue = this.getValue();
21248         
21249         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21250             e.dom.checked = false;
21251             
21252             if(e.dom.value == v){
21253                 e.dom.checked = true;
21254             }
21255         });
21256         
21257         if(suppressEvent !== true){
21258             this.fireEvent('check', this, true);
21259         }
21260
21261         this.validate();
21262         
21263         return;
21264     },
21265     
21266     validate : function()
21267     {
21268         if(this.getVisibilityEl().hasClass('hidden')){
21269             return true;
21270         }
21271         
21272         if(
21273                 this.disabled || 
21274                 (this.inputType == 'radio' && this.validateRadio()) ||
21275                 (this.inputType == 'checkbox' && this.validateCheckbox())
21276         ){
21277             this.markValid();
21278             return true;
21279         }
21280         
21281         this.markInvalid();
21282         return false;
21283     },
21284     
21285     validateRadio : function()
21286     {
21287         if(this.getVisibilityEl().hasClass('hidden')){
21288             return true;
21289         }
21290         
21291         if(this.allowBlank){
21292             return true;
21293         }
21294         
21295         var valid = false;
21296         
21297         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21298             if(!e.dom.checked){
21299                 return;
21300             }
21301             
21302             valid = true;
21303             
21304             return false;
21305         });
21306         
21307         return valid;
21308     },
21309     
21310     validateCheckbox : function()
21311     {
21312         if(!this.groupId){
21313             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21314             //return (this.getValue() == this.inputValue) ? true : false;
21315         }
21316         
21317         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21318         
21319         if(!group){
21320             return false;
21321         }
21322         
21323         var r = false;
21324         
21325         for(var i in group){
21326             if(group[i].el.isVisible(true)){
21327                 r = false;
21328                 break;
21329             }
21330             
21331             r = true;
21332         }
21333         
21334         for(var i in group){
21335             if(r){
21336                 break;
21337             }
21338             
21339             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21340         }
21341         
21342         return r;
21343     },
21344     
21345     /**
21346      * Mark this field as valid
21347      */
21348     markValid : function()
21349     {
21350         var _this = this;
21351         
21352         this.fireEvent('valid', this);
21353         
21354         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21355         
21356         if(this.groupId){
21357             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21358         }
21359         
21360         if(label){
21361             label.markValid();
21362         }
21363
21364         if(this.inputType == 'radio'){
21365             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21366                 var fg = e.findParent('.form-group', false, true);
21367                 if (Roo.bootstrap.version == 3) {
21368                     fg.removeClass([_this.invalidClass, _this.validClass]);
21369                     fg.addClass(_this.validClass);
21370                 } else {
21371                     fg.removeClass(['is-valid', 'is-invalid']);
21372                     fg.addClass('is-valid');
21373                 }
21374             });
21375             
21376             return;
21377         }
21378
21379         if(!this.groupId){
21380             var fg = this.el.findParent('.form-group', false, true);
21381             if (Roo.bootstrap.version == 3) {
21382                 fg.removeClass([this.invalidClass, this.validClass]);
21383                 fg.addClass(this.validClass);
21384             } else {
21385                 fg.removeClass(['is-valid', 'is-invalid']);
21386                 fg.addClass('is-valid');
21387             }
21388             return;
21389         }
21390         
21391         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21392         
21393         if(!group){
21394             return;
21395         }
21396         
21397         for(var i in group){
21398             var fg = group[i].el.findParent('.form-group', false, true);
21399             if (Roo.bootstrap.version == 3) {
21400                 fg.removeClass([this.invalidClass, this.validClass]);
21401                 fg.addClass(this.validClass);
21402             } else {
21403                 fg.removeClass(['is-valid', 'is-invalid']);
21404                 fg.addClass('is-valid');
21405             }
21406         }
21407     },
21408     
21409      /**
21410      * Mark this field as invalid
21411      * @param {String} msg The validation message
21412      */
21413     markInvalid : function(msg)
21414     {
21415         if(this.allowBlank){
21416             return;
21417         }
21418         
21419         var _this = this;
21420         
21421         this.fireEvent('invalid', this, msg);
21422         
21423         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21424         
21425         if(this.groupId){
21426             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21427         }
21428         
21429         if(label){
21430             label.markInvalid();
21431         }
21432             
21433         if(this.inputType == 'radio'){
21434             
21435             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21436                 var fg = e.findParent('.form-group', false, true);
21437                 if (Roo.bootstrap.version == 3) {
21438                     fg.removeClass([_this.invalidClass, _this.validClass]);
21439                     fg.addClass(_this.invalidClass);
21440                 } else {
21441                     fg.removeClass(['is-invalid', 'is-valid']);
21442                     fg.addClass('is-invalid');
21443                 }
21444             });
21445             
21446             return;
21447         }
21448         
21449         if(!this.groupId){
21450             var fg = this.el.findParent('.form-group', false, true);
21451             if (Roo.bootstrap.version == 3) {
21452                 fg.removeClass([_this.invalidClass, _this.validClass]);
21453                 fg.addClass(_this.invalidClass);
21454             } else {
21455                 fg.removeClass(['is-invalid', 'is-valid']);
21456                 fg.addClass('is-invalid');
21457             }
21458             return;
21459         }
21460         
21461         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21462         
21463         if(!group){
21464             return;
21465         }
21466         
21467         for(var i in group){
21468             var fg = group[i].el.findParent('.form-group', false, true);
21469             if (Roo.bootstrap.version == 3) {
21470                 fg.removeClass([_this.invalidClass, _this.validClass]);
21471                 fg.addClass(_this.invalidClass);
21472             } else {
21473                 fg.removeClass(['is-invalid', 'is-valid']);
21474                 fg.addClass('is-invalid');
21475             }
21476         }
21477         
21478     },
21479     
21480     clearInvalid : function()
21481     {
21482         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21483         
21484         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21485         
21486         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21487         
21488         if (label && label.iconEl) {
21489             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21490             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21491         }
21492     },
21493     
21494     disable : function()
21495     {
21496         if(this.inputType != 'radio'){
21497             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21498             return;
21499         }
21500         
21501         var _this = this;
21502         
21503         if(this.rendered){
21504             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21505                 _this.getActionEl().addClass(this.disabledClass);
21506                 e.dom.disabled = true;
21507             });
21508         }
21509         
21510         this.disabled = true;
21511         this.fireEvent("disable", this);
21512         return this;
21513     },
21514
21515     enable : function()
21516     {
21517         if(this.inputType != 'radio'){
21518             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21519             return;
21520         }
21521         
21522         var _this = this;
21523         
21524         if(this.rendered){
21525             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21526                 _this.getActionEl().removeClass(this.disabledClass);
21527                 e.dom.disabled = false;
21528             });
21529         }
21530         
21531         this.disabled = false;
21532         this.fireEvent("enable", this);
21533         return this;
21534     },
21535     
21536     setBoxLabel : function(v)
21537     {
21538         this.boxLabel = v;
21539         
21540         if(this.rendered){
21541             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21542         }
21543     }
21544
21545 });
21546
21547 Roo.apply(Roo.bootstrap.CheckBox, {
21548     
21549     groups: {},
21550     
21551      /**
21552     * register a CheckBox Group
21553     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21554     */
21555     register : function(checkbox)
21556     {
21557         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21558             this.groups[checkbox.groupId] = {};
21559         }
21560         
21561         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21562             return;
21563         }
21564         
21565         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21566         
21567     },
21568     /**
21569     * fetch a CheckBox Group based on the group ID
21570     * @param {string} the group ID
21571     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21572     */
21573     get: function(groupId) {
21574         if (typeof(this.groups[groupId]) == 'undefined') {
21575             return false;
21576         }
21577         
21578         return this.groups[groupId] ;
21579     }
21580     
21581     
21582 });
21583 /*
21584  * - LGPL
21585  *
21586  * RadioItem
21587  * 
21588  */
21589
21590 /**
21591  * @class Roo.bootstrap.Radio
21592  * @extends Roo.bootstrap.Component
21593  * Bootstrap Radio class
21594  * @cfg {String} boxLabel - the label associated
21595  * @cfg {String} value - the value of radio
21596  * 
21597  * @constructor
21598  * Create a new Radio
21599  * @param {Object} config The config object
21600  */
21601 Roo.bootstrap.Radio = function(config){
21602     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21603     
21604 };
21605
21606 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21607     
21608     boxLabel : '',
21609     
21610     value : '',
21611     
21612     getAutoCreate : function()
21613     {
21614         var cfg = {
21615             tag : 'div',
21616             cls : 'form-group radio',
21617             cn : [
21618                 {
21619                     tag : 'label',
21620                     cls : 'box-label',
21621                     html : this.boxLabel
21622                 }
21623             ]
21624         };
21625         
21626         return cfg;
21627     },
21628     
21629     initEvents : function() 
21630     {
21631         this.parent().register(this);
21632         
21633         this.el.on('click', this.onClick, this);
21634         
21635     },
21636     
21637     onClick : function(e)
21638     {
21639         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21640             this.setChecked(true);
21641         }
21642     },
21643     
21644     setChecked : function(state, suppressEvent)
21645     {
21646         this.parent().setValue(this.value, suppressEvent);
21647         
21648     },
21649     
21650     setBoxLabel : function(v)
21651     {
21652         this.boxLabel = v;
21653         
21654         if(this.rendered){
21655             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21656         }
21657     }
21658     
21659 });
21660  
21661
21662  /*
21663  * - LGPL
21664  *
21665  * Input
21666  * 
21667  */
21668
21669 /**
21670  * @class Roo.bootstrap.SecurePass
21671  * @extends Roo.bootstrap.Input
21672  * Bootstrap SecurePass class
21673  *
21674  * 
21675  * @constructor
21676  * Create a new SecurePass
21677  * @param {Object} config The config object
21678  */
21679  
21680 Roo.bootstrap.SecurePass = function (config) {
21681     // these go here, so the translation tool can replace them..
21682     this.errors = {
21683         PwdEmpty: "Please type a password, and then retype it to confirm.",
21684         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21685         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21686         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21687         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21688         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21689         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21690         TooWeak: "Your password is Too Weak."
21691     },
21692     this.meterLabel = "Password strength:";
21693     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21694     this.meterClass = [
21695         "roo-password-meter-tooweak", 
21696         "roo-password-meter-weak", 
21697         "roo-password-meter-medium", 
21698         "roo-password-meter-strong", 
21699         "roo-password-meter-grey"
21700     ];
21701     
21702     this.errors = {};
21703     
21704     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21705 }
21706
21707 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21708     /**
21709      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21710      * {
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      * })
21719      */
21720     // private
21721     
21722     meterWidth: 300,
21723     errorMsg :'',    
21724     errors: false,
21725     imageRoot: '/',
21726     /**
21727      * @cfg {String/Object} Label for the strength meter (defaults to
21728      * 'Password strength:')
21729      */
21730     // private
21731     meterLabel: '',
21732     /**
21733      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21734      * ['Weak', 'Medium', 'Strong'])
21735      */
21736     // private    
21737     pwdStrengths: false,    
21738     // private
21739     strength: 0,
21740     // private
21741     _lastPwd: null,
21742     // private
21743     kCapitalLetter: 0,
21744     kSmallLetter: 1,
21745     kDigit: 2,
21746     kPunctuation: 3,
21747     
21748     insecure: false,
21749     // private
21750     initEvents: function ()
21751     {
21752         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21753
21754         if (this.el.is('input[type=password]') && Roo.isSafari) {
21755             this.el.on('keydown', this.SafariOnKeyDown, this);
21756         }
21757
21758         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21759     },
21760     // private
21761     onRender: function (ct, position)
21762     {
21763         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21764         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21765         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21766
21767         this.trigger.createChild({
21768                    cn: [
21769                     {
21770                     //id: 'PwdMeter',
21771                     tag: 'div',
21772                     cls: 'roo-password-meter-grey col-xs-12',
21773                     style: {
21774                         //width: 0,
21775                         //width: this.meterWidth + 'px'                                                
21776                         }
21777                     },
21778                     {                            
21779                          cls: 'roo-password-meter-text'                          
21780                     }
21781                 ]            
21782         });
21783
21784          
21785         if (this.hideTrigger) {
21786             this.trigger.setDisplayed(false);
21787         }
21788         this.setSize(this.width || '', this.height || '');
21789     },
21790     // private
21791     onDestroy: function ()
21792     {
21793         if (this.trigger) {
21794             this.trigger.removeAllListeners();
21795             this.trigger.remove();
21796         }
21797         if (this.wrap) {
21798             this.wrap.remove();
21799         }
21800         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21801     },
21802     // private
21803     checkStrength: function ()
21804     {
21805         var pwd = this.inputEl().getValue();
21806         if (pwd == this._lastPwd) {
21807             return;
21808         }
21809
21810         var strength;
21811         if (this.ClientSideStrongPassword(pwd)) {
21812             strength = 3;
21813         } else if (this.ClientSideMediumPassword(pwd)) {
21814             strength = 2;
21815         } else if (this.ClientSideWeakPassword(pwd)) {
21816             strength = 1;
21817         } else {
21818             strength = 0;
21819         }
21820         
21821         Roo.log('strength1: ' + strength);
21822         
21823         //var pm = this.trigger.child('div/div/div').dom;
21824         var pm = this.trigger.child('div/div');
21825         pm.removeClass(this.meterClass);
21826         pm.addClass(this.meterClass[strength]);
21827                 
21828         
21829         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21830                 
21831         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21832         
21833         this._lastPwd = pwd;
21834     },
21835     reset: function ()
21836     {
21837         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21838         
21839         this._lastPwd = '';
21840         
21841         var pm = this.trigger.child('div/div');
21842         pm.removeClass(this.meterClass);
21843         pm.addClass('roo-password-meter-grey');        
21844         
21845         
21846         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21847         
21848         pt.innerHTML = '';
21849         this.inputEl().dom.type='password';
21850     },
21851     // private
21852     validateValue: function (value)
21853     {
21854         
21855         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21856             return false;
21857         }
21858         if (value.length == 0) {
21859             if (this.allowBlank) {
21860                 this.clearInvalid();
21861                 return true;
21862             }
21863
21864             this.markInvalid(this.errors.PwdEmpty);
21865             this.errorMsg = this.errors.PwdEmpty;
21866             return false;
21867         }
21868         
21869         if(this.insecure){
21870             return true;
21871         }
21872         
21873         if ('[\x21-\x7e]*'.match(value)) {
21874             this.markInvalid(this.errors.PwdBadChar);
21875             this.errorMsg = this.errors.PwdBadChar;
21876             return false;
21877         }
21878         if (value.length < 6) {
21879             this.markInvalid(this.errors.PwdShort);
21880             this.errorMsg = this.errors.PwdShort;
21881             return false;
21882         }
21883         if (value.length > 16) {
21884             this.markInvalid(this.errors.PwdLong);
21885             this.errorMsg = this.errors.PwdLong;
21886             return false;
21887         }
21888         var strength;
21889         if (this.ClientSideStrongPassword(value)) {
21890             strength = 3;
21891         } else if (this.ClientSideMediumPassword(value)) {
21892             strength = 2;
21893         } else if (this.ClientSideWeakPassword(value)) {
21894             strength = 1;
21895         } else {
21896             strength = 0;
21897         }
21898
21899         
21900         if (strength < 2) {
21901             //this.markInvalid(this.errors.TooWeak);
21902             this.errorMsg = this.errors.TooWeak;
21903             //return false;
21904         }
21905         
21906         
21907         console.log('strength2: ' + strength);
21908         
21909         //var pm = this.trigger.child('div/div/div').dom;
21910         
21911         var pm = this.trigger.child('div/div');
21912         pm.removeClass(this.meterClass);
21913         pm.addClass(this.meterClass[strength]);
21914                 
21915         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21916                 
21917         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21918         
21919         this.errorMsg = ''; 
21920         return true;
21921     },
21922     // private
21923     CharacterSetChecks: function (type)
21924     {
21925         this.type = type;
21926         this.fResult = false;
21927     },
21928     // private
21929     isctype: function (character, type)
21930     {
21931         switch (type) {  
21932             case this.kCapitalLetter:
21933                 if (character >= 'A' && character <= 'Z') {
21934                     return true;
21935                 }
21936                 break;
21937             
21938             case this.kSmallLetter:
21939                 if (character >= 'a' && character <= 'z') {
21940                     return true;
21941                 }
21942                 break;
21943             
21944             case this.kDigit:
21945                 if (character >= '0' && character <= '9') {
21946                     return true;
21947                 }
21948                 break;
21949             
21950             case this.kPunctuation:
21951                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21952                     return true;
21953                 }
21954                 break;
21955             
21956             default:
21957                 return false;
21958         }
21959
21960     },
21961     // private
21962     IsLongEnough: function (pwd, size)
21963     {
21964         return !(pwd == null || isNaN(size) || pwd.length < size);
21965     },
21966     // private
21967     SpansEnoughCharacterSets: function (word, nb)
21968     {
21969         if (!this.IsLongEnough(word, nb))
21970         {
21971             return false;
21972         }
21973
21974         var characterSetChecks = new Array(
21975             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21976             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21977         );
21978         
21979         for (var index = 0; index < word.length; ++index) {
21980             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21981                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21982                     characterSetChecks[nCharSet].fResult = true;
21983                     break;
21984                 }
21985             }
21986         }
21987
21988         var nCharSets = 0;
21989         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21990             if (characterSetChecks[nCharSet].fResult) {
21991                 ++nCharSets;
21992             }
21993         }
21994
21995         if (nCharSets < nb) {
21996             return false;
21997         }
21998         return true;
21999     },
22000     // private
22001     ClientSideStrongPassword: function (pwd)
22002     {
22003         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22004     },
22005     // private
22006     ClientSideMediumPassword: function (pwd)
22007     {
22008         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22009     },
22010     // private
22011     ClientSideWeakPassword: function (pwd)
22012     {
22013         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22014     }
22015           
22016 })//<script type="text/javascript">
22017
22018 /*
22019  * Based  Ext JS Library 1.1.1
22020  * Copyright(c) 2006-2007, Ext JS, LLC.
22021  * LGPL
22022  *
22023  */
22024  
22025 /**
22026  * @class Roo.HtmlEditorCore
22027  * @extends Roo.Component
22028  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22029  *
22030  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22031  */
22032
22033 Roo.HtmlEditorCore = function(config){
22034     
22035     
22036     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22037     
22038     
22039     this.addEvents({
22040         /**
22041          * @event initialize
22042          * Fires when the editor is fully initialized (including the iframe)
22043          * @param {Roo.HtmlEditorCore} this
22044          */
22045         initialize: true,
22046         /**
22047          * @event activate
22048          * Fires when the editor is first receives the focus. Any insertion must wait
22049          * until after this event.
22050          * @param {Roo.HtmlEditorCore} this
22051          */
22052         activate: true,
22053          /**
22054          * @event beforesync
22055          * Fires before the textarea is updated with content from the editor iframe. Return false
22056          * to cancel the sync.
22057          * @param {Roo.HtmlEditorCore} this
22058          * @param {String} html
22059          */
22060         beforesync: true,
22061          /**
22062          * @event beforepush
22063          * Fires before the iframe editor is updated with content from the textarea. Return false
22064          * to cancel the push.
22065          * @param {Roo.HtmlEditorCore} this
22066          * @param {String} html
22067          */
22068         beforepush: true,
22069          /**
22070          * @event sync
22071          * Fires when the textarea is updated with content from the editor iframe.
22072          * @param {Roo.HtmlEditorCore} this
22073          * @param {String} html
22074          */
22075         sync: true,
22076          /**
22077          * @event push
22078          * Fires when the iframe editor is updated with content from the textarea.
22079          * @param {Roo.HtmlEditorCore} this
22080          * @param {String} html
22081          */
22082         push: true,
22083         
22084         /**
22085          * @event editorevent
22086          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22087          * @param {Roo.HtmlEditorCore} this
22088          */
22089         editorevent: true
22090         
22091     });
22092     
22093     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22094     
22095     // defaults : white / black...
22096     this.applyBlacklists();
22097     
22098     
22099     
22100 };
22101
22102
22103 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22104
22105
22106      /**
22107      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22108      */
22109     
22110     owner : false,
22111     
22112      /**
22113      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22114      *                        Roo.resizable.
22115      */
22116     resizable : false,
22117      /**
22118      * @cfg {Number} height (in pixels)
22119      */   
22120     height: 300,
22121    /**
22122      * @cfg {Number} width (in pixels)
22123      */   
22124     width: 500,
22125     
22126     /**
22127      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22128      * 
22129      */
22130     stylesheets: false,
22131     
22132     // id of frame..
22133     frameId: false,
22134     
22135     // private properties
22136     validationEvent : false,
22137     deferHeight: true,
22138     initialized : false,
22139     activated : false,
22140     sourceEditMode : false,
22141     onFocus : Roo.emptyFn,
22142     iframePad:3,
22143     hideMode:'offsets',
22144     
22145     clearUp: true,
22146     
22147     // blacklist + whitelisted elements..
22148     black: false,
22149     white: false,
22150      
22151     bodyCls : '',
22152
22153     /**
22154      * Protected method that will not generally be called directly. It
22155      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22156      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22157      */
22158     getDocMarkup : function(){
22159         // body styles..
22160         var st = '';
22161         
22162         // inherit styels from page...?? 
22163         if (this.stylesheets === false) {
22164             
22165             Roo.get(document.head).select('style').each(function(node) {
22166                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22167             });
22168             
22169             Roo.get(document.head).select('link').each(function(node) { 
22170                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22171             });
22172             
22173         } else if (!this.stylesheets.length) {
22174                 // simple..
22175                 st = '<style type="text/css">' +
22176                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22177                    '</style>';
22178         } else { 
22179             st = '<style type="text/css">' +
22180                     this.stylesheets +
22181                 '</style>';
22182         }
22183         
22184         st +=  '<style type="text/css">' +
22185             'IMG { cursor: pointer } ' +
22186         '</style>';
22187
22188         var cls = 'roo-htmleditor-body';
22189         
22190         if(this.bodyCls.length){
22191             cls += ' ' + this.bodyCls;
22192         }
22193         
22194         return '<html><head>' + st  +
22195             //<style type="text/css">' +
22196             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22197             //'</style>' +
22198             ' </head><body class="' +  cls + '"></body></html>';
22199     },
22200
22201     // private
22202     onRender : function(ct, position)
22203     {
22204         var _t = this;
22205         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22206         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22207         
22208         
22209         this.el.dom.style.border = '0 none';
22210         this.el.dom.setAttribute('tabIndex', -1);
22211         this.el.addClass('x-hidden hide');
22212         
22213         
22214         
22215         if(Roo.isIE){ // fix IE 1px bogus margin
22216             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22217         }
22218        
22219         
22220         this.frameId = Roo.id();
22221         
22222          
22223         
22224         var iframe = this.owner.wrap.createChild({
22225             tag: 'iframe',
22226             cls: 'form-control', // bootstrap..
22227             id: this.frameId,
22228             name: this.frameId,
22229             frameBorder : 'no',
22230             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22231         }, this.el
22232         );
22233         
22234         
22235         this.iframe = iframe.dom;
22236
22237          this.assignDocWin();
22238         
22239         this.doc.designMode = 'on';
22240        
22241         this.doc.open();
22242         this.doc.write(this.getDocMarkup());
22243         this.doc.close();
22244
22245         
22246         var task = { // must defer to wait for browser to be ready
22247             run : function(){
22248                 //console.log("run task?" + this.doc.readyState);
22249                 this.assignDocWin();
22250                 if(this.doc.body || this.doc.readyState == 'complete'){
22251                     try {
22252                         this.doc.designMode="on";
22253                     } catch (e) {
22254                         return;
22255                     }
22256                     Roo.TaskMgr.stop(task);
22257                     this.initEditor.defer(10, this);
22258                 }
22259             },
22260             interval : 10,
22261             duration: 10000,
22262             scope: this
22263         };
22264         Roo.TaskMgr.start(task);
22265
22266     },
22267
22268     // private
22269     onResize : function(w, h)
22270     {
22271          Roo.log('resize: ' +w + ',' + h );
22272         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22273         if(!this.iframe){
22274             return;
22275         }
22276         if(typeof w == 'number'){
22277             
22278             this.iframe.style.width = w + 'px';
22279         }
22280         if(typeof h == 'number'){
22281             
22282             this.iframe.style.height = h + 'px';
22283             if(this.doc){
22284                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22285             }
22286         }
22287         
22288     },
22289
22290     /**
22291      * Toggles the editor between standard and source edit mode.
22292      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22293      */
22294     toggleSourceEdit : function(sourceEditMode){
22295         
22296         this.sourceEditMode = sourceEditMode === true;
22297         
22298         if(this.sourceEditMode){
22299  
22300             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22301             
22302         }else{
22303             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22304             //this.iframe.className = '';
22305             this.deferFocus();
22306         }
22307         //this.setSize(this.owner.wrap.getSize());
22308         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22309     },
22310
22311     
22312   
22313
22314     /**
22315      * Protected method that will not generally be called directly. If you need/want
22316      * custom HTML cleanup, this is the method you should override.
22317      * @param {String} html The HTML to be cleaned
22318      * return {String} The cleaned HTML
22319      */
22320     cleanHtml : function(html){
22321         html = String(html);
22322         if(html.length > 5){
22323             if(Roo.isSafari){ // strip safari nonsense
22324                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22325             }
22326         }
22327         if(html == '&nbsp;'){
22328             html = '';
22329         }
22330         return html;
22331     },
22332
22333     /**
22334      * HTML Editor -> Textarea
22335      * Protected method that will not generally be called directly. Syncs the contents
22336      * of the editor iframe with the textarea.
22337      */
22338     syncValue : function(){
22339         if(this.initialized){
22340             var bd = (this.doc.body || this.doc.documentElement);
22341             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22342             var html = bd.innerHTML;
22343             if(Roo.isSafari){
22344                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22345                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22346                 if(m && m[1]){
22347                     html = '<div style="'+m[0]+'">' + html + '</div>';
22348                 }
22349             }
22350             html = this.cleanHtml(html);
22351             // fix up the special chars.. normaly like back quotes in word...
22352             // however we do not want to do this with chinese..
22353             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22354                 var cc = b.charCodeAt();
22355                 if (
22356                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22357                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22358                     (cc >= 0xf900 && cc < 0xfb00 )
22359                 ) {
22360                         return b;
22361                 }
22362                 return "&#"+cc+";" 
22363             });
22364             if(this.owner.fireEvent('beforesync', this, html) !== false){
22365                 this.el.dom.value = html;
22366                 this.owner.fireEvent('sync', this, html);
22367             }
22368         }
22369     },
22370
22371     /**
22372      * Protected method that will not generally be called directly. Pushes the value of the textarea
22373      * into the iframe editor.
22374      */
22375     pushValue : function(){
22376         if(this.initialized){
22377             var v = this.el.dom.value.trim();
22378             
22379 //            if(v.length < 1){
22380 //                v = '&#160;';
22381 //            }
22382             
22383             if(this.owner.fireEvent('beforepush', this, v) !== false){
22384                 var d = (this.doc.body || this.doc.documentElement);
22385                 d.innerHTML = v;
22386                 this.cleanUpPaste();
22387                 this.el.dom.value = d.innerHTML;
22388                 this.owner.fireEvent('push', this, v);
22389             }
22390         }
22391     },
22392
22393     // private
22394     deferFocus : function(){
22395         this.focus.defer(10, this);
22396     },
22397
22398     // doc'ed in Field
22399     focus : function(){
22400         if(this.win && !this.sourceEditMode){
22401             this.win.focus();
22402         }else{
22403             this.el.focus();
22404         }
22405     },
22406     
22407     assignDocWin: function()
22408     {
22409         var iframe = this.iframe;
22410         
22411          if(Roo.isIE){
22412             this.doc = iframe.contentWindow.document;
22413             this.win = iframe.contentWindow;
22414         } else {
22415 //            if (!Roo.get(this.frameId)) {
22416 //                return;
22417 //            }
22418 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22419 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22420             
22421             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22422                 return;
22423             }
22424             
22425             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22426             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22427         }
22428     },
22429     
22430     // private
22431     initEditor : function(){
22432         //console.log("INIT EDITOR");
22433         this.assignDocWin();
22434         
22435         
22436         
22437         this.doc.designMode="on";
22438         this.doc.open();
22439         this.doc.write(this.getDocMarkup());
22440         this.doc.close();
22441         
22442         var dbody = (this.doc.body || this.doc.documentElement);
22443         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22444         // this copies styles from the containing element into thsi one..
22445         // not sure why we need all of this..
22446         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22447         
22448         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22449         //ss['background-attachment'] = 'fixed'; // w3c
22450         dbody.bgProperties = 'fixed'; // ie
22451         //Roo.DomHelper.applyStyles(dbody, ss);
22452         Roo.EventManager.on(this.doc, {
22453             //'mousedown': this.onEditorEvent,
22454             'mouseup': this.onEditorEvent,
22455             'dblclick': this.onEditorEvent,
22456             'click': this.onEditorEvent,
22457             'keyup': this.onEditorEvent,
22458             buffer:100,
22459             scope: this
22460         });
22461         if(Roo.isGecko){
22462             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22463         }
22464         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22465             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22466         }
22467         this.initialized = true;
22468
22469         this.owner.fireEvent('initialize', this);
22470         this.pushValue();
22471     },
22472
22473     // private
22474     onDestroy : function(){
22475         
22476         
22477         
22478         if(this.rendered){
22479             
22480             //for (var i =0; i < this.toolbars.length;i++) {
22481             //    // fixme - ask toolbars for heights?
22482             //    this.toolbars[i].onDestroy();
22483            // }
22484             
22485             //this.wrap.dom.innerHTML = '';
22486             //this.wrap.remove();
22487         }
22488     },
22489
22490     // private
22491     onFirstFocus : function(){
22492         
22493         this.assignDocWin();
22494         
22495         
22496         this.activated = true;
22497          
22498     
22499         if(Roo.isGecko){ // prevent silly gecko errors
22500             this.win.focus();
22501             var s = this.win.getSelection();
22502             if(!s.focusNode || s.focusNode.nodeType != 3){
22503                 var r = s.getRangeAt(0);
22504                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22505                 r.collapse(true);
22506                 this.deferFocus();
22507             }
22508             try{
22509                 this.execCmd('useCSS', true);
22510                 this.execCmd('styleWithCSS', false);
22511             }catch(e){}
22512         }
22513         this.owner.fireEvent('activate', this);
22514     },
22515
22516     // private
22517     adjustFont: function(btn){
22518         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22519         //if(Roo.isSafari){ // safari
22520         //    adjust *= 2;
22521        // }
22522         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22523         if(Roo.isSafari){ // safari
22524             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22525             v =  (v < 10) ? 10 : v;
22526             v =  (v > 48) ? 48 : v;
22527             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22528             
22529         }
22530         
22531         
22532         v = Math.max(1, v+adjust);
22533         
22534         this.execCmd('FontSize', v  );
22535     },
22536
22537     onEditorEvent : function(e)
22538     {
22539         this.owner.fireEvent('editorevent', this, e);
22540       //  this.updateToolbar();
22541         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22542     },
22543
22544     insertTag : function(tg)
22545     {
22546         // could be a bit smarter... -> wrap the current selected tRoo..
22547         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22548             
22549             range = this.createRange(this.getSelection());
22550             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22551             wrappingNode.appendChild(range.extractContents());
22552             range.insertNode(wrappingNode);
22553
22554             return;
22555             
22556             
22557             
22558         }
22559         this.execCmd("formatblock",   tg);
22560         
22561     },
22562     
22563     insertText : function(txt)
22564     {
22565         
22566         
22567         var range = this.createRange();
22568         range.deleteContents();
22569                //alert(Sender.getAttribute('label'));
22570                
22571         range.insertNode(this.doc.createTextNode(txt));
22572     } ,
22573     
22574      
22575
22576     /**
22577      * Executes a Midas editor command on the editor document and performs necessary focus and
22578      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22579      * @param {String} cmd The Midas command
22580      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22581      */
22582     relayCmd : function(cmd, value){
22583         this.win.focus();
22584         this.execCmd(cmd, value);
22585         this.owner.fireEvent('editorevent', this);
22586         //this.updateToolbar();
22587         this.owner.deferFocus();
22588     },
22589
22590     /**
22591      * Executes a Midas editor command directly on the editor document.
22592      * For visual commands, you should use {@link #relayCmd} instead.
22593      * <b>This should only be called after the editor is initialized.</b>
22594      * @param {String} cmd The Midas command
22595      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22596      */
22597     execCmd : function(cmd, value){
22598         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22599         this.syncValue();
22600     },
22601  
22602  
22603    
22604     /**
22605      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22606      * to insert tRoo.
22607      * @param {String} text | dom node.. 
22608      */
22609     insertAtCursor : function(text)
22610     {
22611         
22612         if(!this.activated){
22613             return;
22614         }
22615         /*
22616         if(Roo.isIE){
22617             this.win.focus();
22618             var r = this.doc.selection.createRange();
22619             if(r){
22620                 r.collapse(true);
22621                 r.pasteHTML(text);
22622                 this.syncValue();
22623                 this.deferFocus();
22624             
22625             }
22626             return;
22627         }
22628         */
22629         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22630             this.win.focus();
22631             
22632             
22633             // from jquery ui (MIT licenced)
22634             var range, node;
22635             var win = this.win;
22636             
22637             if (win.getSelection && win.getSelection().getRangeAt) {
22638                 range = win.getSelection().getRangeAt(0);
22639                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22640                 range.insertNode(node);
22641             } else if (win.document.selection && win.document.selection.createRange) {
22642                 // no firefox support
22643                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22644                 win.document.selection.createRange().pasteHTML(txt);
22645             } else {
22646                 // no firefox support
22647                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22648                 this.execCmd('InsertHTML', txt);
22649             } 
22650             
22651             this.syncValue();
22652             
22653             this.deferFocus();
22654         }
22655     },
22656  // private
22657     mozKeyPress : function(e){
22658         if(e.ctrlKey){
22659             var c = e.getCharCode(), cmd;
22660           
22661             if(c > 0){
22662                 c = String.fromCharCode(c).toLowerCase();
22663                 switch(c){
22664                     case 'b':
22665                         cmd = 'bold';
22666                         break;
22667                     case 'i':
22668                         cmd = 'italic';
22669                         break;
22670                     
22671                     case 'u':
22672                         cmd = 'underline';
22673                         break;
22674                     
22675                     case 'v':
22676                         this.cleanUpPaste.defer(100, this);
22677                         return;
22678                         
22679                 }
22680                 if(cmd){
22681                     this.win.focus();
22682                     this.execCmd(cmd);
22683                     this.deferFocus();
22684                     e.preventDefault();
22685                 }
22686                 
22687             }
22688         }
22689     },
22690
22691     // private
22692     fixKeys : function(){ // load time branching for fastest keydown performance
22693         if(Roo.isIE){
22694             return function(e){
22695                 var k = e.getKey(), r;
22696                 if(k == e.TAB){
22697                     e.stopEvent();
22698                     r = this.doc.selection.createRange();
22699                     if(r){
22700                         r.collapse(true);
22701                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22702                         this.deferFocus();
22703                     }
22704                     return;
22705                 }
22706                 
22707                 if(k == e.ENTER){
22708                     r = this.doc.selection.createRange();
22709                     if(r){
22710                         var target = r.parentElement();
22711                         if(!target || target.tagName.toLowerCase() != 'li'){
22712                             e.stopEvent();
22713                             r.pasteHTML('<br />');
22714                             r.collapse(false);
22715                             r.select();
22716                         }
22717                     }
22718                 }
22719                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22720                     this.cleanUpPaste.defer(100, this);
22721                     return;
22722                 }
22723                 
22724                 
22725             };
22726         }else if(Roo.isOpera){
22727             return function(e){
22728                 var k = e.getKey();
22729                 if(k == e.TAB){
22730                     e.stopEvent();
22731                     this.win.focus();
22732                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22733                     this.deferFocus();
22734                 }
22735                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22736                     this.cleanUpPaste.defer(100, this);
22737                     return;
22738                 }
22739                 
22740             };
22741         }else if(Roo.isSafari){
22742             return function(e){
22743                 var k = e.getKey();
22744                 
22745                 if(k == e.TAB){
22746                     e.stopEvent();
22747                     this.execCmd('InsertText','\t');
22748                     this.deferFocus();
22749                     return;
22750                 }
22751                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22752                     this.cleanUpPaste.defer(100, this);
22753                     return;
22754                 }
22755                 
22756              };
22757         }
22758     }(),
22759     
22760     getAllAncestors: function()
22761     {
22762         var p = this.getSelectedNode();
22763         var a = [];
22764         if (!p) {
22765             a.push(p); // push blank onto stack..
22766             p = this.getParentElement();
22767         }
22768         
22769         
22770         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22771             a.push(p);
22772             p = p.parentNode;
22773         }
22774         a.push(this.doc.body);
22775         return a;
22776     },
22777     lastSel : false,
22778     lastSelNode : false,
22779     
22780     
22781     getSelection : function() 
22782     {
22783         this.assignDocWin();
22784         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22785     },
22786     
22787     getSelectedNode: function() 
22788     {
22789         // this may only work on Gecko!!!
22790         
22791         // should we cache this!!!!
22792         
22793         
22794         
22795          
22796         var range = this.createRange(this.getSelection()).cloneRange();
22797         
22798         if (Roo.isIE) {
22799             var parent = range.parentElement();
22800             while (true) {
22801                 var testRange = range.duplicate();
22802                 testRange.moveToElementText(parent);
22803                 if (testRange.inRange(range)) {
22804                     break;
22805                 }
22806                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22807                     break;
22808                 }
22809                 parent = parent.parentElement;
22810             }
22811             return parent;
22812         }
22813         
22814         // is ancestor a text element.
22815         var ac =  range.commonAncestorContainer;
22816         if (ac.nodeType == 3) {
22817             ac = ac.parentNode;
22818         }
22819         
22820         var ar = ac.childNodes;
22821          
22822         var nodes = [];
22823         var other_nodes = [];
22824         var has_other_nodes = false;
22825         for (var i=0;i<ar.length;i++) {
22826             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22827                 continue;
22828             }
22829             // fullly contained node.
22830             
22831             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22832                 nodes.push(ar[i]);
22833                 continue;
22834             }
22835             
22836             // probably selected..
22837             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22838                 other_nodes.push(ar[i]);
22839                 continue;
22840             }
22841             // outer..
22842             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22843                 continue;
22844             }
22845             
22846             
22847             has_other_nodes = true;
22848         }
22849         if (!nodes.length && other_nodes.length) {
22850             nodes= other_nodes;
22851         }
22852         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22853             return false;
22854         }
22855         
22856         return nodes[0];
22857     },
22858     createRange: function(sel)
22859     {
22860         // this has strange effects when using with 
22861         // top toolbar - not sure if it's a great idea.
22862         //this.editor.contentWindow.focus();
22863         if (typeof sel != "undefined") {
22864             try {
22865                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22866             } catch(e) {
22867                 return this.doc.createRange();
22868             }
22869         } else {
22870             return this.doc.createRange();
22871         }
22872     },
22873     getParentElement: function()
22874     {
22875         
22876         this.assignDocWin();
22877         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22878         
22879         var range = this.createRange(sel);
22880          
22881         try {
22882             var p = range.commonAncestorContainer;
22883             while (p.nodeType == 3) { // text node
22884                 p = p.parentNode;
22885             }
22886             return p;
22887         } catch (e) {
22888             return null;
22889         }
22890     
22891     },
22892     /***
22893      *
22894      * Range intersection.. the hard stuff...
22895      *  '-1' = before
22896      *  '0' = hits..
22897      *  '1' = after.
22898      *         [ -- selected range --- ]
22899      *   [fail]                        [fail]
22900      *
22901      *    basically..
22902      *      if end is before start or  hits it. fail.
22903      *      if start is after end or hits it fail.
22904      *
22905      *   if either hits (but other is outside. - then it's not 
22906      *   
22907      *    
22908      **/
22909     
22910     
22911     // @see http://www.thismuchiknow.co.uk/?p=64.
22912     rangeIntersectsNode : function(range, node)
22913     {
22914         var nodeRange = node.ownerDocument.createRange();
22915         try {
22916             nodeRange.selectNode(node);
22917         } catch (e) {
22918             nodeRange.selectNodeContents(node);
22919         }
22920     
22921         var rangeStartRange = range.cloneRange();
22922         rangeStartRange.collapse(true);
22923     
22924         var rangeEndRange = range.cloneRange();
22925         rangeEndRange.collapse(false);
22926     
22927         var nodeStartRange = nodeRange.cloneRange();
22928         nodeStartRange.collapse(true);
22929     
22930         var nodeEndRange = nodeRange.cloneRange();
22931         nodeEndRange.collapse(false);
22932     
22933         return rangeStartRange.compareBoundaryPoints(
22934                  Range.START_TO_START, nodeEndRange) == -1 &&
22935                rangeEndRange.compareBoundaryPoints(
22936                  Range.START_TO_START, nodeStartRange) == 1;
22937         
22938          
22939     },
22940     rangeCompareNode : function(range, node)
22941     {
22942         var nodeRange = node.ownerDocument.createRange();
22943         try {
22944             nodeRange.selectNode(node);
22945         } catch (e) {
22946             nodeRange.selectNodeContents(node);
22947         }
22948         
22949         
22950         range.collapse(true);
22951     
22952         nodeRange.collapse(true);
22953      
22954         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22955         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22956          
22957         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22958         
22959         var nodeIsBefore   =  ss == 1;
22960         var nodeIsAfter    = ee == -1;
22961         
22962         if (nodeIsBefore && nodeIsAfter) {
22963             return 0; // outer
22964         }
22965         if (!nodeIsBefore && nodeIsAfter) {
22966             return 1; //right trailed.
22967         }
22968         
22969         if (nodeIsBefore && !nodeIsAfter) {
22970             return 2;  // left trailed.
22971         }
22972         // fully contined.
22973         return 3;
22974     },
22975
22976     // private? - in a new class?
22977     cleanUpPaste :  function()
22978     {
22979         // cleans up the whole document..
22980         Roo.log('cleanuppaste');
22981         
22982         this.cleanUpChildren(this.doc.body);
22983         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22984         if (clean != this.doc.body.innerHTML) {
22985             this.doc.body.innerHTML = clean;
22986         }
22987         
22988     },
22989     
22990     cleanWordChars : function(input) {// change the chars to hex code
22991         var he = Roo.HtmlEditorCore;
22992         
22993         var output = input;
22994         Roo.each(he.swapCodes, function(sw) { 
22995             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22996             
22997             output = output.replace(swapper, sw[1]);
22998         });
22999         
23000         return output;
23001     },
23002     
23003     
23004     cleanUpChildren : function (n)
23005     {
23006         if (!n.childNodes.length) {
23007             return;
23008         }
23009         for (var i = n.childNodes.length-1; i > -1 ; i--) {
23010            this.cleanUpChild(n.childNodes[i]);
23011         }
23012     },
23013     
23014     
23015         
23016     
23017     cleanUpChild : function (node)
23018     {
23019         var ed = this;
23020         //console.log(node);
23021         if (node.nodeName == "#text") {
23022             // clean up silly Windows -- stuff?
23023             return; 
23024         }
23025         if (node.nodeName == "#comment") {
23026             node.parentNode.removeChild(node);
23027             // clean up silly Windows -- stuff?
23028             return; 
23029         }
23030         var lcname = node.tagName.toLowerCase();
23031         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23032         // whitelist of tags..
23033         
23034         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23035             // remove node.
23036             node.parentNode.removeChild(node);
23037             return;
23038             
23039         }
23040         
23041         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23042         
23043         // remove <a name=....> as rendering on yahoo mailer is borked with this.
23044         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23045         
23046         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23047         //    remove_keep_children = true;
23048         //}
23049         
23050         if (remove_keep_children) {
23051             this.cleanUpChildren(node);
23052             // inserts everything just before this node...
23053             while (node.childNodes.length) {
23054                 var cn = node.childNodes[0];
23055                 node.removeChild(cn);
23056                 node.parentNode.insertBefore(cn, node);
23057             }
23058             node.parentNode.removeChild(node);
23059             return;
23060         }
23061         
23062         if (!node.attributes || !node.attributes.length) {
23063             this.cleanUpChildren(node);
23064             return;
23065         }
23066         
23067         function cleanAttr(n,v)
23068         {
23069             
23070             if (v.match(/^\./) || v.match(/^\//)) {
23071                 return;
23072             }
23073             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23074                 return;
23075             }
23076             if (v.match(/^#/)) {
23077                 return;
23078             }
23079 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23080             node.removeAttribute(n);
23081             
23082         }
23083         
23084         var cwhite = this.cwhite;
23085         var cblack = this.cblack;
23086             
23087         function cleanStyle(n,v)
23088         {
23089             if (v.match(/expression/)) { //XSS?? should we even bother..
23090                 node.removeAttribute(n);
23091                 return;
23092             }
23093             
23094             var parts = v.split(/;/);
23095             var clean = [];
23096             
23097             Roo.each(parts, function(p) {
23098                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23099                 if (!p.length) {
23100                     return true;
23101                 }
23102                 var l = p.split(':').shift().replace(/\s+/g,'');
23103                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23104                 
23105                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23106 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23107                     //node.removeAttribute(n);
23108                     return true;
23109                 }
23110                 //Roo.log()
23111                 // only allow 'c whitelisted system attributes'
23112                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23113 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23114                     //node.removeAttribute(n);
23115                     return true;
23116                 }
23117                 
23118                 
23119                  
23120                 
23121                 clean.push(p);
23122                 return true;
23123             });
23124             if (clean.length) { 
23125                 node.setAttribute(n, clean.join(';'));
23126             } else {
23127                 node.removeAttribute(n);
23128             }
23129             
23130         }
23131         
23132         
23133         for (var i = node.attributes.length-1; i > -1 ; i--) {
23134             var a = node.attributes[i];
23135             //console.log(a);
23136             
23137             if (a.name.toLowerCase().substr(0,2)=='on')  {
23138                 node.removeAttribute(a.name);
23139                 continue;
23140             }
23141             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23142                 node.removeAttribute(a.name);
23143                 continue;
23144             }
23145             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23146                 cleanAttr(a.name,a.value); // fixme..
23147                 continue;
23148             }
23149             if (a.name == 'style') {
23150                 cleanStyle(a.name,a.value);
23151                 continue;
23152             }
23153             /// clean up MS crap..
23154             // tecnically this should be a list of valid class'es..
23155             
23156             
23157             if (a.name == 'class') {
23158                 if (a.value.match(/^Mso/)) {
23159                     node.className = '';
23160                 }
23161                 
23162                 if (a.value.match(/^body$/)) {
23163                     node.className = '';
23164                 }
23165                 continue;
23166             }
23167             
23168             // style cleanup!?
23169             // class cleanup?
23170             
23171         }
23172         
23173         
23174         this.cleanUpChildren(node);
23175         
23176         
23177     },
23178     
23179     /**
23180      * Clean up MS wordisms...
23181      */
23182     cleanWord : function(node)
23183     {
23184         
23185         
23186         if (!node) {
23187             this.cleanWord(this.doc.body);
23188             return;
23189         }
23190         if (node.nodeName == "#text") {
23191             // clean up silly Windows -- stuff?
23192             return; 
23193         }
23194         if (node.nodeName == "#comment") {
23195             node.parentNode.removeChild(node);
23196             // clean up silly Windows -- stuff?
23197             return; 
23198         }
23199         
23200         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23201             node.parentNode.removeChild(node);
23202             return;
23203         }
23204         
23205         // remove - but keep children..
23206         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23207             while (node.childNodes.length) {
23208                 var cn = node.childNodes[0];
23209                 node.removeChild(cn);
23210                 node.parentNode.insertBefore(cn, node);
23211             }
23212             node.parentNode.removeChild(node);
23213             this.iterateChildren(node, this.cleanWord);
23214             return;
23215         }
23216         // clean styles
23217         if (node.className.length) {
23218             
23219             var cn = node.className.split(/\W+/);
23220             var cna = [];
23221             Roo.each(cn, function(cls) {
23222                 if (cls.match(/Mso[a-zA-Z]+/)) {
23223                     return;
23224                 }
23225                 cna.push(cls);
23226             });
23227             node.className = cna.length ? cna.join(' ') : '';
23228             if (!cna.length) {
23229                 node.removeAttribute("class");
23230             }
23231         }
23232         
23233         if (node.hasAttribute("lang")) {
23234             node.removeAttribute("lang");
23235         }
23236         
23237         if (node.hasAttribute("style")) {
23238             
23239             var styles = node.getAttribute("style").split(";");
23240             var nstyle = [];
23241             Roo.each(styles, function(s) {
23242                 if (!s.match(/:/)) {
23243                     return;
23244                 }
23245                 var kv = s.split(":");
23246                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23247                     return;
23248                 }
23249                 // what ever is left... we allow.
23250                 nstyle.push(s);
23251             });
23252             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23253             if (!nstyle.length) {
23254                 node.removeAttribute('style');
23255             }
23256         }
23257         this.iterateChildren(node, this.cleanWord);
23258         
23259         
23260         
23261     },
23262     /**
23263      * iterateChildren of a Node, calling fn each time, using this as the scole..
23264      * @param {DomNode} node node to iterate children of.
23265      * @param {Function} fn method of this class to call on each item.
23266      */
23267     iterateChildren : function(node, fn)
23268     {
23269         if (!node.childNodes.length) {
23270                 return;
23271         }
23272         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23273            fn.call(this, node.childNodes[i])
23274         }
23275     },
23276     
23277     
23278     /**
23279      * cleanTableWidths.
23280      *
23281      * Quite often pasting from word etc.. results in tables with column and widths.
23282      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23283      *
23284      */
23285     cleanTableWidths : function(node)
23286     {
23287          
23288          
23289         if (!node) {
23290             this.cleanTableWidths(this.doc.body);
23291             return;
23292         }
23293         
23294         // ignore list...
23295         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23296             return; 
23297         }
23298         Roo.log(node.tagName);
23299         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23300             this.iterateChildren(node, this.cleanTableWidths);
23301             return;
23302         }
23303         if (node.hasAttribute('width')) {
23304             node.removeAttribute('width');
23305         }
23306         
23307          
23308         if (node.hasAttribute("style")) {
23309             // pretty basic...
23310             
23311             var styles = node.getAttribute("style").split(";");
23312             var nstyle = [];
23313             Roo.each(styles, function(s) {
23314                 if (!s.match(/:/)) {
23315                     return;
23316                 }
23317                 var kv = s.split(":");
23318                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23319                     return;
23320                 }
23321                 // what ever is left... we allow.
23322                 nstyle.push(s);
23323             });
23324             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23325             if (!nstyle.length) {
23326                 node.removeAttribute('style');
23327             }
23328         }
23329         
23330         this.iterateChildren(node, this.cleanTableWidths);
23331         
23332         
23333     },
23334     
23335     
23336     
23337     
23338     domToHTML : function(currentElement, depth, nopadtext) {
23339         
23340         depth = depth || 0;
23341         nopadtext = nopadtext || false;
23342     
23343         if (!currentElement) {
23344             return this.domToHTML(this.doc.body);
23345         }
23346         
23347         //Roo.log(currentElement);
23348         var j;
23349         var allText = false;
23350         var nodeName = currentElement.nodeName;
23351         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23352         
23353         if  (nodeName == '#text') {
23354             
23355             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23356         }
23357         
23358         
23359         var ret = '';
23360         if (nodeName != 'BODY') {
23361              
23362             var i = 0;
23363             // Prints the node tagName, such as <A>, <IMG>, etc
23364             if (tagName) {
23365                 var attr = [];
23366                 for(i = 0; i < currentElement.attributes.length;i++) {
23367                     // quoting?
23368                     var aname = currentElement.attributes.item(i).name;
23369                     if (!currentElement.attributes.item(i).value.length) {
23370                         continue;
23371                     }
23372                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23373                 }
23374                 
23375                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23376             } 
23377             else {
23378                 
23379                 // eack
23380             }
23381         } else {
23382             tagName = false;
23383         }
23384         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23385             return ret;
23386         }
23387         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23388             nopadtext = true;
23389         }
23390         
23391         
23392         // Traverse the tree
23393         i = 0;
23394         var currentElementChild = currentElement.childNodes.item(i);
23395         var allText = true;
23396         var innerHTML  = '';
23397         lastnode = '';
23398         while (currentElementChild) {
23399             // Formatting code (indent the tree so it looks nice on the screen)
23400             var nopad = nopadtext;
23401             if (lastnode == 'SPAN') {
23402                 nopad  = true;
23403             }
23404             // text
23405             if  (currentElementChild.nodeName == '#text') {
23406                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23407                 toadd = nopadtext ? toadd : toadd.trim();
23408                 if (!nopad && toadd.length > 80) {
23409                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23410                 }
23411                 innerHTML  += toadd;
23412                 
23413                 i++;
23414                 currentElementChild = currentElement.childNodes.item(i);
23415                 lastNode = '';
23416                 continue;
23417             }
23418             allText = false;
23419             
23420             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23421                 
23422             // Recursively traverse the tree structure of the child node
23423             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23424             lastnode = currentElementChild.nodeName;
23425             i++;
23426             currentElementChild=currentElement.childNodes.item(i);
23427         }
23428         
23429         ret += innerHTML;
23430         
23431         if (!allText) {
23432                 // The remaining code is mostly for formatting the tree
23433             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23434         }
23435         
23436         
23437         if (tagName) {
23438             ret+= "</"+tagName+">";
23439         }
23440         return ret;
23441         
23442     },
23443         
23444     applyBlacklists : function()
23445     {
23446         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23447         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23448         
23449         this.white = [];
23450         this.black = [];
23451         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23452             if (b.indexOf(tag) > -1) {
23453                 return;
23454             }
23455             this.white.push(tag);
23456             
23457         }, this);
23458         
23459         Roo.each(w, function(tag) {
23460             if (b.indexOf(tag) > -1) {
23461                 return;
23462             }
23463             if (this.white.indexOf(tag) > -1) {
23464                 return;
23465             }
23466             this.white.push(tag);
23467             
23468         }, this);
23469         
23470         
23471         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23472             if (w.indexOf(tag) > -1) {
23473                 return;
23474             }
23475             this.black.push(tag);
23476             
23477         }, this);
23478         
23479         Roo.each(b, function(tag) {
23480             if (w.indexOf(tag) > -1) {
23481                 return;
23482             }
23483             if (this.black.indexOf(tag) > -1) {
23484                 return;
23485             }
23486             this.black.push(tag);
23487             
23488         }, this);
23489         
23490         
23491         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23492         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23493         
23494         this.cwhite = [];
23495         this.cblack = [];
23496         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23497             if (b.indexOf(tag) > -1) {
23498                 return;
23499             }
23500             this.cwhite.push(tag);
23501             
23502         }, this);
23503         
23504         Roo.each(w, function(tag) {
23505             if (b.indexOf(tag) > -1) {
23506                 return;
23507             }
23508             if (this.cwhite.indexOf(tag) > -1) {
23509                 return;
23510             }
23511             this.cwhite.push(tag);
23512             
23513         }, this);
23514         
23515         
23516         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23517             if (w.indexOf(tag) > -1) {
23518                 return;
23519             }
23520             this.cblack.push(tag);
23521             
23522         }, this);
23523         
23524         Roo.each(b, function(tag) {
23525             if (w.indexOf(tag) > -1) {
23526                 return;
23527             }
23528             if (this.cblack.indexOf(tag) > -1) {
23529                 return;
23530             }
23531             this.cblack.push(tag);
23532             
23533         }, this);
23534     },
23535     
23536     setStylesheets : function(stylesheets)
23537     {
23538         if(typeof(stylesheets) == 'string'){
23539             Roo.get(this.iframe.contentDocument.head).createChild({
23540                 tag : 'link',
23541                 rel : 'stylesheet',
23542                 type : 'text/css',
23543                 href : stylesheets
23544             });
23545             
23546             return;
23547         }
23548         var _this = this;
23549      
23550         Roo.each(stylesheets, function(s) {
23551             if(!s.length){
23552                 return;
23553             }
23554             
23555             Roo.get(_this.iframe.contentDocument.head).createChild({
23556                 tag : 'link',
23557                 rel : 'stylesheet',
23558                 type : 'text/css',
23559                 href : s
23560             });
23561         });
23562
23563         
23564     },
23565     
23566     removeStylesheets : function()
23567     {
23568         var _this = this;
23569         
23570         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23571             s.remove();
23572         });
23573     },
23574     
23575     setStyle : function(style)
23576     {
23577         Roo.get(this.iframe.contentDocument.head).createChild({
23578             tag : 'style',
23579             type : 'text/css',
23580             html : style
23581         });
23582
23583         return;
23584     }
23585     
23586     // hide stuff that is not compatible
23587     /**
23588      * @event blur
23589      * @hide
23590      */
23591     /**
23592      * @event change
23593      * @hide
23594      */
23595     /**
23596      * @event focus
23597      * @hide
23598      */
23599     /**
23600      * @event specialkey
23601      * @hide
23602      */
23603     /**
23604      * @cfg {String} fieldClass @hide
23605      */
23606     /**
23607      * @cfg {String} focusClass @hide
23608      */
23609     /**
23610      * @cfg {String} autoCreate @hide
23611      */
23612     /**
23613      * @cfg {String} inputType @hide
23614      */
23615     /**
23616      * @cfg {String} invalidClass @hide
23617      */
23618     /**
23619      * @cfg {String} invalidText @hide
23620      */
23621     /**
23622      * @cfg {String} msgFx @hide
23623      */
23624     /**
23625      * @cfg {String} validateOnBlur @hide
23626      */
23627 });
23628
23629 Roo.HtmlEditorCore.white = [
23630         'area', 'br', 'img', 'input', 'hr', 'wbr',
23631         
23632        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23633        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23634        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23635        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23636        'table',   'ul',         'xmp', 
23637        
23638        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23639       'thead',   'tr', 
23640      
23641       'dir', 'menu', 'ol', 'ul', 'dl',
23642        
23643       'embed',  'object'
23644 ];
23645
23646
23647 Roo.HtmlEditorCore.black = [
23648     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23649         'applet', // 
23650         'base',   'basefont', 'bgsound', 'blink',  'body', 
23651         'frame',  'frameset', 'head',    'html',   'ilayer', 
23652         'iframe', 'layer',  'link',     'meta',    'object',   
23653         'script', 'style' ,'title',  'xml' // clean later..
23654 ];
23655 Roo.HtmlEditorCore.clean = [
23656     'script', 'style', 'title', 'xml'
23657 ];
23658 Roo.HtmlEditorCore.remove = [
23659     'font'
23660 ];
23661 // attributes..
23662
23663 Roo.HtmlEditorCore.ablack = [
23664     'on'
23665 ];
23666     
23667 Roo.HtmlEditorCore.aclean = [ 
23668     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23669 ];
23670
23671 // protocols..
23672 Roo.HtmlEditorCore.pwhite= [
23673         'http',  'https',  'mailto'
23674 ];
23675
23676 // white listed style attributes.
23677 Roo.HtmlEditorCore.cwhite= [
23678       //  'text-align', /// default is to allow most things..
23679       
23680          
23681 //        'font-size'//??
23682 ];
23683
23684 // black listed style attributes.
23685 Roo.HtmlEditorCore.cblack= [
23686       //  'font-size' -- this can be set by the project 
23687 ];
23688
23689
23690 Roo.HtmlEditorCore.swapCodes   =[ 
23691     [    8211, "--" ], 
23692     [    8212, "--" ], 
23693     [    8216,  "'" ],  
23694     [    8217, "'" ],  
23695     [    8220, '"' ],  
23696     [    8221, '"' ],  
23697     [    8226, "*" ],  
23698     [    8230, "..." ]
23699 ]; 
23700
23701     /*
23702  * - LGPL
23703  *
23704  * HtmlEditor
23705  * 
23706  */
23707
23708 /**
23709  * @class Roo.bootstrap.HtmlEditor
23710  * @extends Roo.bootstrap.TextArea
23711  * Bootstrap HtmlEditor class
23712
23713  * @constructor
23714  * Create a new HtmlEditor
23715  * @param {Object} config The config object
23716  */
23717
23718 Roo.bootstrap.HtmlEditor = function(config){
23719     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23720     if (!this.toolbars) {
23721         this.toolbars = [];
23722     }
23723     
23724     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23725     this.addEvents({
23726             /**
23727              * @event initialize
23728              * Fires when the editor is fully initialized (including the iframe)
23729              * @param {HtmlEditor} this
23730              */
23731             initialize: true,
23732             /**
23733              * @event activate
23734              * Fires when the editor is first receives the focus. Any insertion must wait
23735              * until after this event.
23736              * @param {HtmlEditor} this
23737              */
23738             activate: true,
23739              /**
23740              * @event beforesync
23741              * Fires before the textarea is updated with content from the editor iframe. Return false
23742              * to cancel the sync.
23743              * @param {HtmlEditor} this
23744              * @param {String} html
23745              */
23746             beforesync: true,
23747              /**
23748              * @event beforepush
23749              * Fires before the iframe editor is updated with content from the textarea. Return false
23750              * to cancel the push.
23751              * @param {HtmlEditor} this
23752              * @param {String} html
23753              */
23754             beforepush: true,
23755              /**
23756              * @event sync
23757              * Fires when the textarea is updated with content from the editor iframe.
23758              * @param {HtmlEditor} this
23759              * @param {String} html
23760              */
23761             sync: true,
23762              /**
23763              * @event push
23764              * Fires when the iframe editor is updated with content from the textarea.
23765              * @param {HtmlEditor} this
23766              * @param {String} html
23767              */
23768             push: true,
23769              /**
23770              * @event editmodechange
23771              * Fires when the editor switches edit modes
23772              * @param {HtmlEditor} this
23773              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23774              */
23775             editmodechange: true,
23776             /**
23777              * @event editorevent
23778              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23779              * @param {HtmlEditor} this
23780              */
23781             editorevent: true,
23782             /**
23783              * @event firstfocus
23784              * Fires when on first focus - needed by toolbars..
23785              * @param {HtmlEditor} this
23786              */
23787             firstfocus: true,
23788             /**
23789              * @event autosave
23790              * Auto save the htmlEditor value as a file into Events
23791              * @param {HtmlEditor} this
23792              */
23793             autosave: true,
23794             /**
23795              * @event savedpreview
23796              * preview the saved version of htmlEditor
23797              * @param {HtmlEditor} this
23798              */
23799             savedpreview: true
23800         });
23801 };
23802
23803
23804 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23805     
23806     
23807       /**
23808      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23809      */
23810     toolbars : false,
23811     
23812      /**
23813     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23814     */
23815     btns : [],
23816    
23817      /**
23818      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23819      *                        Roo.resizable.
23820      */
23821     resizable : false,
23822      /**
23823      * @cfg {Number} height (in pixels)
23824      */   
23825     height: 300,
23826    /**
23827      * @cfg {Number} width (in pixels)
23828      */   
23829     width: false,
23830     
23831     /**
23832      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23833      * 
23834      */
23835     stylesheets: false,
23836     
23837     // id of frame..
23838     frameId: false,
23839     
23840     // private properties
23841     validationEvent : false,
23842     deferHeight: true,
23843     initialized : false,
23844     activated : false,
23845     
23846     onFocus : Roo.emptyFn,
23847     iframePad:3,
23848     hideMode:'offsets',
23849     
23850     tbContainer : false,
23851     
23852     bodyCls : '',
23853     
23854     toolbarContainer :function() {
23855         return this.wrap.select('.x-html-editor-tb',true).first();
23856     },
23857
23858     /**
23859      * Protected method that will not generally be called directly. It
23860      * is called when the editor creates its toolbar. Override this method if you need to
23861      * add custom toolbar buttons.
23862      * @param {HtmlEditor} editor
23863      */
23864     createToolbar : function(){
23865         Roo.log('renewing');
23866         Roo.log("create toolbars");
23867         
23868         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23869         this.toolbars[0].render(this.toolbarContainer());
23870         
23871         return;
23872         
23873 //        if (!editor.toolbars || !editor.toolbars.length) {
23874 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23875 //        }
23876 //        
23877 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23878 //            editor.toolbars[i] = Roo.factory(
23879 //                    typeof(editor.toolbars[i]) == 'string' ?
23880 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23881 //                Roo.bootstrap.HtmlEditor);
23882 //            editor.toolbars[i].init(editor);
23883 //        }
23884     },
23885
23886      
23887     // private
23888     onRender : function(ct, position)
23889     {
23890        // Roo.log("Call onRender: " + this.xtype);
23891         var _t = this;
23892         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23893       
23894         this.wrap = this.inputEl().wrap({
23895             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23896         });
23897         
23898         this.editorcore.onRender(ct, position);
23899          
23900         if (this.resizable) {
23901             this.resizeEl = new Roo.Resizable(this.wrap, {
23902                 pinned : true,
23903                 wrap: true,
23904                 dynamic : true,
23905                 minHeight : this.height,
23906                 height: this.height,
23907                 handles : this.resizable,
23908                 width: this.width,
23909                 listeners : {
23910                     resize : function(r, w, h) {
23911                         _t.onResize(w,h); // -something
23912                     }
23913                 }
23914             });
23915             
23916         }
23917         this.createToolbar(this);
23918        
23919         
23920         if(!this.width && this.resizable){
23921             this.setSize(this.wrap.getSize());
23922         }
23923         if (this.resizeEl) {
23924             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23925             // should trigger onReize..
23926         }
23927         
23928     },
23929
23930     // private
23931     onResize : function(w, h)
23932     {
23933         Roo.log('resize: ' +w + ',' + h );
23934         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23935         var ew = false;
23936         var eh = false;
23937         
23938         if(this.inputEl() ){
23939             if(typeof w == 'number'){
23940                 var aw = w - this.wrap.getFrameWidth('lr');
23941                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23942                 ew = aw;
23943             }
23944             if(typeof h == 'number'){
23945                  var tbh = -11;  // fixme it needs to tool bar size!
23946                 for (var i =0; i < this.toolbars.length;i++) {
23947                     // fixme - ask toolbars for heights?
23948                     tbh += this.toolbars[i].el.getHeight();
23949                     //if (this.toolbars[i].footer) {
23950                     //    tbh += this.toolbars[i].footer.el.getHeight();
23951                     //}
23952                 }
23953               
23954                 
23955                 
23956                 
23957                 
23958                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23959                 ah -= 5; // knock a few pixes off for look..
23960                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23961                 var eh = ah;
23962             }
23963         }
23964         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23965         this.editorcore.onResize(ew,eh);
23966         
23967     },
23968
23969     /**
23970      * Toggles the editor between standard and source edit mode.
23971      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23972      */
23973     toggleSourceEdit : function(sourceEditMode)
23974     {
23975         this.editorcore.toggleSourceEdit(sourceEditMode);
23976         
23977         if(this.editorcore.sourceEditMode){
23978             Roo.log('editor - showing textarea');
23979             
23980 //            Roo.log('in');
23981 //            Roo.log(this.syncValue());
23982             this.syncValue();
23983             this.inputEl().removeClass(['hide', 'x-hidden']);
23984             this.inputEl().dom.removeAttribute('tabIndex');
23985             this.inputEl().focus();
23986         }else{
23987             Roo.log('editor - hiding textarea');
23988 //            Roo.log('out')
23989 //            Roo.log(this.pushValue()); 
23990             this.pushValue();
23991             
23992             this.inputEl().addClass(['hide', 'x-hidden']);
23993             this.inputEl().dom.setAttribute('tabIndex', -1);
23994             //this.deferFocus();
23995         }
23996          
23997         if(this.resizable){
23998             this.setSize(this.wrap.getSize());
23999         }
24000         
24001         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24002     },
24003  
24004     // private (for BoxComponent)
24005     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24006
24007     // private (for BoxComponent)
24008     getResizeEl : function(){
24009         return this.wrap;
24010     },
24011
24012     // private (for BoxComponent)
24013     getPositionEl : function(){
24014         return this.wrap;
24015     },
24016
24017     // private
24018     initEvents : function(){
24019         this.originalValue = this.getValue();
24020     },
24021
24022 //    /**
24023 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24024 //     * @method
24025 //     */
24026 //    markInvalid : Roo.emptyFn,
24027 //    /**
24028 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24029 //     * @method
24030 //     */
24031 //    clearInvalid : Roo.emptyFn,
24032
24033     setValue : function(v){
24034         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24035         this.editorcore.pushValue();
24036     },
24037
24038      
24039     // private
24040     deferFocus : function(){
24041         this.focus.defer(10, this);
24042     },
24043
24044     // doc'ed in Field
24045     focus : function(){
24046         this.editorcore.focus();
24047         
24048     },
24049       
24050
24051     // private
24052     onDestroy : function(){
24053         
24054         
24055         
24056         if(this.rendered){
24057             
24058             for (var i =0; i < this.toolbars.length;i++) {
24059                 // fixme - ask toolbars for heights?
24060                 this.toolbars[i].onDestroy();
24061             }
24062             
24063             this.wrap.dom.innerHTML = '';
24064             this.wrap.remove();
24065         }
24066     },
24067
24068     // private
24069     onFirstFocus : function(){
24070         //Roo.log("onFirstFocus");
24071         this.editorcore.onFirstFocus();
24072          for (var i =0; i < this.toolbars.length;i++) {
24073             this.toolbars[i].onFirstFocus();
24074         }
24075         
24076     },
24077     
24078     // private
24079     syncValue : function()
24080     {   
24081         this.editorcore.syncValue();
24082     },
24083     
24084     pushValue : function()
24085     {   
24086         this.editorcore.pushValue();
24087     }
24088      
24089     
24090     // hide stuff that is not compatible
24091     /**
24092      * @event blur
24093      * @hide
24094      */
24095     /**
24096      * @event change
24097      * @hide
24098      */
24099     /**
24100      * @event focus
24101      * @hide
24102      */
24103     /**
24104      * @event specialkey
24105      * @hide
24106      */
24107     /**
24108      * @cfg {String} fieldClass @hide
24109      */
24110     /**
24111      * @cfg {String} focusClass @hide
24112      */
24113     /**
24114      * @cfg {String} autoCreate @hide
24115      */
24116     /**
24117      * @cfg {String} inputType @hide
24118      */
24119      
24120     /**
24121      * @cfg {String} invalidText @hide
24122      */
24123     /**
24124      * @cfg {String} msgFx @hide
24125      */
24126     /**
24127      * @cfg {String} validateOnBlur @hide
24128      */
24129 });
24130  
24131     
24132    
24133    
24134    
24135       
24136 Roo.namespace('Roo.bootstrap.htmleditor');
24137 /**
24138  * @class Roo.bootstrap.HtmlEditorToolbar1
24139  * Basic Toolbar
24140  * 
24141  * @example
24142  * Usage:
24143  *
24144  new Roo.bootstrap.HtmlEditor({
24145     ....
24146     toolbars : [
24147         new Roo.bootstrap.HtmlEditorToolbar1({
24148             disable : { fonts: 1 , format: 1, ..., ... , ...],
24149             btns : [ .... ]
24150         })
24151     }
24152      
24153  * 
24154  * @cfg {Object} disable List of elements to disable..
24155  * @cfg {Array} btns List of additional buttons.
24156  * 
24157  * 
24158  * NEEDS Extra CSS? 
24159  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24160  */
24161  
24162 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24163 {
24164     
24165     Roo.apply(this, config);
24166     
24167     // default disabled, based on 'good practice'..
24168     this.disable = this.disable || {};
24169     Roo.applyIf(this.disable, {
24170         fontSize : true,
24171         colors : true,
24172         specialElements : true
24173     });
24174     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24175     
24176     this.editor = config.editor;
24177     this.editorcore = config.editor.editorcore;
24178     
24179     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24180     
24181     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24182     // dont call parent... till later.
24183 }
24184 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24185      
24186     bar : true,
24187     
24188     editor : false,
24189     editorcore : false,
24190     
24191     
24192     formats : [
24193         "p" ,  
24194         "h1","h2","h3","h4","h5","h6", 
24195         "pre", "code", 
24196         "abbr", "acronym", "address", "cite", "samp", "var",
24197         'div','span'
24198     ],
24199     
24200     onRender : function(ct, position)
24201     {
24202        // Roo.log("Call onRender: " + this.xtype);
24203         
24204        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24205        Roo.log(this.el);
24206        this.el.dom.style.marginBottom = '0';
24207        var _this = this;
24208        var editorcore = this.editorcore;
24209        var editor= this.editor;
24210        
24211        var children = [];
24212        var btn = function(id,cmd , toggle, handler, html){
24213        
24214             var  event = toggle ? 'toggle' : 'click';
24215        
24216             var a = {
24217                 size : 'sm',
24218                 xtype: 'Button',
24219                 xns: Roo.bootstrap,
24220                 //glyphicon : id,
24221                 fa: id,
24222                 cmd : id || cmd,
24223                 enableToggle:toggle !== false,
24224                 html : html || '',
24225                 pressed : toggle ? false : null,
24226                 listeners : {}
24227             };
24228             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24229                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24230             };
24231             children.push(a);
24232             return a;
24233        }
24234        
24235     //    var cb_box = function...
24236         
24237         var style = {
24238                 xtype: 'Button',
24239                 size : 'sm',
24240                 xns: Roo.bootstrap,
24241                 fa : 'font',
24242                 //html : 'submit'
24243                 menu : {
24244                     xtype: 'Menu',
24245                     xns: Roo.bootstrap,
24246                     items:  []
24247                 }
24248         };
24249         Roo.each(this.formats, function(f) {
24250             style.menu.items.push({
24251                 xtype :'MenuItem',
24252                 xns: Roo.bootstrap,
24253                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24254                 tagname : f,
24255                 listeners : {
24256                     click : function()
24257                     {
24258                         editorcore.insertTag(this.tagname);
24259                         editor.focus();
24260                     }
24261                 }
24262                 
24263             });
24264         });
24265         children.push(style);   
24266         
24267         btn('bold',false,true);
24268         btn('italic',false,true);
24269         btn('align-left', 'justifyleft',true);
24270         btn('align-center', 'justifycenter',true);
24271         btn('align-right' , 'justifyright',true);
24272         btn('link', false, false, function(btn) {
24273             //Roo.log("create link?");
24274             var url = prompt(this.createLinkText, this.defaultLinkValue);
24275             if(url && url != 'http:/'+'/'){
24276                 this.editorcore.relayCmd('createlink', url);
24277             }
24278         }),
24279         btn('list','insertunorderedlist',true);
24280         btn('pencil', false,true, function(btn){
24281                 Roo.log(this);
24282                 this.toggleSourceEdit(btn.pressed);
24283         });
24284         
24285         if (this.editor.btns.length > 0) {
24286             for (var i = 0; i<this.editor.btns.length; i++) {
24287                 children.push(this.editor.btns[i]);
24288             }
24289         }
24290         
24291         /*
24292         var cog = {
24293                 xtype: 'Button',
24294                 size : 'sm',
24295                 xns: Roo.bootstrap,
24296                 glyphicon : 'cog',
24297                 //html : 'submit'
24298                 menu : {
24299                     xtype: 'Menu',
24300                     xns: Roo.bootstrap,
24301                     items:  []
24302                 }
24303         };
24304         
24305         cog.menu.items.push({
24306             xtype :'MenuItem',
24307             xns: Roo.bootstrap,
24308             html : Clean styles,
24309             tagname : f,
24310             listeners : {
24311                 click : function()
24312                 {
24313                     editorcore.insertTag(this.tagname);
24314                     editor.focus();
24315                 }
24316             }
24317             
24318         });
24319        */
24320         
24321          
24322        this.xtype = 'NavSimplebar';
24323         
24324         for(var i=0;i< children.length;i++) {
24325             
24326             this.buttons.add(this.addxtypeChild(children[i]));
24327             
24328         }
24329         
24330         editor.on('editorevent', this.updateToolbar, this);
24331     },
24332     onBtnClick : function(id)
24333     {
24334        this.editorcore.relayCmd(id);
24335        this.editorcore.focus();
24336     },
24337     
24338     /**
24339      * Protected method that will not generally be called directly. It triggers
24340      * a toolbar update by reading the markup state of the current selection in the editor.
24341      */
24342     updateToolbar: function(){
24343
24344         if(!this.editorcore.activated){
24345             this.editor.onFirstFocus(); // is this neeed?
24346             return;
24347         }
24348
24349         var btns = this.buttons; 
24350         var doc = this.editorcore.doc;
24351         btns.get('bold').setActive(doc.queryCommandState('bold'));
24352         btns.get('italic').setActive(doc.queryCommandState('italic'));
24353         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24354         
24355         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24356         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24357         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24358         
24359         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24360         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24361          /*
24362         
24363         var ans = this.editorcore.getAllAncestors();
24364         if (this.formatCombo) {
24365             
24366             
24367             var store = this.formatCombo.store;
24368             this.formatCombo.setValue("");
24369             for (var i =0; i < ans.length;i++) {
24370                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24371                     // select it..
24372                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24373                     break;
24374                 }
24375             }
24376         }
24377         
24378         
24379         
24380         // hides menus... - so this cant be on a menu...
24381         Roo.bootstrap.MenuMgr.hideAll();
24382         */
24383         Roo.bootstrap.MenuMgr.hideAll();
24384         //this.editorsyncValue();
24385     },
24386     onFirstFocus: function() {
24387         this.buttons.each(function(item){
24388            item.enable();
24389         });
24390     },
24391     toggleSourceEdit : function(sourceEditMode){
24392         
24393           
24394         if(sourceEditMode){
24395             Roo.log("disabling buttons");
24396            this.buttons.each( function(item){
24397                 if(item.cmd != 'pencil'){
24398                     item.disable();
24399                 }
24400             });
24401           
24402         }else{
24403             Roo.log("enabling buttons");
24404             if(this.editorcore.initialized){
24405                 this.buttons.each( function(item){
24406                     item.enable();
24407                 });
24408             }
24409             
24410         }
24411         Roo.log("calling toggole on editor");
24412         // tell the editor that it's been pressed..
24413         this.editor.toggleSourceEdit(sourceEditMode);
24414        
24415     }
24416 });
24417
24418
24419
24420
24421
24422 /**
24423  * @class Roo.bootstrap.Table.AbstractSelectionModel
24424  * @extends Roo.util.Observable
24425  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24426  * implemented by descendant classes.  This class should not be directly instantiated.
24427  * @constructor
24428  */
24429 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24430     this.locked = false;
24431     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24432 };
24433
24434
24435 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24436     /** @ignore Called by the grid automatically. Do not call directly. */
24437     init : function(grid){
24438         this.grid = grid;
24439         this.initEvents();
24440     },
24441
24442     /**
24443      * Locks the selections.
24444      */
24445     lock : function(){
24446         this.locked = true;
24447     },
24448
24449     /**
24450      * Unlocks the selections.
24451      */
24452     unlock : function(){
24453         this.locked = false;
24454     },
24455
24456     /**
24457      * Returns true if the selections are locked.
24458      * @return {Boolean}
24459      */
24460     isLocked : function(){
24461         return this.locked;
24462     }
24463 });
24464 /**
24465  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24466  * @class Roo.bootstrap.Table.RowSelectionModel
24467  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24468  * It supports multiple selections and keyboard selection/navigation. 
24469  * @constructor
24470  * @param {Object} config
24471  */
24472
24473 Roo.bootstrap.Table.RowSelectionModel = function(config){
24474     Roo.apply(this, config);
24475     this.selections = new Roo.util.MixedCollection(false, function(o){
24476         return o.id;
24477     });
24478
24479     this.last = false;
24480     this.lastActive = false;
24481
24482     this.addEvents({
24483         /**
24484              * @event selectionchange
24485              * Fires when the selection changes
24486              * @param {SelectionModel} this
24487              */
24488             "selectionchange" : true,
24489         /**
24490              * @event afterselectionchange
24491              * Fires after the selection changes (eg. by key press or clicking)
24492              * @param {SelectionModel} this
24493              */
24494             "afterselectionchange" : true,
24495         /**
24496              * @event beforerowselect
24497              * Fires when a row is selected being selected, return false to cancel.
24498              * @param {SelectionModel} this
24499              * @param {Number} rowIndex The selected index
24500              * @param {Boolean} keepExisting False if other selections will be cleared
24501              */
24502             "beforerowselect" : true,
24503         /**
24504              * @event rowselect
24505              * Fires when a row is selected.
24506              * @param {SelectionModel} this
24507              * @param {Number} rowIndex The selected index
24508              * @param {Roo.data.Record} r The record
24509              */
24510             "rowselect" : true,
24511         /**
24512              * @event rowdeselect
24513              * Fires when a row is deselected.
24514              * @param {SelectionModel} this
24515              * @param {Number} rowIndex The selected index
24516              */
24517         "rowdeselect" : true
24518     });
24519     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24520     this.locked = false;
24521  };
24522
24523 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24524     /**
24525      * @cfg {Boolean} singleSelect
24526      * True to allow selection of only one row at a time (defaults to false)
24527      */
24528     singleSelect : false,
24529
24530     // private
24531     initEvents : function()
24532     {
24533
24534         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24535         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24536         //}else{ // allow click to work like normal
24537          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24538         //}
24539         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24540         this.grid.on("rowclick", this.handleMouseDown, this);
24541         
24542         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24543             "up" : function(e){
24544                 if(!e.shiftKey){
24545                     this.selectPrevious(e.shiftKey);
24546                 }else if(this.last !== false && this.lastActive !== false){
24547                     var last = this.last;
24548                     this.selectRange(this.last,  this.lastActive-1);
24549                     this.grid.getView().focusRow(this.lastActive);
24550                     if(last !== false){
24551                         this.last = last;
24552                     }
24553                 }else{
24554                     this.selectFirstRow();
24555                 }
24556                 this.fireEvent("afterselectionchange", this);
24557             },
24558             "down" : function(e){
24559                 if(!e.shiftKey){
24560                     this.selectNext(e.shiftKey);
24561                 }else if(this.last !== false && this.lastActive !== false){
24562                     var last = this.last;
24563                     this.selectRange(this.last,  this.lastActive+1);
24564                     this.grid.getView().focusRow(this.lastActive);
24565                     if(last !== false){
24566                         this.last = last;
24567                     }
24568                 }else{
24569                     this.selectFirstRow();
24570                 }
24571                 this.fireEvent("afterselectionchange", this);
24572             },
24573             scope: this
24574         });
24575         this.grid.store.on('load', function(){
24576             this.selections.clear();
24577         },this);
24578         /*
24579         var view = this.grid.view;
24580         view.on("refresh", this.onRefresh, this);
24581         view.on("rowupdated", this.onRowUpdated, this);
24582         view.on("rowremoved", this.onRemove, this);
24583         */
24584     },
24585
24586     // private
24587     onRefresh : function()
24588     {
24589         var ds = this.grid.store, i, v = this.grid.view;
24590         var s = this.selections;
24591         s.each(function(r){
24592             if((i = ds.indexOfId(r.id)) != -1){
24593                 v.onRowSelect(i);
24594             }else{
24595                 s.remove(r);
24596             }
24597         });
24598     },
24599
24600     // private
24601     onRemove : function(v, index, r){
24602         this.selections.remove(r);
24603     },
24604
24605     // private
24606     onRowUpdated : function(v, index, r){
24607         if(this.isSelected(r)){
24608             v.onRowSelect(index);
24609         }
24610     },
24611
24612     /**
24613      * Select records.
24614      * @param {Array} records The records to select
24615      * @param {Boolean} keepExisting (optional) True to keep existing selections
24616      */
24617     selectRecords : function(records, keepExisting)
24618     {
24619         if(!keepExisting){
24620             this.clearSelections();
24621         }
24622             var ds = this.grid.store;
24623         for(var i = 0, len = records.length; i < len; i++){
24624             this.selectRow(ds.indexOf(records[i]), true);
24625         }
24626     },
24627
24628     /**
24629      * Gets the number of selected rows.
24630      * @return {Number}
24631      */
24632     getCount : function(){
24633         return this.selections.length;
24634     },
24635
24636     /**
24637      * Selects the first row in the grid.
24638      */
24639     selectFirstRow : function(){
24640         this.selectRow(0);
24641     },
24642
24643     /**
24644      * Select the last row.
24645      * @param {Boolean} keepExisting (optional) True to keep existing selections
24646      */
24647     selectLastRow : function(keepExisting){
24648         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24649         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24650     },
24651
24652     /**
24653      * Selects the row immediately following the last selected row.
24654      * @param {Boolean} keepExisting (optional) True to keep existing selections
24655      */
24656     selectNext : function(keepExisting)
24657     {
24658             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24659             this.selectRow(this.last+1, keepExisting);
24660             this.grid.getView().focusRow(this.last);
24661         }
24662     },
24663
24664     /**
24665      * Selects the row that precedes the last selected row.
24666      * @param {Boolean} keepExisting (optional) True to keep existing selections
24667      */
24668     selectPrevious : function(keepExisting){
24669         if(this.last){
24670             this.selectRow(this.last-1, keepExisting);
24671             this.grid.getView().focusRow(this.last);
24672         }
24673     },
24674
24675     /**
24676      * Returns the selected records
24677      * @return {Array} Array of selected records
24678      */
24679     getSelections : function(){
24680         return [].concat(this.selections.items);
24681     },
24682
24683     /**
24684      * Returns the first selected record.
24685      * @return {Record}
24686      */
24687     getSelected : function(){
24688         return this.selections.itemAt(0);
24689     },
24690
24691
24692     /**
24693      * Clears all selections.
24694      */
24695     clearSelections : function(fast)
24696     {
24697         if(this.locked) {
24698             return;
24699         }
24700         if(fast !== true){
24701                 var ds = this.grid.store;
24702             var s = this.selections;
24703             s.each(function(r){
24704                 this.deselectRow(ds.indexOfId(r.id));
24705             }, this);
24706             s.clear();
24707         }else{
24708             this.selections.clear();
24709         }
24710         this.last = false;
24711     },
24712
24713
24714     /**
24715      * Selects all rows.
24716      */
24717     selectAll : function(){
24718         if(this.locked) {
24719             return;
24720         }
24721         this.selections.clear();
24722         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24723             this.selectRow(i, true);
24724         }
24725     },
24726
24727     /**
24728      * Returns True if there is a selection.
24729      * @return {Boolean}
24730      */
24731     hasSelection : function(){
24732         return this.selections.length > 0;
24733     },
24734
24735     /**
24736      * Returns True if the specified row is selected.
24737      * @param {Number/Record} record The record or index of the record to check
24738      * @return {Boolean}
24739      */
24740     isSelected : function(index){
24741             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24742         return (r && this.selections.key(r.id) ? true : false);
24743     },
24744
24745     /**
24746      * Returns True if the specified record id is selected.
24747      * @param {String} id The id of record to check
24748      * @return {Boolean}
24749      */
24750     isIdSelected : function(id){
24751         return (this.selections.key(id) ? true : false);
24752     },
24753
24754
24755     // private
24756     handleMouseDBClick : function(e, t){
24757         
24758     },
24759     // private
24760     handleMouseDown : function(e, t)
24761     {
24762             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24763         if(this.isLocked() || rowIndex < 0 ){
24764             return;
24765         };
24766         if(e.shiftKey && this.last !== false){
24767             var last = this.last;
24768             this.selectRange(last, rowIndex, e.ctrlKey);
24769             this.last = last; // reset the last
24770             t.focus();
24771     
24772         }else{
24773             var isSelected = this.isSelected(rowIndex);
24774             //Roo.log("select row:" + rowIndex);
24775             if(isSelected){
24776                 this.deselectRow(rowIndex);
24777             } else {
24778                         this.selectRow(rowIndex, true);
24779             }
24780     
24781             /*
24782                 if(e.button !== 0 && isSelected){
24783                 alert('rowIndex 2: ' + rowIndex);
24784                     view.focusRow(rowIndex);
24785                 }else if(e.ctrlKey && isSelected){
24786                     this.deselectRow(rowIndex);
24787                 }else if(!isSelected){
24788                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24789                     view.focusRow(rowIndex);
24790                 }
24791             */
24792         }
24793         this.fireEvent("afterselectionchange", this);
24794     },
24795     // private
24796     handleDragableRowClick :  function(grid, rowIndex, e) 
24797     {
24798         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24799             this.selectRow(rowIndex, false);
24800             grid.view.focusRow(rowIndex);
24801              this.fireEvent("afterselectionchange", this);
24802         }
24803     },
24804     
24805     /**
24806      * Selects multiple rows.
24807      * @param {Array} rows Array of the indexes of the row to select
24808      * @param {Boolean} keepExisting (optional) True to keep existing selections
24809      */
24810     selectRows : function(rows, keepExisting){
24811         if(!keepExisting){
24812             this.clearSelections();
24813         }
24814         for(var i = 0, len = rows.length; i < len; i++){
24815             this.selectRow(rows[i], true);
24816         }
24817     },
24818
24819     /**
24820      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24821      * @param {Number} startRow The index of the first row in the range
24822      * @param {Number} endRow The index of the last row in the range
24823      * @param {Boolean} keepExisting (optional) True to retain existing selections
24824      */
24825     selectRange : function(startRow, endRow, keepExisting){
24826         if(this.locked) {
24827             return;
24828         }
24829         if(!keepExisting){
24830             this.clearSelections();
24831         }
24832         if(startRow <= endRow){
24833             for(var i = startRow; i <= endRow; i++){
24834                 this.selectRow(i, true);
24835             }
24836         }else{
24837             for(var i = startRow; i >= endRow; i--){
24838                 this.selectRow(i, true);
24839             }
24840         }
24841     },
24842
24843     /**
24844      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24845      * @param {Number} startRow The index of the first row in the range
24846      * @param {Number} endRow The index of the last row in the range
24847      */
24848     deselectRange : function(startRow, endRow, preventViewNotify){
24849         if(this.locked) {
24850             return;
24851         }
24852         for(var i = startRow; i <= endRow; i++){
24853             this.deselectRow(i, preventViewNotify);
24854         }
24855     },
24856
24857     /**
24858      * Selects a row.
24859      * @param {Number} row The index of the row to select
24860      * @param {Boolean} keepExisting (optional) True to keep existing selections
24861      */
24862     selectRow : function(index, keepExisting, preventViewNotify)
24863     {
24864             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24865             return;
24866         }
24867         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24868             if(!keepExisting || this.singleSelect){
24869                 this.clearSelections();
24870             }
24871             
24872             var r = this.grid.store.getAt(index);
24873             //console.log('selectRow - record id :' + r.id);
24874             
24875             this.selections.add(r);
24876             this.last = this.lastActive = index;
24877             if(!preventViewNotify){
24878                 var proxy = new Roo.Element(
24879                                 this.grid.getRowDom(index)
24880                 );
24881                 proxy.addClass('bg-info info');
24882             }
24883             this.fireEvent("rowselect", this, index, r);
24884             this.fireEvent("selectionchange", this);
24885         }
24886     },
24887
24888     /**
24889      * Deselects a row.
24890      * @param {Number} row The index of the row to deselect
24891      */
24892     deselectRow : function(index, preventViewNotify)
24893     {
24894         if(this.locked) {
24895             return;
24896         }
24897         if(this.last == index){
24898             this.last = false;
24899         }
24900         if(this.lastActive == index){
24901             this.lastActive = false;
24902         }
24903         
24904         var r = this.grid.store.getAt(index);
24905         if (!r) {
24906             return;
24907         }
24908         
24909         this.selections.remove(r);
24910         //.console.log('deselectRow - record id :' + r.id);
24911         if(!preventViewNotify){
24912         
24913             var proxy = new Roo.Element(
24914                 this.grid.getRowDom(index)
24915             );
24916             proxy.removeClass('bg-info info');
24917         }
24918         this.fireEvent("rowdeselect", this, index);
24919         this.fireEvent("selectionchange", this);
24920     },
24921
24922     // private
24923     restoreLast : function(){
24924         if(this._last){
24925             this.last = this._last;
24926         }
24927     },
24928
24929     // private
24930     acceptsNav : function(row, col, cm){
24931         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24932     },
24933
24934     // private
24935     onEditorKey : function(field, e){
24936         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24937         if(k == e.TAB){
24938             e.stopEvent();
24939             ed.completeEdit();
24940             if(e.shiftKey){
24941                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24942             }else{
24943                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24944             }
24945         }else if(k == e.ENTER && !e.ctrlKey){
24946             e.stopEvent();
24947             ed.completeEdit();
24948             if(e.shiftKey){
24949                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24950             }else{
24951                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24952             }
24953         }else if(k == e.ESC){
24954             ed.cancelEdit();
24955         }
24956         if(newCell){
24957             g.startEditing(newCell[0], newCell[1]);
24958         }
24959     }
24960 });
24961 /*
24962  * Based on:
24963  * Ext JS Library 1.1.1
24964  * Copyright(c) 2006-2007, Ext JS, LLC.
24965  *
24966  * Originally Released Under LGPL - original licence link has changed is not relivant.
24967  *
24968  * Fork - LGPL
24969  * <script type="text/javascript">
24970  */
24971  
24972 /**
24973  * @class Roo.bootstrap.PagingToolbar
24974  * @extends Roo.bootstrap.NavSimplebar
24975  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24976  * @constructor
24977  * Create a new PagingToolbar
24978  * @param {Object} config The config object
24979  * @param {Roo.data.Store} store
24980  */
24981 Roo.bootstrap.PagingToolbar = function(config)
24982 {
24983     // old args format still supported... - xtype is prefered..
24984         // created from xtype...
24985     
24986     this.ds = config.dataSource;
24987     
24988     if (config.store && !this.ds) {
24989         this.store= Roo.factory(config.store, Roo.data);
24990         this.ds = this.store;
24991         this.ds.xmodule = this.xmodule || false;
24992     }
24993     
24994     this.toolbarItems = [];
24995     if (config.items) {
24996         this.toolbarItems = config.items;
24997     }
24998     
24999     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25000     
25001     this.cursor = 0;
25002     
25003     if (this.ds) { 
25004         this.bind(this.ds);
25005     }
25006     
25007     if (Roo.bootstrap.version == 4) {
25008         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25009     } else {
25010         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25011     }
25012     
25013 };
25014
25015 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25016     /**
25017      * @cfg {Roo.data.Store} dataSource
25018      * The underlying data store providing the paged data
25019      */
25020     /**
25021      * @cfg {String/HTMLElement/Element} container
25022      * container The id or element that will contain the toolbar
25023      */
25024     /**
25025      * @cfg {Boolean} displayInfo
25026      * True to display the displayMsg (defaults to false)
25027      */
25028     /**
25029      * @cfg {Number} pageSize
25030      * The number of records to display per page (defaults to 20)
25031      */
25032     pageSize: 20,
25033     /**
25034      * @cfg {String} displayMsg
25035      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25036      */
25037     displayMsg : 'Displaying {0} - {1} of {2}',
25038     /**
25039      * @cfg {String} emptyMsg
25040      * The message to display when no records are found (defaults to "No data to display")
25041      */
25042     emptyMsg : 'No data to display',
25043     /**
25044      * Customizable piece of the default paging text (defaults to "Page")
25045      * @type String
25046      */
25047     beforePageText : "Page",
25048     /**
25049      * Customizable piece of the default paging text (defaults to "of %0")
25050      * @type String
25051      */
25052     afterPageText : "of {0}",
25053     /**
25054      * Customizable piece of the default paging text (defaults to "First Page")
25055      * @type String
25056      */
25057     firstText : "First Page",
25058     /**
25059      * Customizable piece of the default paging text (defaults to "Previous Page")
25060      * @type String
25061      */
25062     prevText : "Previous Page",
25063     /**
25064      * Customizable piece of the default paging text (defaults to "Next Page")
25065      * @type String
25066      */
25067     nextText : "Next Page",
25068     /**
25069      * Customizable piece of the default paging text (defaults to "Last Page")
25070      * @type String
25071      */
25072     lastText : "Last Page",
25073     /**
25074      * Customizable piece of the default paging text (defaults to "Refresh")
25075      * @type String
25076      */
25077     refreshText : "Refresh",
25078
25079     buttons : false,
25080     // private
25081     onRender : function(ct, position) 
25082     {
25083         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25084         this.navgroup.parentId = this.id;
25085         this.navgroup.onRender(this.el, null);
25086         // add the buttons to the navgroup
25087         
25088         if(this.displayInfo){
25089             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25090             this.displayEl = this.el.select('.x-paging-info', true).first();
25091 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25092 //            this.displayEl = navel.el.select('span',true).first();
25093         }
25094         
25095         var _this = this;
25096         
25097         if(this.buttons){
25098             Roo.each(_this.buttons, function(e){ // this might need to use render????
25099                Roo.factory(e).render(_this.el);
25100             });
25101         }
25102             
25103         Roo.each(_this.toolbarItems, function(e) {
25104             _this.navgroup.addItem(e);
25105         });
25106         
25107         
25108         this.first = this.navgroup.addItem({
25109             tooltip: this.firstText,
25110             cls: "prev btn-outline-secondary",
25111             html : ' <i class="fa fa-step-backward"></i>',
25112             disabled: true,
25113             preventDefault: true,
25114             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25115         });
25116         
25117         this.prev =  this.navgroup.addItem({
25118             tooltip: this.prevText,
25119             cls: "prev btn-outline-secondary",
25120             html : ' <i class="fa fa-backward"></i>',
25121             disabled: true,
25122             preventDefault: true,
25123             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25124         });
25125     //this.addSeparator();
25126         
25127         
25128         var field = this.navgroup.addItem( {
25129             tagtype : 'span',
25130             cls : 'x-paging-position  btn-outline-secondary',
25131              disabled: true,
25132             html : this.beforePageText  +
25133                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25134                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25135          } ); //?? escaped?
25136         
25137         this.field = field.el.select('input', true).first();
25138         this.field.on("keydown", this.onPagingKeydown, this);
25139         this.field.on("focus", function(){this.dom.select();});
25140     
25141     
25142         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25143         //this.field.setHeight(18);
25144         //this.addSeparator();
25145         this.next = this.navgroup.addItem({
25146             tooltip: this.nextText,
25147             cls: "next btn-outline-secondary",
25148             html : ' <i class="fa fa-forward"></i>',
25149             disabled: true,
25150             preventDefault: true,
25151             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25152         });
25153         this.last = this.navgroup.addItem({
25154             tooltip: this.lastText,
25155             html : ' <i class="fa fa-step-forward"></i>',
25156             cls: "next btn-outline-secondary",
25157             disabled: true,
25158             preventDefault: true,
25159             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25160         });
25161     //this.addSeparator();
25162         this.loading = this.navgroup.addItem({
25163             tooltip: this.refreshText,
25164             cls: "btn-outline-secondary",
25165             html : ' <i class="fa fa-refresh"></i>',
25166             preventDefault: true,
25167             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25168         });
25169         
25170     },
25171
25172     // private
25173     updateInfo : function(){
25174         if(this.displayEl){
25175             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25176             var msg = count == 0 ?
25177                 this.emptyMsg :
25178                 String.format(
25179                     this.displayMsg,
25180                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25181                 );
25182             this.displayEl.update(msg);
25183         }
25184     },
25185
25186     // private
25187     onLoad : function(ds, r, o)
25188     {
25189         this.cursor = o.params.start ? o.params.start : 0;
25190         
25191         var d = this.getPageData(),
25192             ap = d.activePage,
25193             ps = d.pages;
25194         
25195         
25196         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25197         this.field.dom.value = ap;
25198         this.first.setDisabled(ap == 1);
25199         this.prev.setDisabled(ap == 1);
25200         this.next.setDisabled(ap == ps);
25201         this.last.setDisabled(ap == ps);
25202         this.loading.enable();
25203         this.updateInfo();
25204     },
25205
25206     // private
25207     getPageData : function(){
25208         var total = this.ds.getTotalCount();
25209         return {
25210             total : total,
25211             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25212             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25213         };
25214     },
25215
25216     // private
25217     onLoadError : function(){
25218         this.loading.enable();
25219     },
25220
25221     // private
25222     onPagingKeydown : function(e){
25223         var k = e.getKey();
25224         var d = this.getPageData();
25225         if(k == e.RETURN){
25226             var v = this.field.dom.value, pageNum;
25227             if(!v || isNaN(pageNum = parseInt(v, 10))){
25228                 this.field.dom.value = d.activePage;
25229                 return;
25230             }
25231             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25232             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25233             e.stopEvent();
25234         }
25235         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))
25236         {
25237           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25238           this.field.dom.value = pageNum;
25239           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25240           e.stopEvent();
25241         }
25242         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25243         {
25244           var v = this.field.dom.value, pageNum; 
25245           var increment = (e.shiftKey) ? 10 : 1;
25246           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25247                 increment *= -1;
25248           }
25249           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25250             this.field.dom.value = d.activePage;
25251             return;
25252           }
25253           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25254           {
25255             this.field.dom.value = parseInt(v, 10) + increment;
25256             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25257             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25258           }
25259           e.stopEvent();
25260         }
25261     },
25262
25263     // private
25264     beforeLoad : function(){
25265         if(this.loading){
25266             this.loading.disable();
25267         }
25268     },
25269
25270     // private
25271     onClick : function(which){
25272         
25273         var ds = this.ds;
25274         if (!ds) {
25275             return;
25276         }
25277         
25278         switch(which){
25279             case "first":
25280                 ds.load({params:{start: 0, limit: this.pageSize}});
25281             break;
25282             case "prev":
25283                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25284             break;
25285             case "next":
25286                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25287             break;
25288             case "last":
25289                 var total = ds.getTotalCount();
25290                 var extra = total % this.pageSize;
25291                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25292                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25293             break;
25294             case "refresh":
25295                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25296             break;
25297         }
25298     },
25299
25300     /**
25301      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25302      * @param {Roo.data.Store} store The data store to unbind
25303      */
25304     unbind : function(ds){
25305         ds.un("beforeload", this.beforeLoad, this);
25306         ds.un("load", this.onLoad, this);
25307         ds.un("loadexception", this.onLoadError, this);
25308         ds.un("remove", this.updateInfo, this);
25309         ds.un("add", this.updateInfo, this);
25310         this.ds = undefined;
25311     },
25312
25313     /**
25314      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25315      * @param {Roo.data.Store} store The data store to bind
25316      */
25317     bind : function(ds){
25318         ds.on("beforeload", this.beforeLoad, this);
25319         ds.on("load", this.onLoad, this);
25320         ds.on("loadexception", this.onLoadError, this);
25321         ds.on("remove", this.updateInfo, this);
25322         ds.on("add", this.updateInfo, this);
25323         this.ds = ds;
25324     }
25325 });/*
25326  * - LGPL
25327  *
25328  * element
25329  * 
25330  */
25331
25332 /**
25333  * @class Roo.bootstrap.MessageBar
25334  * @extends Roo.bootstrap.Component
25335  * Bootstrap MessageBar class
25336  * @cfg {String} html contents of the MessageBar
25337  * @cfg {String} weight (info | success | warning | danger) default info
25338  * @cfg {String} beforeClass insert the bar before the given class
25339  * @cfg {Boolean} closable (true | false) default false
25340  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25341  * 
25342  * @constructor
25343  * Create a new Element
25344  * @param {Object} config The config object
25345  */
25346
25347 Roo.bootstrap.MessageBar = function(config){
25348     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25349 };
25350
25351 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25352     
25353     html: '',
25354     weight: 'info',
25355     closable: false,
25356     fixed: false,
25357     beforeClass: 'bootstrap-sticky-wrap',
25358     
25359     getAutoCreate : function(){
25360         
25361         var cfg = {
25362             tag: 'div',
25363             cls: 'alert alert-dismissable alert-' + this.weight,
25364             cn: [
25365                 {
25366                     tag: 'span',
25367                     cls: 'message',
25368                     html: this.html || ''
25369                 }
25370             ]
25371         };
25372         
25373         if(this.fixed){
25374             cfg.cls += ' alert-messages-fixed';
25375         }
25376         
25377         if(this.closable){
25378             cfg.cn.push({
25379                 tag: 'button',
25380                 cls: 'close',
25381                 html: 'x'
25382             });
25383         }
25384         
25385         return cfg;
25386     },
25387     
25388     onRender : function(ct, position)
25389     {
25390         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25391         
25392         if(!this.el){
25393             var cfg = Roo.apply({},  this.getAutoCreate());
25394             cfg.id = Roo.id();
25395             
25396             if (this.cls) {
25397                 cfg.cls += ' ' + this.cls;
25398             }
25399             if (this.style) {
25400                 cfg.style = this.style;
25401             }
25402             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25403             
25404             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25405         }
25406         
25407         this.el.select('>button.close').on('click', this.hide, this);
25408         
25409     },
25410     
25411     show : function()
25412     {
25413         if (!this.rendered) {
25414             this.render();
25415         }
25416         
25417         this.el.show();
25418         
25419         this.fireEvent('show', this);
25420         
25421     },
25422     
25423     hide : function()
25424     {
25425         if (!this.rendered) {
25426             this.render();
25427         }
25428         
25429         this.el.hide();
25430         
25431         this.fireEvent('hide', this);
25432     },
25433     
25434     update : function()
25435     {
25436 //        var e = this.el.dom.firstChild;
25437 //        
25438 //        if(this.closable){
25439 //            e = e.nextSibling;
25440 //        }
25441 //        
25442 //        e.data = this.html || '';
25443
25444         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25445     }
25446    
25447 });
25448
25449  
25450
25451      /*
25452  * - LGPL
25453  *
25454  * Graph
25455  * 
25456  */
25457
25458
25459 /**
25460  * @class Roo.bootstrap.Graph
25461  * @extends Roo.bootstrap.Component
25462  * Bootstrap Graph class
25463 > Prameters
25464  -sm {number} sm 4
25465  -md {number} md 5
25466  @cfg {String} graphtype  bar | vbar | pie
25467  @cfg {number} g_x coodinator | centre x (pie)
25468  @cfg {number} g_y coodinator | centre y (pie)
25469  @cfg {number} g_r radius (pie)
25470  @cfg {number} g_height height of the chart (respected by all elements in the set)
25471  @cfg {number} g_width width of the chart (respected by all elements in the set)
25472  @cfg {Object} title The title of the chart
25473     
25474  -{Array}  values
25475  -opts (object) options for the chart 
25476      o {
25477      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25478      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25479      o vgutter (number)
25480      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.
25481      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25482      o to
25483      o stretch (boolean)
25484      o }
25485  -opts (object) options for the pie
25486      o{
25487      o cut
25488      o startAngle (number)
25489      o endAngle (number)
25490      } 
25491  *
25492  * @constructor
25493  * Create a new Input
25494  * @param {Object} config The config object
25495  */
25496
25497 Roo.bootstrap.Graph = function(config){
25498     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25499     
25500     this.addEvents({
25501         // img events
25502         /**
25503          * @event click
25504          * The img click event for the img.
25505          * @param {Roo.EventObject} e
25506          */
25507         "click" : true
25508     });
25509 };
25510
25511 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25512     
25513     sm: 4,
25514     md: 5,
25515     graphtype: 'bar',
25516     g_height: 250,
25517     g_width: 400,
25518     g_x: 50,
25519     g_y: 50,
25520     g_r: 30,
25521     opts:{
25522         //g_colors: this.colors,
25523         g_type: 'soft',
25524         g_gutter: '20%'
25525
25526     },
25527     title : false,
25528
25529     getAutoCreate : function(){
25530         
25531         var cfg = {
25532             tag: 'div',
25533             html : null
25534         };
25535         
25536         
25537         return  cfg;
25538     },
25539
25540     onRender : function(ct,position){
25541         
25542         
25543         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25544         
25545         if (typeof(Raphael) == 'undefined') {
25546             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25547             return;
25548         }
25549         
25550         this.raphael = Raphael(this.el.dom);
25551         
25552                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25553                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25554                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25555                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25556                 /*
25557                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25558                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25559                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25560                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25561                 
25562                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25563                 r.barchart(330, 10, 300, 220, data1);
25564                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25565                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25566                 */
25567                 
25568                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25569                 // r.barchart(30, 30, 560, 250,  xdata, {
25570                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25571                 //     axis : "0 0 1 1",
25572                 //     axisxlabels :  xdata
25573                 //     //yvalues : cols,
25574                    
25575                 // });
25576 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25577 //        
25578 //        this.load(null,xdata,{
25579 //                axis : "0 0 1 1",
25580 //                axisxlabels :  xdata
25581 //                });
25582
25583     },
25584
25585     load : function(graphtype,xdata,opts)
25586     {
25587         this.raphael.clear();
25588         if(!graphtype) {
25589             graphtype = this.graphtype;
25590         }
25591         if(!opts){
25592             opts = this.opts;
25593         }
25594         var r = this.raphael,
25595             fin = function () {
25596                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25597             },
25598             fout = function () {
25599                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25600             },
25601             pfin = function() {
25602                 this.sector.stop();
25603                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25604
25605                 if (this.label) {
25606                     this.label[0].stop();
25607                     this.label[0].attr({ r: 7.5 });
25608                     this.label[1].attr({ "font-weight": 800 });
25609                 }
25610             },
25611             pfout = function() {
25612                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25613
25614                 if (this.label) {
25615                     this.label[0].animate({ r: 5 }, 500, "bounce");
25616                     this.label[1].attr({ "font-weight": 400 });
25617                 }
25618             };
25619
25620         switch(graphtype){
25621             case 'bar':
25622                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25623                 break;
25624             case 'hbar':
25625                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25626                 break;
25627             case 'pie':
25628 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25629 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25630 //            
25631                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25632                 
25633                 break;
25634
25635         }
25636         
25637         if(this.title){
25638             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25639         }
25640         
25641     },
25642     
25643     setTitle: function(o)
25644     {
25645         this.title = o;
25646     },
25647     
25648     initEvents: function() {
25649         
25650         if(!this.href){
25651             this.el.on('click', this.onClick, this);
25652         }
25653     },
25654     
25655     onClick : function(e)
25656     {
25657         Roo.log('img onclick');
25658         this.fireEvent('click', this, e);
25659     }
25660    
25661 });
25662
25663  
25664 /*
25665  * - LGPL
25666  *
25667  * numberBox
25668  * 
25669  */
25670 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25671
25672 /**
25673  * @class Roo.bootstrap.dash.NumberBox
25674  * @extends Roo.bootstrap.Component
25675  * Bootstrap NumberBox class
25676  * @cfg {String} headline Box headline
25677  * @cfg {String} content Box content
25678  * @cfg {String} icon Box icon
25679  * @cfg {String} footer Footer text
25680  * @cfg {String} fhref Footer href
25681  * 
25682  * @constructor
25683  * Create a new NumberBox
25684  * @param {Object} config The config object
25685  */
25686
25687
25688 Roo.bootstrap.dash.NumberBox = function(config){
25689     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25690     
25691 };
25692
25693 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25694     
25695     headline : '',
25696     content : '',
25697     icon : '',
25698     footer : '',
25699     fhref : '',
25700     ficon : '',
25701     
25702     getAutoCreate : function(){
25703         
25704         var cfg = {
25705             tag : 'div',
25706             cls : 'small-box ',
25707             cn : [
25708                 {
25709                     tag : 'div',
25710                     cls : 'inner',
25711                     cn :[
25712                         {
25713                             tag : 'h3',
25714                             cls : 'roo-headline',
25715                             html : this.headline
25716                         },
25717                         {
25718                             tag : 'p',
25719                             cls : 'roo-content',
25720                             html : this.content
25721                         }
25722                     ]
25723                 }
25724             ]
25725         };
25726         
25727         if(this.icon){
25728             cfg.cn.push({
25729                 tag : 'div',
25730                 cls : 'icon',
25731                 cn :[
25732                     {
25733                         tag : 'i',
25734                         cls : 'ion ' + this.icon
25735                     }
25736                 ]
25737             });
25738         }
25739         
25740         if(this.footer){
25741             var footer = {
25742                 tag : 'a',
25743                 cls : 'small-box-footer',
25744                 href : this.fhref || '#',
25745                 html : this.footer
25746             };
25747             
25748             cfg.cn.push(footer);
25749             
25750         }
25751         
25752         return  cfg;
25753     },
25754
25755     onRender : function(ct,position){
25756         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25757
25758
25759        
25760                 
25761     },
25762
25763     setHeadline: function (value)
25764     {
25765         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25766     },
25767     
25768     setFooter: function (value, href)
25769     {
25770         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25771         
25772         if(href){
25773             this.el.select('a.small-box-footer',true).first().attr('href', href);
25774         }
25775         
25776     },
25777
25778     setContent: function (value)
25779     {
25780         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25781     },
25782
25783     initEvents: function() 
25784     {   
25785         
25786     }
25787     
25788 });
25789
25790  
25791 /*
25792  * - LGPL
25793  *
25794  * TabBox
25795  * 
25796  */
25797 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25798
25799 /**
25800  * @class Roo.bootstrap.dash.TabBox
25801  * @extends Roo.bootstrap.Component
25802  * Bootstrap TabBox class
25803  * @cfg {String} title Title of the TabBox
25804  * @cfg {String} icon Icon of the TabBox
25805  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25806  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25807  * 
25808  * @constructor
25809  * Create a new TabBox
25810  * @param {Object} config The config object
25811  */
25812
25813
25814 Roo.bootstrap.dash.TabBox = function(config){
25815     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25816     this.addEvents({
25817         // raw events
25818         /**
25819          * @event addpane
25820          * When a pane is added
25821          * @param {Roo.bootstrap.dash.TabPane} pane
25822          */
25823         "addpane" : true,
25824         /**
25825          * @event activatepane
25826          * When a pane is activated
25827          * @param {Roo.bootstrap.dash.TabPane} pane
25828          */
25829         "activatepane" : true
25830         
25831          
25832     });
25833     
25834     this.panes = [];
25835 };
25836
25837 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25838
25839     title : '',
25840     icon : false,
25841     showtabs : true,
25842     tabScrollable : false,
25843     
25844     getChildContainer : function()
25845     {
25846         return this.el.select('.tab-content', true).first();
25847     },
25848     
25849     getAutoCreate : function(){
25850         
25851         var header = {
25852             tag: 'li',
25853             cls: 'pull-left header',
25854             html: this.title,
25855             cn : []
25856         };
25857         
25858         if(this.icon){
25859             header.cn.push({
25860                 tag: 'i',
25861                 cls: 'fa ' + this.icon
25862             });
25863         }
25864         
25865         var h = {
25866             tag: 'ul',
25867             cls: 'nav nav-tabs pull-right',
25868             cn: [
25869                 header
25870             ]
25871         };
25872         
25873         if(this.tabScrollable){
25874             h = {
25875                 tag: 'div',
25876                 cls: 'tab-header',
25877                 cn: [
25878                     {
25879                         tag: 'ul',
25880                         cls: 'nav nav-tabs pull-right',
25881                         cn: [
25882                             header
25883                         ]
25884                     }
25885                 ]
25886             };
25887         }
25888         
25889         var cfg = {
25890             tag: 'div',
25891             cls: 'nav-tabs-custom',
25892             cn: [
25893                 h,
25894                 {
25895                     tag: 'div',
25896                     cls: 'tab-content no-padding',
25897                     cn: []
25898                 }
25899             ]
25900         };
25901
25902         return  cfg;
25903     },
25904     initEvents : function()
25905     {
25906         //Roo.log('add add pane handler');
25907         this.on('addpane', this.onAddPane, this);
25908     },
25909      /**
25910      * Updates the box title
25911      * @param {String} html to set the title to.
25912      */
25913     setTitle : function(value)
25914     {
25915         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25916     },
25917     onAddPane : function(pane)
25918     {
25919         this.panes.push(pane);
25920         //Roo.log('addpane');
25921         //Roo.log(pane);
25922         // tabs are rendere left to right..
25923         if(!this.showtabs){
25924             return;
25925         }
25926         
25927         var ctr = this.el.select('.nav-tabs', true).first();
25928          
25929          
25930         var existing = ctr.select('.nav-tab',true);
25931         var qty = existing.getCount();;
25932         
25933         
25934         var tab = ctr.createChild({
25935             tag : 'li',
25936             cls : 'nav-tab' + (qty ? '' : ' active'),
25937             cn : [
25938                 {
25939                     tag : 'a',
25940                     href:'#',
25941                     html : pane.title
25942                 }
25943             ]
25944         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25945         pane.tab = tab;
25946         
25947         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25948         if (!qty) {
25949             pane.el.addClass('active');
25950         }
25951         
25952                 
25953     },
25954     onTabClick : function(ev,un,ob,pane)
25955     {
25956         //Roo.log('tab - prev default');
25957         ev.preventDefault();
25958         
25959         
25960         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25961         pane.tab.addClass('active');
25962         //Roo.log(pane.title);
25963         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25964         // technically we should have a deactivate event.. but maybe add later.
25965         // and it should not de-activate the selected tab...
25966         this.fireEvent('activatepane', pane);
25967         pane.el.addClass('active');
25968         pane.fireEvent('activate');
25969         
25970         
25971     },
25972     
25973     getActivePane : function()
25974     {
25975         var r = false;
25976         Roo.each(this.panes, function(p) {
25977             if(p.el.hasClass('active')){
25978                 r = p;
25979                 return false;
25980             }
25981             
25982             return;
25983         });
25984         
25985         return r;
25986     }
25987     
25988     
25989 });
25990
25991  
25992 /*
25993  * - LGPL
25994  *
25995  * Tab pane
25996  * 
25997  */
25998 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25999 /**
26000  * @class Roo.bootstrap.TabPane
26001  * @extends Roo.bootstrap.Component
26002  * Bootstrap TabPane class
26003  * @cfg {Boolean} active (false | true) Default false
26004  * @cfg {String} title title of panel
26005
26006  * 
26007  * @constructor
26008  * Create a new TabPane
26009  * @param {Object} config The config object
26010  */
26011
26012 Roo.bootstrap.dash.TabPane = function(config){
26013     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26014     
26015     this.addEvents({
26016         // raw events
26017         /**
26018          * @event activate
26019          * When a pane is activated
26020          * @param {Roo.bootstrap.dash.TabPane} pane
26021          */
26022         "activate" : true
26023          
26024     });
26025 };
26026
26027 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
26028     
26029     active : false,
26030     title : '',
26031     
26032     // the tabBox that this is attached to.
26033     tab : false,
26034      
26035     getAutoCreate : function() 
26036     {
26037         var cfg = {
26038             tag: 'div',
26039             cls: 'tab-pane'
26040         };
26041         
26042         if(this.active){
26043             cfg.cls += ' active';
26044         }
26045         
26046         return cfg;
26047     },
26048     initEvents  : function()
26049     {
26050         //Roo.log('trigger add pane handler');
26051         this.parent().fireEvent('addpane', this)
26052     },
26053     
26054      /**
26055      * Updates the tab title 
26056      * @param {String} html to set the title to.
26057      */
26058     setTitle: function(str)
26059     {
26060         if (!this.tab) {
26061             return;
26062         }
26063         this.title = str;
26064         this.tab.select('a', true).first().dom.innerHTML = str;
26065         
26066     }
26067     
26068     
26069     
26070 });
26071
26072  
26073
26074
26075  /*
26076  * - LGPL
26077  *
26078  * menu
26079  * 
26080  */
26081 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26082
26083 /**
26084  * @class Roo.bootstrap.menu.Menu
26085  * @extends Roo.bootstrap.Component
26086  * Bootstrap Menu class - container for Menu
26087  * @cfg {String} html Text of the menu
26088  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26089  * @cfg {String} icon Font awesome icon
26090  * @cfg {String} pos Menu align to (top | bottom) default bottom
26091  * 
26092  * 
26093  * @constructor
26094  * Create a new Menu
26095  * @param {Object} config The config object
26096  */
26097
26098
26099 Roo.bootstrap.menu.Menu = function(config){
26100     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26101     
26102     this.addEvents({
26103         /**
26104          * @event beforeshow
26105          * Fires before this menu is displayed
26106          * @param {Roo.bootstrap.menu.Menu} this
26107          */
26108         beforeshow : true,
26109         /**
26110          * @event beforehide
26111          * Fires before this menu is hidden
26112          * @param {Roo.bootstrap.menu.Menu} this
26113          */
26114         beforehide : true,
26115         /**
26116          * @event show
26117          * Fires after this menu is displayed
26118          * @param {Roo.bootstrap.menu.Menu} this
26119          */
26120         show : true,
26121         /**
26122          * @event hide
26123          * Fires after this menu is hidden
26124          * @param {Roo.bootstrap.menu.Menu} this
26125          */
26126         hide : true,
26127         /**
26128          * @event click
26129          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26130          * @param {Roo.bootstrap.menu.Menu} this
26131          * @param {Roo.EventObject} e
26132          */
26133         click : true
26134     });
26135     
26136 };
26137
26138 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26139     
26140     submenu : false,
26141     html : '',
26142     weight : 'default',
26143     icon : false,
26144     pos : 'bottom',
26145     
26146     
26147     getChildContainer : function() {
26148         if(this.isSubMenu){
26149             return this.el;
26150         }
26151         
26152         return this.el.select('ul.dropdown-menu', true).first();  
26153     },
26154     
26155     getAutoCreate : function()
26156     {
26157         var text = [
26158             {
26159                 tag : 'span',
26160                 cls : 'roo-menu-text',
26161                 html : this.html
26162             }
26163         ];
26164         
26165         if(this.icon){
26166             text.unshift({
26167                 tag : 'i',
26168                 cls : 'fa ' + this.icon
26169             })
26170         }
26171         
26172         
26173         var cfg = {
26174             tag : 'div',
26175             cls : 'btn-group',
26176             cn : [
26177                 {
26178                     tag : 'button',
26179                     cls : 'dropdown-button btn btn-' + this.weight,
26180                     cn : text
26181                 },
26182                 {
26183                     tag : 'button',
26184                     cls : 'dropdown-toggle btn btn-' + this.weight,
26185                     cn : [
26186                         {
26187                             tag : 'span',
26188                             cls : 'caret'
26189                         }
26190                     ]
26191                 },
26192                 {
26193                     tag : 'ul',
26194                     cls : 'dropdown-menu'
26195                 }
26196             ]
26197             
26198         };
26199         
26200         if(this.pos == 'top'){
26201             cfg.cls += ' dropup';
26202         }
26203         
26204         if(this.isSubMenu){
26205             cfg = {
26206                 tag : 'ul',
26207                 cls : 'dropdown-menu'
26208             }
26209         }
26210         
26211         return cfg;
26212     },
26213     
26214     onRender : function(ct, position)
26215     {
26216         this.isSubMenu = ct.hasClass('dropdown-submenu');
26217         
26218         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26219     },
26220     
26221     initEvents : function() 
26222     {
26223         if(this.isSubMenu){
26224             return;
26225         }
26226         
26227         this.hidden = true;
26228         
26229         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26230         this.triggerEl.on('click', this.onTriggerPress, this);
26231         
26232         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26233         this.buttonEl.on('click', this.onClick, this);
26234         
26235     },
26236     
26237     list : function()
26238     {
26239         if(this.isSubMenu){
26240             return this.el;
26241         }
26242         
26243         return this.el.select('ul.dropdown-menu', true).first();
26244     },
26245     
26246     onClick : function(e)
26247     {
26248         this.fireEvent("click", this, e);
26249     },
26250     
26251     onTriggerPress  : function(e)
26252     {   
26253         if (this.isVisible()) {
26254             this.hide();
26255         } else {
26256             this.show();
26257         }
26258     },
26259     
26260     isVisible : function(){
26261         return !this.hidden;
26262     },
26263     
26264     show : function()
26265     {
26266         this.fireEvent("beforeshow", this);
26267         
26268         this.hidden = false;
26269         this.el.addClass('open');
26270         
26271         Roo.get(document).on("mouseup", this.onMouseUp, this);
26272         
26273         this.fireEvent("show", this);
26274         
26275         
26276     },
26277     
26278     hide : function()
26279     {
26280         this.fireEvent("beforehide", this);
26281         
26282         this.hidden = true;
26283         this.el.removeClass('open');
26284         
26285         Roo.get(document).un("mouseup", this.onMouseUp);
26286         
26287         this.fireEvent("hide", this);
26288     },
26289     
26290     onMouseUp : function()
26291     {
26292         this.hide();
26293     }
26294     
26295 });
26296
26297  
26298  /*
26299  * - LGPL
26300  *
26301  * menu item
26302  * 
26303  */
26304 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26305
26306 /**
26307  * @class Roo.bootstrap.menu.Item
26308  * @extends Roo.bootstrap.Component
26309  * Bootstrap MenuItem class
26310  * @cfg {Boolean} submenu (true | false) default false
26311  * @cfg {String} html text of the item
26312  * @cfg {String} href the link
26313  * @cfg {Boolean} disable (true | false) default false
26314  * @cfg {Boolean} preventDefault (true | false) default true
26315  * @cfg {String} icon Font awesome icon
26316  * @cfg {String} pos Submenu align to (left | right) default right 
26317  * 
26318  * 
26319  * @constructor
26320  * Create a new Item
26321  * @param {Object} config The config object
26322  */
26323
26324
26325 Roo.bootstrap.menu.Item = function(config){
26326     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26327     this.addEvents({
26328         /**
26329          * @event mouseover
26330          * Fires when the mouse is hovering over this menu
26331          * @param {Roo.bootstrap.menu.Item} this
26332          * @param {Roo.EventObject} e
26333          */
26334         mouseover : true,
26335         /**
26336          * @event mouseout
26337          * Fires when the mouse exits this menu
26338          * @param {Roo.bootstrap.menu.Item} this
26339          * @param {Roo.EventObject} e
26340          */
26341         mouseout : true,
26342         // raw events
26343         /**
26344          * @event click
26345          * The raw click event for the entire grid.
26346          * @param {Roo.EventObject} e
26347          */
26348         click : true
26349     });
26350 };
26351
26352 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26353     
26354     submenu : false,
26355     href : '',
26356     html : '',
26357     preventDefault: true,
26358     disable : false,
26359     icon : false,
26360     pos : 'right',
26361     
26362     getAutoCreate : function()
26363     {
26364         var text = [
26365             {
26366                 tag : 'span',
26367                 cls : 'roo-menu-item-text',
26368                 html : this.html
26369             }
26370         ];
26371         
26372         if(this.icon){
26373             text.unshift({
26374                 tag : 'i',
26375                 cls : 'fa ' + this.icon
26376             })
26377         }
26378         
26379         var cfg = {
26380             tag : 'li',
26381             cn : [
26382                 {
26383                     tag : 'a',
26384                     href : this.href || '#',
26385                     cn : text
26386                 }
26387             ]
26388         };
26389         
26390         if(this.disable){
26391             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26392         }
26393         
26394         if(this.submenu){
26395             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26396             
26397             if(this.pos == 'left'){
26398                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26399             }
26400         }
26401         
26402         return cfg;
26403     },
26404     
26405     initEvents : function() 
26406     {
26407         this.el.on('mouseover', this.onMouseOver, this);
26408         this.el.on('mouseout', this.onMouseOut, this);
26409         
26410         this.el.select('a', true).first().on('click', this.onClick, this);
26411         
26412     },
26413     
26414     onClick : function(e)
26415     {
26416         if(this.preventDefault){
26417             e.preventDefault();
26418         }
26419         
26420         this.fireEvent("click", this, e);
26421     },
26422     
26423     onMouseOver : function(e)
26424     {
26425         if(this.submenu && this.pos == 'left'){
26426             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26427         }
26428         
26429         this.fireEvent("mouseover", this, e);
26430     },
26431     
26432     onMouseOut : function(e)
26433     {
26434         this.fireEvent("mouseout", this, e);
26435     }
26436 });
26437
26438  
26439
26440  /*
26441  * - LGPL
26442  *
26443  * menu separator
26444  * 
26445  */
26446 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26447
26448 /**
26449  * @class Roo.bootstrap.menu.Separator
26450  * @extends Roo.bootstrap.Component
26451  * Bootstrap Separator class
26452  * 
26453  * @constructor
26454  * Create a new Separator
26455  * @param {Object} config The config object
26456  */
26457
26458
26459 Roo.bootstrap.menu.Separator = function(config){
26460     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26461 };
26462
26463 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26464     
26465     getAutoCreate : function(){
26466         var cfg = {
26467             tag : 'li',
26468             cls: 'divider'
26469         };
26470         
26471         return cfg;
26472     }
26473    
26474 });
26475
26476  
26477
26478  /*
26479  * - LGPL
26480  *
26481  * Tooltip
26482  * 
26483  */
26484
26485 /**
26486  * @class Roo.bootstrap.Tooltip
26487  * Bootstrap Tooltip class
26488  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26489  * to determine which dom element triggers the tooltip.
26490  * 
26491  * It needs to add support for additional attributes like tooltip-position
26492  * 
26493  * @constructor
26494  * Create a new Toolti
26495  * @param {Object} config The config object
26496  */
26497
26498 Roo.bootstrap.Tooltip = function(config){
26499     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26500     
26501     this.alignment = Roo.bootstrap.Tooltip.alignment;
26502     
26503     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26504         this.alignment = config.alignment;
26505     }
26506     
26507 };
26508
26509 Roo.apply(Roo.bootstrap.Tooltip, {
26510     /**
26511      * @function init initialize tooltip monitoring.
26512      * @static
26513      */
26514     currentEl : false,
26515     currentTip : false,
26516     currentRegion : false,
26517     
26518     //  init : delay?
26519     
26520     init : function()
26521     {
26522         Roo.get(document).on('mouseover', this.enter ,this);
26523         Roo.get(document).on('mouseout', this.leave, this);
26524          
26525         
26526         this.currentTip = new Roo.bootstrap.Tooltip();
26527     },
26528     
26529     enter : function(ev)
26530     {
26531         var dom = ev.getTarget();
26532         
26533         //Roo.log(['enter',dom]);
26534         var el = Roo.fly(dom);
26535         if (this.currentEl) {
26536             //Roo.log(dom);
26537             //Roo.log(this.currentEl);
26538             //Roo.log(this.currentEl.contains(dom));
26539             if (this.currentEl == el) {
26540                 return;
26541             }
26542             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26543                 return;
26544             }
26545
26546         }
26547         
26548         if (this.currentTip.el) {
26549             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26550         }    
26551         //Roo.log(ev);
26552         
26553         if(!el || el.dom == document){
26554             return;
26555         }
26556         
26557         var bindEl = el;
26558         
26559         // you can not look for children, as if el is the body.. then everythign is the child..
26560         if (!el.attr('tooltip')) { //
26561             if (!el.select("[tooltip]").elements.length) {
26562                 return;
26563             }
26564             // is the mouse over this child...?
26565             bindEl = el.select("[tooltip]").first();
26566             var xy = ev.getXY();
26567             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26568                 //Roo.log("not in region.");
26569                 return;
26570             }
26571             //Roo.log("child element over..");
26572             
26573         }
26574         this.currentEl = bindEl;
26575         this.currentTip.bind(bindEl);
26576         this.currentRegion = Roo.lib.Region.getRegion(dom);
26577         this.currentTip.enter();
26578         
26579     },
26580     leave : function(ev)
26581     {
26582         var dom = ev.getTarget();
26583         //Roo.log(['leave',dom]);
26584         if (!this.currentEl) {
26585             return;
26586         }
26587         
26588         
26589         if (dom != this.currentEl.dom) {
26590             return;
26591         }
26592         var xy = ev.getXY();
26593         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26594             return;
26595         }
26596         // only activate leave if mouse cursor is outside... bounding box..
26597         
26598         
26599         
26600         
26601         if (this.currentTip) {
26602             this.currentTip.leave();
26603         }
26604         //Roo.log('clear currentEl');
26605         this.currentEl = false;
26606         
26607         
26608     },
26609     alignment : {
26610         'left' : ['r-l', [-2,0], 'right'],
26611         'right' : ['l-r', [2,0], 'left'],
26612         'bottom' : ['t-b', [0,2], 'top'],
26613         'top' : [ 'b-t', [0,-2], 'bottom']
26614     }
26615     
26616 });
26617
26618
26619 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26620     
26621     
26622     bindEl : false,
26623     
26624     delay : null, // can be { show : 300 , hide: 500}
26625     
26626     timeout : null,
26627     
26628     hoverState : null, //???
26629     
26630     placement : 'bottom', 
26631     
26632     alignment : false,
26633     
26634     getAutoCreate : function(){
26635     
26636         var cfg = {
26637            cls : 'tooltip',
26638            role : 'tooltip',
26639            cn : [
26640                 {
26641                     cls : 'tooltip-arrow'
26642                 },
26643                 {
26644                     cls : 'tooltip-inner'
26645                 }
26646            ]
26647         };
26648         
26649         return cfg;
26650     },
26651     bind : function(el)
26652     {
26653         this.bindEl = el;
26654     },
26655       
26656     
26657     enter : function () {
26658        
26659         if (this.timeout != null) {
26660             clearTimeout(this.timeout);
26661         }
26662         
26663         this.hoverState = 'in';
26664          //Roo.log("enter - show");
26665         if (!this.delay || !this.delay.show) {
26666             this.show();
26667             return;
26668         }
26669         var _t = this;
26670         this.timeout = setTimeout(function () {
26671             if (_t.hoverState == 'in') {
26672                 _t.show();
26673             }
26674         }, this.delay.show);
26675     },
26676     leave : function()
26677     {
26678         clearTimeout(this.timeout);
26679     
26680         this.hoverState = 'out';
26681          if (!this.delay || !this.delay.hide) {
26682             this.hide();
26683             return;
26684         }
26685        
26686         var _t = this;
26687         this.timeout = setTimeout(function () {
26688             //Roo.log("leave - timeout");
26689             
26690             if (_t.hoverState == 'out') {
26691                 _t.hide();
26692                 Roo.bootstrap.Tooltip.currentEl = false;
26693             }
26694         }, delay);
26695     },
26696     
26697     show : function (msg)
26698     {
26699         if (!this.el) {
26700             this.render(document.body);
26701         }
26702         // set content.
26703         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26704         
26705         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26706         
26707         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26708         
26709         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26710         
26711         var placement = typeof this.placement == 'function' ?
26712             this.placement.call(this, this.el, on_el) :
26713             this.placement;
26714             
26715         var autoToken = /\s?auto?\s?/i;
26716         var autoPlace = autoToken.test(placement);
26717         if (autoPlace) {
26718             placement = placement.replace(autoToken, '') || 'top';
26719         }
26720         
26721         //this.el.detach()
26722         //this.el.setXY([0,0]);
26723         this.el.show();
26724         //this.el.dom.style.display='block';
26725         
26726         //this.el.appendTo(on_el);
26727         
26728         var p = this.getPosition();
26729         var box = this.el.getBox();
26730         
26731         if (autoPlace) {
26732             // fixme..
26733         }
26734         
26735         var align = this.alignment[placement];
26736         
26737         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26738         
26739         if(placement == 'top' || placement == 'bottom'){
26740             if(xy[0] < 0){
26741                 placement = 'right';
26742             }
26743             
26744             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26745                 placement = 'left';
26746             }
26747             
26748             var scroll = Roo.select('body', true).first().getScroll();
26749             
26750             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26751                 placement = 'top';
26752             }
26753             
26754             align = this.alignment[placement];
26755         }
26756         
26757         this.el.alignTo(this.bindEl, align[0],align[1]);
26758         //var arrow = this.el.select('.arrow',true).first();
26759         //arrow.set(align[2], 
26760         
26761         this.el.addClass(placement);
26762         
26763         this.el.addClass('in fade');
26764         
26765         this.hoverState = null;
26766         
26767         if (this.el.hasClass('fade')) {
26768             // fade it?
26769         }
26770         
26771     },
26772     hide : function()
26773     {
26774          
26775         if (!this.el) {
26776             return;
26777         }
26778         //this.el.setXY([0,0]);
26779         this.el.removeClass('in');
26780         //this.el.hide();
26781         
26782     }
26783     
26784 });
26785  
26786
26787  /*
26788  * - LGPL
26789  *
26790  * Location Picker
26791  * 
26792  */
26793
26794 /**
26795  * @class Roo.bootstrap.LocationPicker
26796  * @extends Roo.bootstrap.Component
26797  * Bootstrap LocationPicker class
26798  * @cfg {Number} latitude Position when init default 0
26799  * @cfg {Number} longitude Position when init default 0
26800  * @cfg {Number} zoom default 15
26801  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26802  * @cfg {Boolean} mapTypeControl default false
26803  * @cfg {Boolean} disableDoubleClickZoom default false
26804  * @cfg {Boolean} scrollwheel default true
26805  * @cfg {Boolean} streetViewControl default false
26806  * @cfg {Number} radius default 0
26807  * @cfg {String} locationName
26808  * @cfg {Boolean} draggable default true
26809  * @cfg {Boolean} enableAutocomplete default false
26810  * @cfg {Boolean} enableReverseGeocode default true
26811  * @cfg {String} markerTitle
26812  * 
26813  * @constructor
26814  * Create a new LocationPicker
26815  * @param {Object} config The config object
26816  */
26817
26818
26819 Roo.bootstrap.LocationPicker = function(config){
26820     
26821     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26822     
26823     this.addEvents({
26824         /**
26825          * @event initial
26826          * Fires when the picker initialized.
26827          * @param {Roo.bootstrap.LocationPicker} this
26828          * @param {Google Location} location
26829          */
26830         initial : true,
26831         /**
26832          * @event positionchanged
26833          * Fires when the picker position changed.
26834          * @param {Roo.bootstrap.LocationPicker} this
26835          * @param {Google Location} location
26836          */
26837         positionchanged : true,
26838         /**
26839          * @event resize
26840          * Fires when the map resize.
26841          * @param {Roo.bootstrap.LocationPicker} this
26842          */
26843         resize : true,
26844         /**
26845          * @event show
26846          * Fires when the map show.
26847          * @param {Roo.bootstrap.LocationPicker} this
26848          */
26849         show : true,
26850         /**
26851          * @event hide
26852          * Fires when the map hide.
26853          * @param {Roo.bootstrap.LocationPicker} this
26854          */
26855         hide : true,
26856         /**
26857          * @event mapClick
26858          * Fires when click the map.
26859          * @param {Roo.bootstrap.LocationPicker} this
26860          * @param {Map event} e
26861          */
26862         mapClick : true,
26863         /**
26864          * @event mapRightClick
26865          * Fires when right click the map.
26866          * @param {Roo.bootstrap.LocationPicker} this
26867          * @param {Map event} e
26868          */
26869         mapRightClick : true,
26870         /**
26871          * @event markerClick
26872          * Fires when click the marker.
26873          * @param {Roo.bootstrap.LocationPicker} this
26874          * @param {Map event} e
26875          */
26876         markerClick : true,
26877         /**
26878          * @event markerRightClick
26879          * Fires when right click the marker.
26880          * @param {Roo.bootstrap.LocationPicker} this
26881          * @param {Map event} e
26882          */
26883         markerRightClick : true,
26884         /**
26885          * @event OverlayViewDraw
26886          * Fires when OverlayView Draw
26887          * @param {Roo.bootstrap.LocationPicker} this
26888          */
26889         OverlayViewDraw : true,
26890         /**
26891          * @event OverlayViewOnAdd
26892          * Fires when OverlayView Draw
26893          * @param {Roo.bootstrap.LocationPicker} this
26894          */
26895         OverlayViewOnAdd : true,
26896         /**
26897          * @event OverlayViewOnRemove
26898          * Fires when OverlayView Draw
26899          * @param {Roo.bootstrap.LocationPicker} this
26900          */
26901         OverlayViewOnRemove : true,
26902         /**
26903          * @event OverlayViewShow
26904          * Fires when OverlayView Draw
26905          * @param {Roo.bootstrap.LocationPicker} this
26906          * @param {Pixel} cpx
26907          */
26908         OverlayViewShow : true,
26909         /**
26910          * @event OverlayViewHide
26911          * Fires when OverlayView Draw
26912          * @param {Roo.bootstrap.LocationPicker} this
26913          */
26914         OverlayViewHide : true,
26915         /**
26916          * @event loadexception
26917          * Fires when load google lib failed.
26918          * @param {Roo.bootstrap.LocationPicker} this
26919          */
26920         loadexception : true
26921     });
26922         
26923 };
26924
26925 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26926     
26927     gMapContext: false,
26928     
26929     latitude: 0,
26930     longitude: 0,
26931     zoom: 15,
26932     mapTypeId: false,
26933     mapTypeControl: false,
26934     disableDoubleClickZoom: false,
26935     scrollwheel: true,
26936     streetViewControl: false,
26937     radius: 0,
26938     locationName: '',
26939     draggable: true,
26940     enableAutocomplete: false,
26941     enableReverseGeocode: true,
26942     markerTitle: '',
26943     
26944     getAutoCreate: function()
26945     {
26946
26947         var cfg = {
26948             tag: 'div',
26949             cls: 'roo-location-picker'
26950         };
26951         
26952         return cfg
26953     },
26954     
26955     initEvents: function(ct, position)
26956     {       
26957         if(!this.el.getWidth() || this.isApplied()){
26958             return;
26959         }
26960         
26961         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26962         
26963         this.initial();
26964     },
26965     
26966     initial: function()
26967     {
26968         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26969             this.fireEvent('loadexception', this);
26970             return;
26971         }
26972         
26973         if(!this.mapTypeId){
26974             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26975         }
26976         
26977         this.gMapContext = this.GMapContext();
26978         
26979         this.initOverlayView();
26980         
26981         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26982         
26983         var _this = this;
26984                 
26985         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26986             _this.setPosition(_this.gMapContext.marker.position);
26987         });
26988         
26989         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26990             _this.fireEvent('mapClick', this, event);
26991             
26992         });
26993
26994         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26995             _this.fireEvent('mapRightClick', this, event);
26996             
26997         });
26998         
26999         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27000             _this.fireEvent('markerClick', this, event);
27001             
27002         });
27003
27004         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27005             _this.fireEvent('markerRightClick', this, event);
27006             
27007         });
27008         
27009         this.setPosition(this.gMapContext.location);
27010         
27011         this.fireEvent('initial', this, this.gMapContext.location);
27012     },
27013     
27014     initOverlayView: function()
27015     {
27016         var _this = this;
27017         
27018         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27019             
27020             draw: function()
27021             {
27022                 _this.fireEvent('OverlayViewDraw', _this);
27023             },
27024             
27025             onAdd: function()
27026             {
27027                 _this.fireEvent('OverlayViewOnAdd', _this);
27028             },
27029             
27030             onRemove: function()
27031             {
27032                 _this.fireEvent('OverlayViewOnRemove', _this);
27033             },
27034             
27035             show: function(cpx)
27036             {
27037                 _this.fireEvent('OverlayViewShow', _this, cpx);
27038             },
27039             
27040             hide: function()
27041             {
27042                 _this.fireEvent('OverlayViewHide', _this);
27043             }
27044             
27045         });
27046     },
27047     
27048     fromLatLngToContainerPixel: function(event)
27049     {
27050         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27051     },
27052     
27053     isApplied: function() 
27054     {
27055         return this.getGmapContext() == false ? false : true;
27056     },
27057     
27058     getGmapContext: function() 
27059     {
27060         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27061     },
27062     
27063     GMapContext: function() 
27064     {
27065         var position = new google.maps.LatLng(this.latitude, this.longitude);
27066         
27067         var _map = new google.maps.Map(this.el.dom, {
27068             center: position,
27069             zoom: this.zoom,
27070             mapTypeId: this.mapTypeId,
27071             mapTypeControl: this.mapTypeControl,
27072             disableDoubleClickZoom: this.disableDoubleClickZoom,
27073             scrollwheel: this.scrollwheel,
27074             streetViewControl: this.streetViewControl,
27075             locationName: this.locationName,
27076             draggable: this.draggable,
27077             enableAutocomplete: this.enableAutocomplete,
27078             enableReverseGeocode: this.enableReverseGeocode
27079         });
27080         
27081         var _marker = new google.maps.Marker({
27082             position: position,
27083             map: _map,
27084             title: this.markerTitle,
27085             draggable: this.draggable
27086         });
27087         
27088         return {
27089             map: _map,
27090             marker: _marker,
27091             circle: null,
27092             location: position,
27093             radius: this.radius,
27094             locationName: this.locationName,
27095             addressComponents: {
27096                 formatted_address: null,
27097                 addressLine1: null,
27098                 addressLine2: null,
27099                 streetName: null,
27100                 streetNumber: null,
27101                 city: null,
27102                 district: null,
27103                 state: null,
27104                 stateOrProvince: null
27105             },
27106             settings: this,
27107             domContainer: this.el.dom,
27108             geodecoder: new google.maps.Geocoder()
27109         };
27110     },
27111     
27112     drawCircle: function(center, radius, options) 
27113     {
27114         if (this.gMapContext.circle != null) {
27115             this.gMapContext.circle.setMap(null);
27116         }
27117         if (radius > 0) {
27118             radius *= 1;
27119             options = Roo.apply({}, options, {
27120                 strokeColor: "#0000FF",
27121                 strokeOpacity: .35,
27122                 strokeWeight: 2,
27123                 fillColor: "#0000FF",
27124                 fillOpacity: .2
27125             });
27126             
27127             options.map = this.gMapContext.map;
27128             options.radius = radius;
27129             options.center = center;
27130             this.gMapContext.circle = new google.maps.Circle(options);
27131             return this.gMapContext.circle;
27132         }
27133         
27134         return null;
27135     },
27136     
27137     setPosition: function(location) 
27138     {
27139         this.gMapContext.location = location;
27140         this.gMapContext.marker.setPosition(location);
27141         this.gMapContext.map.panTo(location);
27142         this.drawCircle(location, this.gMapContext.radius, {});
27143         
27144         var _this = this;
27145         
27146         if (this.gMapContext.settings.enableReverseGeocode) {
27147             this.gMapContext.geodecoder.geocode({
27148                 latLng: this.gMapContext.location
27149             }, function(results, status) {
27150                 
27151                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27152                     _this.gMapContext.locationName = results[0].formatted_address;
27153                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27154                     
27155                     _this.fireEvent('positionchanged', this, location);
27156                 }
27157             });
27158             
27159             return;
27160         }
27161         
27162         this.fireEvent('positionchanged', this, location);
27163     },
27164     
27165     resize: function()
27166     {
27167         google.maps.event.trigger(this.gMapContext.map, "resize");
27168         
27169         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27170         
27171         this.fireEvent('resize', this);
27172     },
27173     
27174     setPositionByLatLng: function(latitude, longitude)
27175     {
27176         this.setPosition(new google.maps.LatLng(latitude, longitude));
27177     },
27178     
27179     getCurrentPosition: function() 
27180     {
27181         return {
27182             latitude: this.gMapContext.location.lat(),
27183             longitude: this.gMapContext.location.lng()
27184         };
27185     },
27186     
27187     getAddressName: function() 
27188     {
27189         return this.gMapContext.locationName;
27190     },
27191     
27192     getAddressComponents: function() 
27193     {
27194         return this.gMapContext.addressComponents;
27195     },
27196     
27197     address_component_from_google_geocode: function(address_components) 
27198     {
27199         var result = {};
27200         
27201         for (var i = 0; i < address_components.length; i++) {
27202             var component = address_components[i];
27203             if (component.types.indexOf("postal_code") >= 0) {
27204                 result.postalCode = component.short_name;
27205             } else if (component.types.indexOf("street_number") >= 0) {
27206                 result.streetNumber = component.short_name;
27207             } else if (component.types.indexOf("route") >= 0) {
27208                 result.streetName = component.short_name;
27209             } else if (component.types.indexOf("neighborhood") >= 0) {
27210                 result.city = component.short_name;
27211             } else if (component.types.indexOf("locality") >= 0) {
27212                 result.city = component.short_name;
27213             } else if (component.types.indexOf("sublocality") >= 0) {
27214                 result.district = component.short_name;
27215             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27216                 result.stateOrProvince = component.short_name;
27217             } else if (component.types.indexOf("country") >= 0) {
27218                 result.country = component.short_name;
27219             }
27220         }
27221         
27222         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27223         result.addressLine2 = "";
27224         return result;
27225     },
27226     
27227     setZoomLevel: function(zoom)
27228     {
27229         this.gMapContext.map.setZoom(zoom);
27230     },
27231     
27232     show: function()
27233     {
27234         if(!this.el){
27235             return;
27236         }
27237         
27238         this.el.show();
27239         
27240         this.resize();
27241         
27242         this.fireEvent('show', this);
27243     },
27244     
27245     hide: function()
27246     {
27247         if(!this.el){
27248             return;
27249         }
27250         
27251         this.el.hide();
27252         
27253         this.fireEvent('hide', this);
27254     }
27255     
27256 });
27257
27258 Roo.apply(Roo.bootstrap.LocationPicker, {
27259     
27260     OverlayView : function(map, options)
27261     {
27262         options = options || {};
27263         
27264         this.setMap(map);
27265     }
27266     
27267     
27268 });/**
27269  * @class Roo.bootstrap.Alert
27270  * @extends Roo.bootstrap.Component
27271  * Bootstrap Alert class - shows an alert area box
27272  * eg
27273  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27274   Enter a valid email address
27275 </div>
27276  * @licence LGPL
27277  * @cfg {String} title The title of alert
27278  * @cfg {String} html The content of alert
27279  * @cfg {String} weight (  success | info | warning | danger )
27280  * @cfg {String} faicon font-awesomeicon
27281  * 
27282  * @constructor
27283  * Create a new alert
27284  * @param {Object} config The config object
27285  */
27286
27287
27288 Roo.bootstrap.Alert = function(config){
27289     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27290     
27291 };
27292
27293 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27294     
27295     title: '',
27296     html: '',
27297     weight: false,
27298     faicon: false,
27299     
27300     getAutoCreate : function()
27301     {
27302         
27303         var cfg = {
27304             tag : 'div',
27305             cls : 'alert',
27306             cn : [
27307                 {
27308                     tag : 'i',
27309                     cls : 'roo-alert-icon'
27310                     
27311                 },
27312                 {
27313                     tag : 'b',
27314                     cls : 'roo-alert-title',
27315                     html : this.title
27316                 },
27317                 {
27318                     tag : 'span',
27319                     cls : 'roo-alert-text',
27320                     html : this.html
27321                 }
27322             ]
27323         };
27324         
27325         if(this.faicon){
27326             cfg.cn[0].cls += ' fa ' + this.faicon;
27327         }
27328         
27329         if(this.weight){
27330             cfg.cls += ' alert-' + this.weight;
27331         }
27332         
27333         return cfg;
27334     },
27335     
27336     initEvents: function() 
27337     {
27338         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27339     },
27340     
27341     setTitle : function(str)
27342     {
27343         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27344     },
27345     
27346     setText : function(str)
27347     {
27348         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27349     },
27350     
27351     setWeight : function(weight)
27352     {
27353         if(this.weight){
27354             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27355         }
27356         
27357         this.weight = weight;
27358         
27359         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27360     },
27361     
27362     setIcon : function(icon)
27363     {
27364         if(this.faicon){
27365             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27366         }
27367         
27368         this.faicon = icon;
27369         
27370         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27371     },
27372     
27373     hide: function() 
27374     {
27375         this.el.hide();   
27376     },
27377     
27378     show: function() 
27379     {  
27380         this.el.show();   
27381     }
27382     
27383 });
27384
27385  
27386 /*
27387 * Licence: LGPL
27388 */
27389
27390 /**
27391  * @class Roo.bootstrap.UploadCropbox
27392  * @extends Roo.bootstrap.Component
27393  * Bootstrap UploadCropbox class
27394  * @cfg {String} emptyText show when image has been loaded
27395  * @cfg {String} rotateNotify show when image too small to rotate
27396  * @cfg {Number} errorTimeout default 3000
27397  * @cfg {Number} minWidth default 300
27398  * @cfg {Number} minHeight default 300
27399  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27400  * @cfg {Boolean} isDocument (true|false) default false
27401  * @cfg {String} url action url
27402  * @cfg {String} paramName default 'imageUpload'
27403  * @cfg {String} method default POST
27404  * @cfg {Boolean} loadMask (true|false) default true
27405  * @cfg {Boolean} loadingText default 'Loading...'
27406  * 
27407  * @constructor
27408  * Create a new UploadCropbox
27409  * @param {Object} config The config object
27410  */
27411
27412 Roo.bootstrap.UploadCropbox = function(config){
27413     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27414     
27415     this.addEvents({
27416         /**
27417          * @event beforeselectfile
27418          * Fire before select file
27419          * @param {Roo.bootstrap.UploadCropbox} this
27420          */
27421         "beforeselectfile" : true,
27422         /**
27423          * @event initial
27424          * Fire after initEvent
27425          * @param {Roo.bootstrap.UploadCropbox} this
27426          */
27427         "initial" : true,
27428         /**
27429          * @event crop
27430          * Fire after initEvent
27431          * @param {Roo.bootstrap.UploadCropbox} this
27432          * @param {String} data
27433          */
27434         "crop" : true,
27435         /**
27436          * @event prepare
27437          * Fire when preparing the file data
27438          * @param {Roo.bootstrap.UploadCropbox} this
27439          * @param {Object} file
27440          */
27441         "prepare" : true,
27442         /**
27443          * @event exception
27444          * Fire when get exception
27445          * @param {Roo.bootstrap.UploadCropbox} this
27446          * @param {XMLHttpRequest} xhr
27447          */
27448         "exception" : true,
27449         /**
27450          * @event beforeloadcanvas
27451          * Fire before load the canvas
27452          * @param {Roo.bootstrap.UploadCropbox} this
27453          * @param {String} src
27454          */
27455         "beforeloadcanvas" : true,
27456         /**
27457          * @event trash
27458          * Fire when trash image
27459          * @param {Roo.bootstrap.UploadCropbox} this
27460          */
27461         "trash" : true,
27462         /**
27463          * @event download
27464          * Fire when download the image
27465          * @param {Roo.bootstrap.UploadCropbox} this
27466          */
27467         "download" : true,
27468         /**
27469          * @event footerbuttonclick
27470          * Fire when footerbuttonclick
27471          * @param {Roo.bootstrap.UploadCropbox} this
27472          * @param {String} type
27473          */
27474         "footerbuttonclick" : true,
27475         /**
27476          * @event resize
27477          * Fire when resize
27478          * @param {Roo.bootstrap.UploadCropbox} this
27479          */
27480         "resize" : true,
27481         /**
27482          * @event rotate
27483          * Fire when rotate the image
27484          * @param {Roo.bootstrap.UploadCropbox} this
27485          * @param {String} pos
27486          */
27487         "rotate" : true,
27488         /**
27489          * @event inspect
27490          * Fire when inspect the file
27491          * @param {Roo.bootstrap.UploadCropbox} this
27492          * @param {Object} file
27493          */
27494         "inspect" : true,
27495         /**
27496          * @event upload
27497          * Fire when xhr upload the file
27498          * @param {Roo.bootstrap.UploadCropbox} this
27499          * @param {Object} data
27500          */
27501         "upload" : true,
27502         /**
27503          * @event arrange
27504          * Fire when arrange the file data
27505          * @param {Roo.bootstrap.UploadCropbox} this
27506          * @param {Object} formData
27507          */
27508         "arrange" : true
27509     });
27510     
27511     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27512 };
27513
27514 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27515     
27516     emptyText : 'Click to upload image',
27517     rotateNotify : 'Image is too small to rotate',
27518     errorTimeout : 3000,
27519     scale : 0,
27520     baseScale : 1,
27521     rotate : 0,
27522     dragable : false,
27523     pinching : false,
27524     mouseX : 0,
27525     mouseY : 0,
27526     cropData : false,
27527     minWidth : 300,
27528     minHeight : 300,
27529     file : false,
27530     exif : {},
27531     baseRotate : 1,
27532     cropType : 'image/jpeg',
27533     buttons : false,
27534     canvasLoaded : false,
27535     isDocument : false,
27536     method : 'POST',
27537     paramName : 'imageUpload',
27538     loadMask : true,
27539     loadingText : 'Loading...',
27540     maskEl : false,
27541     
27542     getAutoCreate : function()
27543     {
27544         var cfg = {
27545             tag : 'div',
27546             cls : 'roo-upload-cropbox',
27547             cn : [
27548                 {
27549                     tag : 'input',
27550                     cls : 'roo-upload-cropbox-selector',
27551                     type : 'file'
27552                 },
27553                 {
27554                     tag : 'div',
27555                     cls : 'roo-upload-cropbox-body',
27556                     style : 'cursor:pointer',
27557                     cn : [
27558                         {
27559                             tag : 'div',
27560                             cls : 'roo-upload-cropbox-preview'
27561                         },
27562                         {
27563                             tag : 'div',
27564                             cls : 'roo-upload-cropbox-thumb'
27565                         },
27566                         {
27567                             tag : 'div',
27568                             cls : 'roo-upload-cropbox-empty-notify',
27569                             html : this.emptyText
27570                         },
27571                         {
27572                             tag : 'div',
27573                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27574                             html : this.rotateNotify
27575                         }
27576                     ]
27577                 },
27578                 {
27579                     tag : 'div',
27580                     cls : 'roo-upload-cropbox-footer',
27581                     cn : {
27582                         tag : 'div',
27583                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27584                         cn : []
27585                     }
27586                 }
27587             ]
27588         };
27589         
27590         return cfg;
27591     },
27592     
27593     onRender : function(ct, position)
27594     {
27595         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27596         
27597         if (this.buttons.length) {
27598             
27599             Roo.each(this.buttons, function(bb) {
27600                 
27601                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27602                 
27603                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27604                 
27605             }, this);
27606         }
27607         
27608         if(this.loadMask){
27609             this.maskEl = this.el;
27610         }
27611     },
27612     
27613     initEvents : function()
27614     {
27615         this.urlAPI = (window.createObjectURL && window) || 
27616                                 (window.URL && URL.revokeObjectURL && URL) || 
27617                                 (window.webkitURL && webkitURL);
27618                         
27619         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27620         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27621         
27622         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27623         this.selectorEl.hide();
27624         
27625         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27626         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27627         
27628         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27629         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27630         this.thumbEl.hide();
27631         
27632         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27633         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27634         
27635         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27636         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27637         this.errorEl.hide();
27638         
27639         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27640         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27641         this.footerEl.hide();
27642         
27643         this.setThumbBoxSize();
27644         
27645         this.bind();
27646         
27647         this.resize();
27648         
27649         this.fireEvent('initial', this);
27650     },
27651
27652     bind : function()
27653     {
27654         var _this = this;
27655         
27656         window.addEventListener("resize", function() { _this.resize(); } );
27657         
27658         this.bodyEl.on('click', this.beforeSelectFile, this);
27659         
27660         if(Roo.isTouch){
27661             this.bodyEl.on('touchstart', this.onTouchStart, this);
27662             this.bodyEl.on('touchmove', this.onTouchMove, this);
27663             this.bodyEl.on('touchend', this.onTouchEnd, this);
27664         }
27665         
27666         if(!Roo.isTouch){
27667             this.bodyEl.on('mousedown', this.onMouseDown, this);
27668             this.bodyEl.on('mousemove', this.onMouseMove, this);
27669             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27670             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27671             Roo.get(document).on('mouseup', this.onMouseUp, this);
27672         }
27673         
27674         this.selectorEl.on('change', this.onFileSelected, this);
27675     },
27676     
27677     reset : function()
27678     {    
27679         this.scale = 0;
27680         this.baseScale = 1;
27681         this.rotate = 0;
27682         this.baseRotate = 1;
27683         this.dragable = false;
27684         this.pinching = false;
27685         this.mouseX = 0;
27686         this.mouseY = 0;
27687         this.cropData = false;
27688         this.notifyEl.dom.innerHTML = this.emptyText;
27689         
27690         this.selectorEl.dom.value = '';
27691         
27692     },
27693     
27694     resize : function()
27695     {
27696         if(this.fireEvent('resize', this) != false){
27697             this.setThumbBoxPosition();
27698             this.setCanvasPosition();
27699         }
27700     },
27701     
27702     onFooterButtonClick : function(e, el, o, type)
27703     {
27704         switch (type) {
27705             case 'rotate-left' :
27706                 this.onRotateLeft(e);
27707                 break;
27708             case 'rotate-right' :
27709                 this.onRotateRight(e);
27710                 break;
27711             case 'picture' :
27712                 this.beforeSelectFile(e);
27713                 break;
27714             case 'trash' :
27715                 this.trash(e);
27716                 break;
27717             case 'crop' :
27718                 this.crop(e);
27719                 break;
27720             case 'download' :
27721                 this.download(e);
27722                 break;
27723             default :
27724                 break;
27725         }
27726         
27727         this.fireEvent('footerbuttonclick', this, type);
27728     },
27729     
27730     beforeSelectFile : function(e)
27731     {
27732         e.preventDefault();
27733         
27734         if(this.fireEvent('beforeselectfile', this) != false){
27735             this.selectorEl.dom.click();
27736         }
27737     },
27738     
27739     onFileSelected : function(e)
27740     {
27741         e.preventDefault();
27742         
27743         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27744             return;
27745         }
27746         
27747         var file = this.selectorEl.dom.files[0];
27748         
27749         if(this.fireEvent('inspect', this, file) != false){
27750             this.prepare(file);
27751         }
27752         
27753     },
27754     
27755     trash : function(e)
27756     {
27757         this.fireEvent('trash', this);
27758     },
27759     
27760     download : function(e)
27761     {
27762         this.fireEvent('download', this);
27763     },
27764     
27765     loadCanvas : function(src)
27766     {   
27767         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27768             
27769             this.reset();
27770             
27771             this.imageEl = document.createElement('img');
27772             
27773             var _this = this;
27774             
27775             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27776             
27777             this.imageEl.src = src;
27778         }
27779     },
27780     
27781     onLoadCanvas : function()
27782     {   
27783         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27784         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27785         
27786         this.bodyEl.un('click', this.beforeSelectFile, this);
27787         
27788         this.notifyEl.hide();
27789         this.thumbEl.show();
27790         this.footerEl.show();
27791         
27792         this.baseRotateLevel();
27793         
27794         if(this.isDocument){
27795             this.setThumbBoxSize();
27796         }
27797         
27798         this.setThumbBoxPosition();
27799         
27800         this.baseScaleLevel();
27801         
27802         this.draw();
27803         
27804         this.resize();
27805         
27806         this.canvasLoaded = true;
27807         
27808         if(this.loadMask){
27809             this.maskEl.unmask();
27810         }
27811         
27812     },
27813     
27814     setCanvasPosition : function()
27815     {   
27816         if(!this.canvasEl){
27817             return;
27818         }
27819         
27820         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27821         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27822         
27823         this.previewEl.setLeft(pw);
27824         this.previewEl.setTop(ph);
27825         
27826     },
27827     
27828     onMouseDown : function(e)
27829     {   
27830         e.stopEvent();
27831         
27832         this.dragable = true;
27833         this.pinching = false;
27834         
27835         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27836             this.dragable = false;
27837             return;
27838         }
27839         
27840         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27841         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27842         
27843     },
27844     
27845     onMouseMove : function(e)
27846     {   
27847         e.stopEvent();
27848         
27849         if(!this.canvasLoaded){
27850             return;
27851         }
27852         
27853         if (!this.dragable){
27854             return;
27855         }
27856         
27857         var minX = Math.ceil(this.thumbEl.getLeft(true));
27858         var minY = Math.ceil(this.thumbEl.getTop(true));
27859         
27860         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27861         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27862         
27863         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27864         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27865         
27866         x = x - this.mouseX;
27867         y = y - this.mouseY;
27868         
27869         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27870         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27871         
27872         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27873         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27874         
27875         this.previewEl.setLeft(bgX);
27876         this.previewEl.setTop(bgY);
27877         
27878         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27879         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27880     },
27881     
27882     onMouseUp : function(e)
27883     {   
27884         e.stopEvent();
27885         
27886         this.dragable = false;
27887     },
27888     
27889     onMouseWheel : function(e)
27890     {   
27891         e.stopEvent();
27892         
27893         this.startScale = this.scale;
27894         
27895         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27896         
27897         if(!this.zoomable()){
27898             this.scale = this.startScale;
27899             return;
27900         }
27901         
27902         this.draw();
27903         
27904         return;
27905     },
27906     
27907     zoomable : function()
27908     {
27909         var minScale = this.thumbEl.getWidth() / this.minWidth;
27910         
27911         if(this.minWidth < this.minHeight){
27912             minScale = this.thumbEl.getHeight() / this.minHeight;
27913         }
27914         
27915         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27916         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27917         
27918         if(
27919                 this.isDocument &&
27920                 (this.rotate == 0 || this.rotate == 180) && 
27921                 (
27922                     width > this.imageEl.OriginWidth || 
27923                     height > this.imageEl.OriginHeight ||
27924                     (width < this.minWidth && height < this.minHeight)
27925                 )
27926         ){
27927             return false;
27928         }
27929         
27930         if(
27931                 this.isDocument &&
27932                 (this.rotate == 90 || this.rotate == 270) && 
27933                 (
27934                     width > this.imageEl.OriginWidth || 
27935                     height > this.imageEl.OriginHeight ||
27936                     (width < this.minHeight && height < this.minWidth)
27937                 )
27938         ){
27939             return false;
27940         }
27941         
27942         if(
27943                 !this.isDocument &&
27944                 (this.rotate == 0 || this.rotate == 180) && 
27945                 (
27946                     width < this.minWidth || 
27947                     width > this.imageEl.OriginWidth || 
27948                     height < this.minHeight || 
27949                     height > this.imageEl.OriginHeight
27950                 )
27951         ){
27952             return false;
27953         }
27954         
27955         if(
27956                 !this.isDocument &&
27957                 (this.rotate == 90 || this.rotate == 270) && 
27958                 (
27959                     width < this.minHeight || 
27960                     width > this.imageEl.OriginWidth || 
27961                     height < this.minWidth || 
27962                     height > this.imageEl.OriginHeight
27963                 )
27964         ){
27965             return false;
27966         }
27967         
27968         return true;
27969         
27970     },
27971     
27972     onRotateLeft : function(e)
27973     {   
27974         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27975             
27976             var minScale = this.thumbEl.getWidth() / this.minWidth;
27977             
27978             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27979             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27980             
27981             this.startScale = this.scale;
27982             
27983             while (this.getScaleLevel() < minScale){
27984             
27985                 this.scale = this.scale + 1;
27986                 
27987                 if(!this.zoomable()){
27988                     break;
27989                 }
27990                 
27991                 if(
27992                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27993                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27994                 ){
27995                     continue;
27996                 }
27997                 
27998                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27999
28000                 this.draw();
28001                 
28002                 return;
28003             }
28004             
28005             this.scale = this.startScale;
28006             
28007             this.onRotateFail();
28008             
28009             return false;
28010         }
28011         
28012         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28013
28014         if(this.isDocument){
28015             this.setThumbBoxSize();
28016             this.setThumbBoxPosition();
28017             this.setCanvasPosition();
28018         }
28019         
28020         this.draw();
28021         
28022         this.fireEvent('rotate', this, 'left');
28023         
28024     },
28025     
28026     onRotateRight : function(e)
28027     {
28028         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28029             
28030             var minScale = this.thumbEl.getWidth() / this.minWidth;
28031         
28032             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28033             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28034             
28035             this.startScale = this.scale;
28036             
28037             while (this.getScaleLevel() < minScale){
28038             
28039                 this.scale = this.scale + 1;
28040                 
28041                 if(!this.zoomable()){
28042                     break;
28043                 }
28044                 
28045                 if(
28046                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28047                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28048                 ){
28049                     continue;
28050                 }
28051                 
28052                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28053
28054                 this.draw();
28055                 
28056                 return;
28057             }
28058             
28059             this.scale = this.startScale;
28060             
28061             this.onRotateFail();
28062             
28063             return false;
28064         }
28065         
28066         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28067
28068         if(this.isDocument){
28069             this.setThumbBoxSize();
28070             this.setThumbBoxPosition();
28071             this.setCanvasPosition();
28072         }
28073         
28074         this.draw();
28075         
28076         this.fireEvent('rotate', this, 'right');
28077     },
28078     
28079     onRotateFail : function()
28080     {
28081         this.errorEl.show(true);
28082         
28083         var _this = this;
28084         
28085         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28086     },
28087     
28088     draw : function()
28089     {
28090         this.previewEl.dom.innerHTML = '';
28091         
28092         var canvasEl = document.createElement("canvas");
28093         
28094         var contextEl = canvasEl.getContext("2d");
28095         
28096         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28097         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28098         var center = this.imageEl.OriginWidth / 2;
28099         
28100         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28101             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28102             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28103             center = this.imageEl.OriginHeight / 2;
28104         }
28105         
28106         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28107         
28108         contextEl.translate(center, center);
28109         contextEl.rotate(this.rotate * Math.PI / 180);
28110
28111         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28112         
28113         this.canvasEl = document.createElement("canvas");
28114         
28115         this.contextEl = this.canvasEl.getContext("2d");
28116         
28117         switch (this.rotate) {
28118             case 0 :
28119                 
28120                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28121                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28122                 
28123                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28124                 
28125                 break;
28126             case 90 : 
28127                 
28128                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28129                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28130                 
28131                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28132                     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);
28133                     break;
28134                 }
28135                 
28136                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28137                 
28138                 break;
28139             case 180 :
28140                 
28141                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28142                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28143                 
28144                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28145                     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);
28146                     break;
28147                 }
28148                 
28149                 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);
28150                 
28151                 break;
28152             case 270 :
28153                 
28154                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28155                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28156         
28157                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28158                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28159                     break;
28160                 }
28161                 
28162                 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);
28163                 
28164                 break;
28165             default : 
28166                 break;
28167         }
28168         
28169         this.previewEl.appendChild(this.canvasEl);
28170         
28171         this.setCanvasPosition();
28172     },
28173     
28174     crop : function()
28175     {
28176         if(!this.canvasLoaded){
28177             return;
28178         }
28179         
28180         var imageCanvas = document.createElement("canvas");
28181         
28182         var imageContext = imageCanvas.getContext("2d");
28183         
28184         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28185         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28186         
28187         var center = imageCanvas.width / 2;
28188         
28189         imageContext.translate(center, center);
28190         
28191         imageContext.rotate(this.rotate * Math.PI / 180);
28192         
28193         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28194         
28195         var canvas = document.createElement("canvas");
28196         
28197         var context = canvas.getContext("2d");
28198                 
28199         canvas.width = this.minWidth;
28200         canvas.height = this.minHeight;
28201
28202         switch (this.rotate) {
28203             case 0 :
28204                 
28205                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28206                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28207                 
28208                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28209                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28210                 
28211                 var targetWidth = this.minWidth - 2 * x;
28212                 var targetHeight = this.minHeight - 2 * y;
28213                 
28214                 var scale = 1;
28215                 
28216                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28217                     scale = targetWidth / width;
28218                 }
28219                 
28220                 if(x > 0 && y == 0){
28221                     scale = targetHeight / height;
28222                 }
28223                 
28224                 if(x > 0 && y > 0){
28225                     scale = targetWidth / width;
28226                     
28227                     if(width < height){
28228                         scale = targetHeight / height;
28229                     }
28230                 }
28231                 
28232                 context.scale(scale, scale);
28233                 
28234                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28235                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28236
28237                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28238                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28239
28240                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28241                 
28242                 break;
28243             case 90 : 
28244                 
28245                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28246                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28247                 
28248                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28249                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28250                 
28251                 var targetWidth = this.minWidth - 2 * x;
28252                 var targetHeight = this.minHeight - 2 * y;
28253                 
28254                 var scale = 1;
28255                 
28256                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28257                     scale = targetWidth / width;
28258                 }
28259                 
28260                 if(x > 0 && y == 0){
28261                     scale = targetHeight / height;
28262                 }
28263                 
28264                 if(x > 0 && y > 0){
28265                     scale = targetWidth / width;
28266                     
28267                     if(width < height){
28268                         scale = targetHeight / height;
28269                     }
28270                 }
28271                 
28272                 context.scale(scale, scale);
28273                 
28274                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28275                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28276
28277                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28278                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28279                 
28280                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28281                 
28282                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28283                 
28284                 break;
28285             case 180 :
28286                 
28287                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28288                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28289                 
28290                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28291                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28292                 
28293                 var targetWidth = this.minWidth - 2 * x;
28294                 var targetHeight = this.minHeight - 2 * y;
28295                 
28296                 var scale = 1;
28297                 
28298                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28299                     scale = targetWidth / width;
28300                 }
28301                 
28302                 if(x > 0 && y == 0){
28303                     scale = targetHeight / height;
28304                 }
28305                 
28306                 if(x > 0 && y > 0){
28307                     scale = targetWidth / width;
28308                     
28309                     if(width < height){
28310                         scale = targetHeight / height;
28311                     }
28312                 }
28313                 
28314                 context.scale(scale, scale);
28315                 
28316                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28317                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28318
28319                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28320                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28321
28322                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28323                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28324                 
28325                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28326                 
28327                 break;
28328             case 270 :
28329                 
28330                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28331                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28332                 
28333                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28334                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28335                 
28336                 var targetWidth = this.minWidth - 2 * x;
28337                 var targetHeight = this.minHeight - 2 * y;
28338                 
28339                 var scale = 1;
28340                 
28341                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28342                     scale = targetWidth / width;
28343                 }
28344                 
28345                 if(x > 0 && y == 0){
28346                     scale = targetHeight / height;
28347                 }
28348                 
28349                 if(x > 0 && y > 0){
28350                     scale = targetWidth / width;
28351                     
28352                     if(width < height){
28353                         scale = targetHeight / height;
28354                     }
28355                 }
28356                 
28357                 context.scale(scale, scale);
28358                 
28359                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28360                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28361
28362                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28363                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28364                 
28365                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28366                 
28367                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28368                 
28369                 break;
28370             default : 
28371                 break;
28372         }
28373         
28374         this.cropData = canvas.toDataURL(this.cropType);
28375         
28376         if(this.fireEvent('crop', this, this.cropData) !== false){
28377             this.process(this.file, this.cropData);
28378         }
28379         
28380         return;
28381         
28382     },
28383     
28384     setThumbBoxSize : function()
28385     {
28386         var width, height;
28387         
28388         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28389             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28390             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28391             
28392             this.minWidth = width;
28393             this.minHeight = height;
28394             
28395             if(this.rotate == 90 || this.rotate == 270){
28396                 this.minWidth = height;
28397                 this.minHeight = width;
28398             }
28399         }
28400         
28401         height = 300;
28402         width = Math.ceil(this.minWidth * height / this.minHeight);
28403         
28404         if(this.minWidth > this.minHeight){
28405             width = 300;
28406             height = Math.ceil(this.minHeight * width / this.minWidth);
28407         }
28408         
28409         this.thumbEl.setStyle({
28410             width : width + 'px',
28411             height : height + 'px'
28412         });
28413
28414         return;
28415             
28416     },
28417     
28418     setThumbBoxPosition : function()
28419     {
28420         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28421         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28422         
28423         this.thumbEl.setLeft(x);
28424         this.thumbEl.setTop(y);
28425         
28426     },
28427     
28428     baseRotateLevel : function()
28429     {
28430         this.baseRotate = 1;
28431         
28432         if(
28433                 typeof(this.exif) != 'undefined' &&
28434                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28435                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28436         ){
28437             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28438         }
28439         
28440         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28441         
28442     },
28443     
28444     baseScaleLevel : function()
28445     {
28446         var width, height;
28447         
28448         if(this.isDocument){
28449             
28450             if(this.baseRotate == 6 || this.baseRotate == 8){
28451             
28452                 height = this.thumbEl.getHeight();
28453                 this.baseScale = height / this.imageEl.OriginWidth;
28454
28455                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28456                     width = this.thumbEl.getWidth();
28457                     this.baseScale = width / this.imageEl.OriginHeight;
28458                 }
28459
28460                 return;
28461             }
28462
28463             height = this.thumbEl.getHeight();
28464             this.baseScale = height / this.imageEl.OriginHeight;
28465
28466             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28467                 width = this.thumbEl.getWidth();
28468                 this.baseScale = width / this.imageEl.OriginWidth;
28469             }
28470
28471             return;
28472         }
28473         
28474         if(this.baseRotate == 6 || this.baseRotate == 8){
28475             
28476             width = this.thumbEl.getHeight();
28477             this.baseScale = width / this.imageEl.OriginHeight;
28478             
28479             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28480                 height = this.thumbEl.getWidth();
28481                 this.baseScale = height / this.imageEl.OriginHeight;
28482             }
28483             
28484             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28485                 height = this.thumbEl.getWidth();
28486                 this.baseScale = height / this.imageEl.OriginHeight;
28487                 
28488                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28489                     width = this.thumbEl.getHeight();
28490                     this.baseScale = width / this.imageEl.OriginWidth;
28491                 }
28492             }
28493             
28494             return;
28495         }
28496         
28497         width = this.thumbEl.getWidth();
28498         this.baseScale = width / this.imageEl.OriginWidth;
28499         
28500         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28501             height = this.thumbEl.getHeight();
28502             this.baseScale = height / this.imageEl.OriginHeight;
28503         }
28504         
28505         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28506             
28507             height = this.thumbEl.getHeight();
28508             this.baseScale = height / this.imageEl.OriginHeight;
28509             
28510             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28511                 width = this.thumbEl.getWidth();
28512                 this.baseScale = width / this.imageEl.OriginWidth;
28513             }
28514             
28515         }
28516         
28517         return;
28518     },
28519     
28520     getScaleLevel : function()
28521     {
28522         return this.baseScale * Math.pow(1.1, this.scale);
28523     },
28524     
28525     onTouchStart : function(e)
28526     {
28527         if(!this.canvasLoaded){
28528             this.beforeSelectFile(e);
28529             return;
28530         }
28531         
28532         var touches = e.browserEvent.touches;
28533         
28534         if(!touches){
28535             return;
28536         }
28537         
28538         if(touches.length == 1){
28539             this.onMouseDown(e);
28540             return;
28541         }
28542         
28543         if(touches.length != 2){
28544             return;
28545         }
28546         
28547         var coords = [];
28548         
28549         for(var i = 0, finger; finger = touches[i]; i++){
28550             coords.push(finger.pageX, finger.pageY);
28551         }
28552         
28553         var x = Math.pow(coords[0] - coords[2], 2);
28554         var y = Math.pow(coords[1] - coords[3], 2);
28555         
28556         this.startDistance = Math.sqrt(x + y);
28557         
28558         this.startScale = this.scale;
28559         
28560         this.pinching = true;
28561         this.dragable = false;
28562         
28563     },
28564     
28565     onTouchMove : function(e)
28566     {
28567         if(!this.pinching && !this.dragable){
28568             return;
28569         }
28570         
28571         var touches = e.browserEvent.touches;
28572         
28573         if(!touches){
28574             return;
28575         }
28576         
28577         if(this.dragable){
28578             this.onMouseMove(e);
28579             return;
28580         }
28581         
28582         var coords = [];
28583         
28584         for(var i = 0, finger; finger = touches[i]; i++){
28585             coords.push(finger.pageX, finger.pageY);
28586         }
28587         
28588         var x = Math.pow(coords[0] - coords[2], 2);
28589         var y = Math.pow(coords[1] - coords[3], 2);
28590         
28591         this.endDistance = Math.sqrt(x + y);
28592         
28593         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28594         
28595         if(!this.zoomable()){
28596             this.scale = this.startScale;
28597             return;
28598         }
28599         
28600         this.draw();
28601         
28602     },
28603     
28604     onTouchEnd : function(e)
28605     {
28606         this.pinching = false;
28607         this.dragable = false;
28608         
28609     },
28610     
28611     process : function(file, crop)
28612     {
28613         if(this.loadMask){
28614             this.maskEl.mask(this.loadingText);
28615         }
28616         
28617         this.xhr = new XMLHttpRequest();
28618         
28619         file.xhr = this.xhr;
28620
28621         this.xhr.open(this.method, this.url, true);
28622         
28623         var headers = {
28624             "Accept": "application/json",
28625             "Cache-Control": "no-cache",
28626             "X-Requested-With": "XMLHttpRequest"
28627         };
28628         
28629         for (var headerName in headers) {
28630             var headerValue = headers[headerName];
28631             if (headerValue) {
28632                 this.xhr.setRequestHeader(headerName, headerValue);
28633             }
28634         }
28635         
28636         var _this = this;
28637         
28638         this.xhr.onload = function()
28639         {
28640             _this.xhrOnLoad(_this.xhr);
28641         }
28642         
28643         this.xhr.onerror = function()
28644         {
28645             _this.xhrOnError(_this.xhr);
28646         }
28647         
28648         var formData = new FormData();
28649
28650         formData.append('returnHTML', 'NO');
28651         
28652         if(crop){
28653             formData.append('crop', crop);
28654         }
28655         
28656         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28657             formData.append(this.paramName, file, file.name);
28658         }
28659         
28660         if(typeof(file.filename) != 'undefined'){
28661             formData.append('filename', file.filename);
28662         }
28663         
28664         if(typeof(file.mimetype) != 'undefined'){
28665             formData.append('mimetype', file.mimetype);
28666         }
28667         
28668         if(this.fireEvent('arrange', this, formData) != false){
28669             this.xhr.send(formData);
28670         };
28671     },
28672     
28673     xhrOnLoad : function(xhr)
28674     {
28675         if(this.loadMask){
28676             this.maskEl.unmask();
28677         }
28678         
28679         if (xhr.readyState !== 4) {
28680             this.fireEvent('exception', this, xhr);
28681             return;
28682         }
28683
28684         var response = Roo.decode(xhr.responseText);
28685         
28686         if(!response.success){
28687             this.fireEvent('exception', this, xhr);
28688             return;
28689         }
28690         
28691         var response = Roo.decode(xhr.responseText);
28692         
28693         this.fireEvent('upload', this, response);
28694         
28695     },
28696     
28697     xhrOnError : function()
28698     {
28699         if(this.loadMask){
28700             this.maskEl.unmask();
28701         }
28702         
28703         Roo.log('xhr on error');
28704         
28705         var response = Roo.decode(xhr.responseText);
28706           
28707         Roo.log(response);
28708         
28709     },
28710     
28711     prepare : function(file)
28712     {   
28713         if(this.loadMask){
28714             this.maskEl.mask(this.loadingText);
28715         }
28716         
28717         this.file = false;
28718         this.exif = {};
28719         
28720         if(typeof(file) === 'string'){
28721             this.loadCanvas(file);
28722             return;
28723         }
28724         
28725         if(!file || !this.urlAPI){
28726             return;
28727         }
28728         
28729         this.file = file;
28730         this.cropType = file.type;
28731         
28732         var _this = this;
28733         
28734         if(this.fireEvent('prepare', this, this.file) != false){
28735             
28736             var reader = new FileReader();
28737             
28738             reader.onload = function (e) {
28739                 if (e.target.error) {
28740                     Roo.log(e.target.error);
28741                     return;
28742                 }
28743                 
28744                 var buffer = e.target.result,
28745                     dataView = new DataView(buffer),
28746                     offset = 2,
28747                     maxOffset = dataView.byteLength - 4,
28748                     markerBytes,
28749                     markerLength;
28750                 
28751                 if (dataView.getUint16(0) === 0xffd8) {
28752                     while (offset < maxOffset) {
28753                         markerBytes = dataView.getUint16(offset);
28754                         
28755                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28756                             markerLength = dataView.getUint16(offset + 2) + 2;
28757                             if (offset + markerLength > dataView.byteLength) {
28758                                 Roo.log('Invalid meta data: Invalid segment size.');
28759                                 break;
28760                             }
28761                             
28762                             if(markerBytes == 0xffe1){
28763                                 _this.parseExifData(
28764                                     dataView,
28765                                     offset,
28766                                     markerLength
28767                                 );
28768                             }
28769                             
28770                             offset += markerLength;
28771                             
28772                             continue;
28773                         }
28774                         
28775                         break;
28776                     }
28777                     
28778                 }
28779                 
28780                 var url = _this.urlAPI.createObjectURL(_this.file);
28781                 
28782                 _this.loadCanvas(url);
28783                 
28784                 return;
28785             }
28786             
28787             reader.readAsArrayBuffer(this.file);
28788             
28789         }
28790         
28791     },
28792     
28793     parseExifData : function(dataView, offset, length)
28794     {
28795         var tiffOffset = offset + 10,
28796             littleEndian,
28797             dirOffset;
28798     
28799         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28800             // No Exif data, might be XMP data instead
28801             return;
28802         }
28803         
28804         // Check for the ASCII code for "Exif" (0x45786966):
28805         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28806             // No Exif data, might be XMP data instead
28807             return;
28808         }
28809         if (tiffOffset + 8 > dataView.byteLength) {
28810             Roo.log('Invalid Exif data: Invalid segment size.');
28811             return;
28812         }
28813         // Check for the two null bytes:
28814         if (dataView.getUint16(offset + 8) !== 0x0000) {
28815             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28816             return;
28817         }
28818         // Check the byte alignment:
28819         switch (dataView.getUint16(tiffOffset)) {
28820         case 0x4949:
28821             littleEndian = true;
28822             break;
28823         case 0x4D4D:
28824             littleEndian = false;
28825             break;
28826         default:
28827             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28828             return;
28829         }
28830         // Check for the TIFF tag marker (0x002A):
28831         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28832             Roo.log('Invalid Exif data: Missing TIFF marker.');
28833             return;
28834         }
28835         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28836         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28837         
28838         this.parseExifTags(
28839             dataView,
28840             tiffOffset,
28841             tiffOffset + dirOffset,
28842             littleEndian
28843         );
28844     },
28845     
28846     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28847     {
28848         var tagsNumber,
28849             dirEndOffset,
28850             i;
28851         if (dirOffset + 6 > dataView.byteLength) {
28852             Roo.log('Invalid Exif data: Invalid directory offset.');
28853             return;
28854         }
28855         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28856         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28857         if (dirEndOffset + 4 > dataView.byteLength) {
28858             Roo.log('Invalid Exif data: Invalid directory size.');
28859             return;
28860         }
28861         for (i = 0; i < tagsNumber; i += 1) {
28862             this.parseExifTag(
28863                 dataView,
28864                 tiffOffset,
28865                 dirOffset + 2 + 12 * i, // tag offset
28866                 littleEndian
28867             );
28868         }
28869         // Return the offset to the next directory:
28870         return dataView.getUint32(dirEndOffset, littleEndian);
28871     },
28872     
28873     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28874     {
28875         var tag = dataView.getUint16(offset, littleEndian);
28876         
28877         this.exif[tag] = this.getExifValue(
28878             dataView,
28879             tiffOffset,
28880             offset,
28881             dataView.getUint16(offset + 2, littleEndian), // tag type
28882             dataView.getUint32(offset + 4, littleEndian), // tag length
28883             littleEndian
28884         );
28885     },
28886     
28887     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28888     {
28889         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28890             tagSize,
28891             dataOffset,
28892             values,
28893             i,
28894             str,
28895             c;
28896     
28897         if (!tagType) {
28898             Roo.log('Invalid Exif data: Invalid tag type.');
28899             return;
28900         }
28901         
28902         tagSize = tagType.size * length;
28903         // Determine if the value is contained in the dataOffset bytes,
28904         // or if the value at the dataOffset is a pointer to the actual data:
28905         dataOffset = tagSize > 4 ?
28906                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28907         if (dataOffset + tagSize > dataView.byteLength) {
28908             Roo.log('Invalid Exif data: Invalid data offset.');
28909             return;
28910         }
28911         if (length === 1) {
28912             return tagType.getValue(dataView, dataOffset, littleEndian);
28913         }
28914         values = [];
28915         for (i = 0; i < length; i += 1) {
28916             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28917         }
28918         
28919         if (tagType.ascii) {
28920             str = '';
28921             // Concatenate the chars:
28922             for (i = 0; i < values.length; i += 1) {
28923                 c = values[i];
28924                 // Ignore the terminating NULL byte(s):
28925                 if (c === '\u0000') {
28926                     break;
28927                 }
28928                 str += c;
28929             }
28930             return str;
28931         }
28932         return values;
28933     }
28934     
28935 });
28936
28937 Roo.apply(Roo.bootstrap.UploadCropbox, {
28938     tags : {
28939         'Orientation': 0x0112
28940     },
28941     
28942     Orientation: {
28943             1: 0, //'top-left',
28944 //            2: 'top-right',
28945             3: 180, //'bottom-right',
28946 //            4: 'bottom-left',
28947 //            5: 'left-top',
28948             6: 90, //'right-top',
28949 //            7: 'right-bottom',
28950             8: 270 //'left-bottom'
28951     },
28952     
28953     exifTagTypes : {
28954         // byte, 8-bit unsigned int:
28955         1: {
28956             getValue: function (dataView, dataOffset) {
28957                 return dataView.getUint8(dataOffset);
28958             },
28959             size: 1
28960         },
28961         // ascii, 8-bit byte:
28962         2: {
28963             getValue: function (dataView, dataOffset) {
28964                 return String.fromCharCode(dataView.getUint8(dataOffset));
28965             },
28966             size: 1,
28967             ascii: true
28968         },
28969         // short, 16 bit int:
28970         3: {
28971             getValue: function (dataView, dataOffset, littleEndian) {
28972                 return dataView.getUint16(dataOffset, littleEndian);
28973             },
28974             size: 2
28975         },
28976         // long, 32 bit int:
28977         4: {
28978             getValue: function (dataView, dataOffset, littleEndian) {
28979                 return dataView.getUint32(dataOffset, littleEndian);
28980             },
28981             size: 4
28982         },
28983         // rational = two long values, first is numerator, second is denominator:
28984         5: {
28985             getValue: function (dataView, dataOffset, littleEndian) {
28986                 return dataView.getUint32(dataOffset, littleEndian) /
28987                     dataView.getUint32(dataOffset + 4, littleEndian);
28988             },
28989             size: 8
28990         },
28991         // slong, 32 bit signed int:
28992         9: {
28993             getValue: function (dataView, dataOffset, littleEndian) {
28994                 return dataView.getInt32(dataOffset, littleEndian);
28995             },
28996             size: 4
28997         },
28998         // srational, two slongs, first is numerator, second is denominator:
28999         10: {
29000             getValue: function (dataView, dataOffset, littleEndian) {
29001                 return dataView.getInt32(dataOffset, littleEndian) /
29002                     dataView.getInt32(dataOffset + 4, littleEndian);
29003             },
29004             size: 8
29005         }
29006     },
29007     
29008     footer : {
29009         STANDARD : [
29010             {
29011                 tag : 'div',
29012                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29013                 action : 'rotate-left',
29014                 cn : [
29015                     {
29016                         tag : 'button',
29017                         cls : 'btn btn-default',
29018                         html : '<i class="fa fa-undo"></i>'
29019                     }
29020                 ]
29021             },
29022             {
29023                 tag : 'div',
29024                 cls : 'btn-group roo-upload-cropbox-picture',
29025                 action : 'picture',
29026                 cn : [
29027                     {
29028                         tag : 'button',
29029                         cls : 'btn btn-default',
29030                         html : '<i class="fa fa-picture-o"></i>'
29031                     }
29032                 ]
29033             },
29034             {
29035                 tag : 'div',
29036                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29037                 action : 'rotate-right',
29038                 cn : [
29039                     {
29040                         tag : 'button',
29041                         cls : 'btn btn-default',
29042                         html : '<i class="fa fa-repeat"></i>'
29043                     }
29044                 ]
29045             }
29046         ],
29047         DOCUMENT : [
29048             {
29049                 tag : 'div',
29050                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29051                 action : 'rotate-left',
29052                 cn : [
29053                     {
29054                         tag : 'button',
29055                         cls : 'btn btn-default',
29056                         html : '<i class="fa fa-undo"></i>'
29057                     }
29058                 ]
29059             },
29060             {
29061                 tag : 'div',
29062                 cls : 'btn-group roo-upload-cropbox-download',
29063                 action : 'download',
29064                 cn : [
29065                     {
29066                         tag : 'button',
29067                         cls : 'btn btn-default',
29068                         html : '<i class="fa fa-download"></i>'
29069                     }
29070                 ]
29071             },
29072             {
29073                 tag : 'div',
29074                 cls : 'btn-group roo-upload-cropbox-crop',
29075                 action : 'crop',
29076                 cn : [
29077                     {
29078                         tag : 'button',
29079                         cls : 'btn btn-default',
29080                         html : '<i class="fa fa-crop"></i>'
29081                     }
29082                 ]
29083             },
29084             {
29085                 tag : 'div',
29086                 cls : 'btn-group roo-upload-cropbox-trash',
29087                 action : 'trash',
29088                 cn : [
29089                     {
29090                         tag : 'button',
29091                         cls : 'btn btn-default',
29092                         html : '<i class="fa fa-trash"></i>'
29093                     }
29094                 ]
29095             },
29096             {
29097                 tag : 'div',
29098                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29099                 action : 'rotate-right',
29100                 cn : [
29101                     {
29102                         tag : 'button',
29103                         cls : 'btn btn-default',
29104                         html : '<i class="fa fa-repeat"></i>'
29105                     }
29106                 ]
29107             }
29108         ],
29109         ROTATOR : [
29110             {
29111                 tag : 'div',
29112                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29113                 action : 'rotate-left',
29114                 cn : [
29115                     {
29116                         tag : 'button',
29117                         cls : 'btn btn-default',
29118                         html : '<i class="fa fa-undo"></i>'
29119                     }
29120                 ]
29121             },
29122             {
29123                 tag : 'div',
29124                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29125                 action : 'rotate-right',
29126                 cn : [
29127                     {
29128                         tag : 'button',
29129                         cls : 'btn btn-default',
29130                         html : '<i class="fa fa-repeat"></i>'
29131                     }
29132                 ]
29133             }
29134         ]
29135     }
29136 });
29137
29138 /*
29139 * Licence: LGPL
29140 */
29141
29142 /**
29143  * @class Roo.bootstrap.DocumentManager
29144  * @extends Roo.bootstrap.Component
29145  * Bootstrap DocumentManager class
29146  * @cfg {String} paramName default 'imageUpload'
29147  * @cfg {String} toolTipName default 'filename'
29148  * @cfg {String} method default POST
29149  * @cfg {String} url action url
29150  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29151  * @cfg {Boolean} multiple multiple upload default true
29152  * @cfg {Number} thumbSize default 300
29153  * @cfg {String} fieldLabel
29154  * @cfg {Number} labelWidth default 4
29155  * @cfg {String} labelAlign (left|top) default left
29156  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29157 * @cfg {Number} labellg set the width of label (1-12)
29158  * @cfg {Number} labelmd set the width of label (1-12)
29159  * @cfg {Number} labelsm set the width of label (1-12)
29160  * @cfg {Number} labelxs set the width of label (1-12)
29161  * 
29162  * @constructor
29163  * Create a new DocumentManager
29164  * @param {Object} config The config object
29165  */
29166
29167 Roo.bootstrap.DocumentManager = function(config){
29168     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29169     
29170     this.files = [];
29171     this.delegates = [];
29172     
29173     this.addEvents({
29174         /**
29175          * @event initial
29176          * Fire when initial the DocumentManager
29177          * @param {Roo.bootstrap.DocumentManager} this
29178          */
29179         "initial" : true,
29180         /**
29181          * @event inspect
29182          * inspect selected file
29183          * @param {Roo.bootstrap.DocumentManager} this
29184          * @param {File} file
29185          */
29186         "inspect" : true,
29187         /**
29188          * @event exception
29189          * Fire when xhr load exception
29190          * @param {Roo.bootstrap.DocumentManager} this
29191          * @param {XMLHttpRequest} xhr
29192          */
29193         "exception" : true,
29194         /**
29195          * @event afterupload
29196          * Fire when xhr load exception
29197          * @param {Roo.bootstrap.DocumentManager} this
29198          * @param {XMLHttpRequest} xhr
29199          */
29200         "afterupload" : true,
29201         /**
29202          * @event prepare
29203          * prepare the form data
29204          * @param {Roo.bootstrap.DocumentManager} this
29205          * @param {Object} formData
29206          */
29207         "prepare" : true,
29208         /**
29209          * @event remove
29210          * Fire when remove the file
29211          * @param {Roo.bootstrap.DocumentManager} this
29212          * @param {Object} file
29213          */
29214         "remove" : true,
29215         /**
29216          * @event refresh
29217          * Fire after refresh the file
29218          * @param {Roo.bootstrap.DocumentManager} this
29219          */
29220         "refresh" : true,
29221         /**
29222          * @event click
29223          * Fire after click the image
29224          * @param {Roo.bootstrap.DocumentManager} this
29225          * @param {Object} file
29226          */
29227         "click" : true,
29228         /**
29229          * @event edit
29230          * Fire when upload a image and editable set to true
29231          * @param {Roo.bootstrap.DocumentManager} this
29232          * @param {Object} file
29233          */
29234         "edit" : true,
29235         /**
29236          * @event beforeselectfile
29237          * Fire before select file
29238          * @param {Roo.bootstrap.DocumentManager} this
29239          */
29240         "beforeselectfile" : true,
29241         /**
29242          * @event process
29243          * Fire before process file
29244          * @param {Roo.bootstrap.DocumentManager} this
29245          * @param {Object} file
29246          */
29247         "process" : true,
29248         /**
29249          * @event previewrendered
29250          * Fire when preview rendered
29251          * @param {Roo.bootstrap.DocumentManager} this
29252          * @param {Object} file
29253          */
29254         "previewrendered" : true,
29255         /**
29256          */
29257         "previewResize" : true
29258         
29259     });
29260 };
29261
29262 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29263     
29264     boxes : 0,
29265     inputName : '',
29266     thumbSize : 300,
29267     multiple : true,
29268     files : false,
29269     method : 'POST',
29270     url : '',
29271     paramName : 'imageUpload',
29272     toolTipName : 'filename',
29273     fieldLabel : '',
29274     labelWidth : 4,
29275     labelAlign : 'left',
29276     editable : true,
29277     delegates : false,
29278     xhr : false, 
29279     
29280     labellg : 0,
29281     labelmd : 0,
29282     labelsm : 0,
29283     labelxs : 0,
29284     
29285     getAutoCreate : function()
29286     {   
29287         var managerWidget = {
29288             tag : 'div',
29289             cls : 'roo-document-manager',
29290             cn : [
29291                 {
29292                     tag : 'input',
29293                     cls : 'roo-document-manager-selector',
29294                     type : 'file'
29295                 },
29296                 {
29297                     tag : 'div',
29298                     cls : 'roo-document-manager-uploader',
29299                     cn : [
29300                         {
29301                             tag : 'div',
29302                             cls : 'roo-document-manager-upload-btn',
29303                             html : '<i class="fa fa-plus"></i>'
29304                         }
29305                     ]
29306                     
29307                 }
29308             ]
29309         };
29310         
29311         var content = [
29312             {
29313                 tag : 'div',
29314                 cls : 'column col-md-12',
29315                 cn : managerWidget
29316             }
29317         ];
29318         
29319         if(this.fieldLabel.length){
29320             
29321             content = [
29322                 {
29323                     tag : 'div',
29324                     cls : 'column col-md-12',
29325                     html : this.fieldLabel
29326                 },
29327                 {
29328                     tag : 'div',
29329                     cls : 'column col-md-12',
29330                     cn : managerWidget
29331                 }
29332             ];
29333
29334             if(this.labelAlign == 'left'){
29335                 content = [
29336                     {
29337                         tag : 'div',
29338                         cls : 'column',
29339                         html : this.fieldLabel
29340                     },
29341                     {
29342                         tag : 'div',
29343                         cls : 'column',
29344                         cn : managerWidget
29345                     }
29346                 ];
29347                 
29348                 if(this.labelWidth > 12){
29349                     content[0].style = "width: " + this.labelWidth + 'px';
29350                 }
29351
29352                 if(this.labelWidth < 13 && this.labelmd == 0){
29353                     this.labelmd = this.labelWidth;
29354                 }
29355
29356                 if(this.labellg > 0){
29357                     content[0].cls += ' col-lg-' + this.labellg;
29358                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29359                 }
29360
29361                 if(this.labelmd > 0){
29362                     content[0].cls += ' col-md-' + this.labelmd;
29363                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29364                 }
29365
29366                 if(this.labelsm > 0){
29367                     content[0].cls += ' col-sm-' + this.labelsm;
29368                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29369                 }
29370
29371                 if(this.labelxs > 0){
29372                     content[0].cls += ' col-xs-' + this.labelxs;
29373                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29374                 }
29375                 
29376             }
29377         }
29378         
29379         var cfg = {
29380             tag : 'div',
29381             cls : 'row clearfix',
29382             cn : content
29383         };
29384         
29385         return cfg;
29386         
29387     },
29388     
29389     initEvents : function()
29390     {
29391         this.managerEl = this.el.select('.roo-document-manager', true).first();
29392         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29393         
29394         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29395         this.selectorEl.hide();
29396         
29397         if(this.multiple){
29398             this.selectorEl.attr('multiple', 'multiple');
29399         }
29400         
29401         this.selectorEl.on('change', this.onFileSelected, this);
29402         
29403         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29404         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29405         
29406         this.uploader.on('click', this.onUploaderClick, this);
29407         
29408         this.renderProgressDialog();
29409         
29410         var _this = this;
29411         
29412         window.addEventListener("resize", function() { _this.refresh(); } );
29413         
29414         this.fireEvent('initial', this);
29415     },
29416     
29417     renderProgressDialog : function()
29418     {
29419         var _this = this;
29420         
29421         this.progressDialog = new Roo.bootstrap.Modal({
29422             cls : 'roo-document-manager-progress-dialog',
29423             allow_close : false,
29424             animate : false,
29425             title : '',
29426             buttons : [
29427                 {
29428                     name  :'cancel',
29429                     weight : 'danger',
29430                     html : 'Cancel'
29431                 }
29432             ], 
29433             listeners : { 
29434                 btnclick : function() {
29435                     _this.uploadCancel();
29436                     this.hide();
29437                 }
29438             }
29439         });
29440          
29441         this.progressDialog.render(Roo.get(document.body));
29442          
29443         this.progress = new Roo.bootstrap.Progress({
29444             cls : 'roo-document-manager-progress',
29445             active : true,
29446             striped : true
29447         });
29448         
29449         this.progress.render(this.progressDialog.getChildContainer());
29450         
29451         this.progressBar = new Roo.bootstrap.ProgressBar({
29452             cls : 'roo-document-manager-progress-bar',
29453             aria_valuenow : 0,
29454             aria_valuemin : 0,
29455             aria_valuemax : 12,
29456             panel : 'success'
29457         });
29458         
29459         this.progressBar.render(this.progress.getChildContainer());
29460     },
29461     
29462     onUploaderClick : function(e)
29463     {
29464         e.preventDefault();
29465      
29466         if(this.fireEvent('beforeselectfile', this) != false){
29467             this.selectorEl.dom.click();
29468         }
29469         
29470     },
29471     
29472     onFileSelected : function(e)
29473     {
29474         e.preventDefault();
29475         
29476         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29477             return;
29478         }
29479         
29480         Roo.each(this.selectorEl.dom.files, function(file){
29481             if(this.fireEvent('inspect', this, file) != false){
29482                 this.files.push(file);
29483             }
29484         }, this);
29485         
29486         this.queue();
29487         
29488     },
29489     
29490     queue : function()
29491     {
29492         this.selectorEl.dom.value = '';
29493         
29494         if(!this.files || !this.files.length){
29495             return;
29496         }
29497         
29498         if(this.boxes > 0 && this.files.length > this.boxes){
29499             this.files = this.files.slice(0, this.boxes);
29500         }
29501         
29502         this.uploader.show();
29503         
29504         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29505             this.uploader.hide();
29506         }
29507         
29508         var _this = this;
29509         
29510         var files = [];
29511         
29512         var docs = [];
29513         
29514         Roo.each(this.files, function(file){
29515             
29516             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29517                 var f = this.renderPreview(file);
29518                 files.push(f);
29519                 return;
29520             }
29521             
29522             if(file.type.indexOf('image') != -1){
29523                 this.delegates.push(
29524                     (function(){
29525                         _this.process(file);
29526                     }).createDelegate(this)
29527                 );
29528         
29529                 return;
29530             }
29531             
29532             docs.push(
29533                 (function(){
29534                     _this.process(file);
29535                 }).createDelegate(this)
29536             );
29537             
29538         }, this);
29539         
29540         this.files = files;
29541         
29542         this.delegates = this.delegates.concat(docs);
29543         
29544         if(!this.delegates.length){
29545             this.refresh();
29546             return;
29547         }
29548         
29549         this.progressBar.aria_valuemax = this.delegates.length;
29550         
29551         this.arrange();
29552         
29553         return;
29554     },
29555     
29556     arrange : function()
29557     {
29558         if(!this.delegates.length){
29559             this.progressDialog.hide();
29560             this.refresh();
29561             return;
29562         }
29563         
29564         var delegate = this.delegates.shift();
29565         
29566         this.progressDialog.show();
29567         
29568         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29569         
29570         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29571         
29572         delegate();
29573     },
29574     
29575     refresh : function()
29576     {
29577         this.uploader.show();
29578         
29579         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29580             this.uploader.hide();
29581         }
29582         
29583         Roo.isTouch ? this.closable(false) : this.closable(true);
29584         
29585         this.fireEvent('refresh', this);
29586     },
29587     
29588     onRemove : function(e, el, o)
29589     {
29590         e.preventDefault();
29591         
29592         this.fireEvent('remove', this, o);
29593         
29594     },
29595     
29596     remove : function(o)
29597     {
29598         var files = [];
29599         
29600         Roo.each(this.files, function(file){
29601             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29602                 files.push(file);
29603                 return;
29604             }
29605
29606             o.target.remove();
29607
29608         }, this);
29609         
29610         this.files = files;
29611         
29612         this.refresh();
29613     },
29614     
29615     clear : function()
29616     {
29617         Roo.each(this.files, function(file){
29618             if(!file.target){
29619                 return;
29620             }
29621             
29622             file.target.remove();
29623
29624         }, this);
29625         
29626         this.files = [];
29627         
29628         this.refresh();
29629     },
29630     
29631     onClick : function(e, el, o)
29632     {
29633         e.preventDefault();
29634         
29635         this.fireEvent('click', this, o);
29636         
29637     },
29638     
29639     closable : function(closable)
29640     {
29641         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29642             
29643             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29644             
29645             if(closable){
29646                 el.show();
29647                 return;
29648             }
29649             
29650             el.hide();
29651             
29652         }, this);
29653     },
29654     
29655     xhrOnLoad : function(xhr)
29656     {
29657         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29658             el.remove();
29659         }, this);
29660         
29661         if (xhr.readyState !== 4) {
29662             this.arrange();
29663             this.fireEvent('exception', this, xhr);
29664             return;
29665         }
29666
29667         var response = Roo.decode(xhr.responseText);
29668         
29669         if(!response.success){
29670             this.arrange();
29671             this.fireEvent('exception', this, xhr);
29672             return;
29673         }
29674         
29675         var file = this.renderPreview(response.data);
29676         
29677         this.files.push(file);
29678         
29679         this.arrange();
29680         
29681         this.fireEvent('afterupload', this, xhr);
29682         
29683     },
29684     
29685     xhrOnError : function(xhr)
29686     {
29687         Roo.log('xhr on error');
29688         
29689         var response = Roo.decode(xhr.responseText);
29690           
29691         Roo.log(response);
29692         
29693         this.arrange();
29694     },
29695     
29696     process : function(file)
29697     {
29698         if(this.fireEvent('process', this, file) !== false){
29699             if(this.editable && file.type.indexOf('image') != -1){
29700                 this.fireEvent('edit', this, file);
29701                 return;
29702             }
29703
29704             this.uploadStart(file, false);
29705
29706             return;
29707         }
29708         
29709     },
29710     
29711     uploadStart : function(file, crop)
29712     {
29713         this.xhr = new XMLHttpRequest();
29714         
29715         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29716             this.arrange();
29717             return;
29718         }
29719         
29720         file.xhr = this.xhr;
29721             
29722         this.managerEl.createChild({
29723             tag : 'div',
29724             cls : 'roo-document-manager-loading',
29725             cn : [
29726                 {
29727                     tag : 'div',
29728                     tooltip : file.name,
29729                     cls : 'roo-document-manager-thumb',
29730                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29731                 }
29732             ]
29733
29734         });
29735
29736         this.xhr.open(this.method, this.url, true);
29737         
29738         var headers = {
29739             "Accept": "application/json",
29740             "Cache-Control": "no-cache",
29741             "X-Requested-With": "XMLHttpRequest"
29742         };
29743         
29744         for (var headerName in headers) {
29745             var headerValue = headers[headerName];
29746             if (headerValue) {
29747                 this.xhr.setRequestHeader(headerName, headerValue);
29748             }
29749         }
29750         
29751         var _this = this;
29752         
29753         this.xhr.onload = function()
29754         {
29755             _this.xhrOnLoad(_this.xhr);
29756         }
29757         
29758         this.xhr.onerror = function()
29759         {
29760             _this.xhrOnError(_this.xhr);
29761         }
29762         
29763         var formData = new FormData();
29764
29765         formData.append('returnHTML', 'NO');
29766         
29767         if(crop){
29768             formData.append('crop', crop);
29769         }
29770         
29771         formData.append(this.paramName, file, file.name);
29772         
29773         var options = {
29774             file : file, 
29775             manually : false
29776         };
29777         
29778         if(this.fireEvent('prepare', this, formData, options) != false){
29779             
29780             if(options.manually){
29781                 return;
29782             }
29783             
29784             this.xhr.send(formData);
29785             return;
29786         };
29787         
29788         this.uploadCancel();
29789     },
29790     
29791     uploadCancel : function()
29792     {
29793         if (this.xhr) {
29794             this.xhr.abort();
29795         }
29796         
29797         this.delegates = [];
29798         
29799         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29800             el.remove();
29801         }, this);
29802         
29803         this.arrange();
29804     },
29805     
29806     renderPreview : function(file)
29807     {
29808         if(typeof(file.target) != 'undefined' && file.target){
29809             return file;
29810         }
29811         
29812         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29813         
29814         var previewEl = this.managerEl.createChild({
29815             tag : 'div',
29816             cls : 'roo-document-manager-preview',
29817             cn : [
29818                 {
29819                     tag : 'div',
29820                     tooltip : file[this.toolTipName],
29821                     cls : 'roo-document-manager-thumb',
29822                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29823                 },
29824                 {
29825                     tag : 'button',
29826                     cls : 'close',
29827                     html : '<i class="fa fa-times-circle"></i>'
29828                 }
29829             ]
29830         });
29831
29832         var close = previewEl.select('button.close', true).first();
29833
29834         close.on('click', this.onRemove, this, file);
29835
29836         file.target = previewEl;
29837
29838         var image = previewEl.select('img', true).first();
29839         
29840         var _this = this;
29841         
29842         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29843         
29844         image.on('click', this.onClick, this, file);
29845         
29846         this.fireEvent('previewrendered', this, file);
29847         
29848         return file;
29849         
29850     },
29851     
29852     onPreviewLoad : function(file, image)
29853     {
29854         if(typeof(file.target) == 'undefined' || !file.target){
29855             return;
29856         }
29857         
29858         var width = image.dom.naturalWidth || image.dom.width;
29859         var height = image.dom.naturalHeight || image.dom.height;
29860         
29861         if(!this.previewResize) {
29862             return;
29863         }
29864         
29865         if(width > height){
29866             file.target.addClass('wide');
29867             return;
29868         }
29869         
29870         file.target.addClass('tall');
29871         return;
29872         
29873     },
29874     
29875     uploadFromSource : function(file, crop)
29876     {
29877         this.xhr = new XMLHttpRequest();
29878         
29879         this.managerEl.createChild({
29880             tag : 'div',
29881             cls : 'roo-document-manager-loading',
29882             cn : [
29883                 {
29884                     tag : 'div',
29885                     tooltip : file.name,
29886                     cls : 'roo-document-manager-thumb',
29887                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29888                 }
29889             ]
29890
29891         });
29892
29893         this.xhr.open(this.method, this.url, true);
29894         
29895         var headers = {
29896             "Accept": "application/json",
29897             "Cache-Control": "no-cache",
29898             "X-Requested-With": "XMLHttpRequest"
29899         };
29900         
29901         for (var headerName in headers) {
29902             var headerValue = headers[headerName];
29903             if (headerValue) {
29904                 this.xhr.setRequestHeader(headerName, headerValue);
29905             }
29906         }
29907         
29908         var _this = this;
29909         
29910         this.xhr.onload = function()
29911         {
29912             _this.xhrOnLoad(_this.xhr);
29913         }
29914         
29915         this.xhr.onerror = function()
29916         {
29917             _this.xhrOnError(_this.xhr);
29918         }
29919         
29920         var formData = new FormData();
29921
29922         formData.append('returnHTML', 'NO');
29923         
29924         formData.append('crop', crop);
29925         
29926         if(typeof(file.filename) != 'undefined'){
29927             formData.append('filename', file.filename);
29928         }
29929         
29930         if(typeof(file.mimetype) != 'undefined'){
29931             formData.append('mimetype', file.mimetype);
29932         }
29933         
29934         Roo.log(formData);
29935         
29936         if(this.fireEvent('prepare', this, formData) != false){
29937             this.xhr.send(formData);
29938         };
29939     }
29940 });
29941
29942 /*
29943 * Licence: LGPL
29944 */
29945
29946 /**
29947  * @class Roo.bootstrap.DocumentViewer
29948  * @extends Roo.bootstrap.Component
29949  * Bootstrap DocumentViewer class
29950  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29951  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29952  * 
29953  * @constructor
29954  * Create a new DocumentViewer
29955  * @param {Object} config The config object
29956  */
29957
29958 Roo.bootstrap.DocumentViewer = function(config){
29959     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29960     
29961     this.addEvents({
29962         /**
29963          * @event initial
29964          * Fire after initEvent
29965          * @param {Roo.bootstrap.DocumentViewer} this
29966          */
29967         "initial" : true,
29968         /**
29969          * @event click
29970          * Fire after click
29971          * @param {Roo.bootstrap.DocumentViewer} this
29972          */
29973         "click" : true,
29974         /**
29975          * @event download
29976          * Fire after download button
29977          * @param {Roo.bootstrap.DocumentViewer} this
29978          */
29979         "download" : true,
29980         /**
29981          * @event trash
29982          * Fire after trash button
29983          * @param {Roo.bootstrap.DocumentViewer} this
29984          */
29985         "trash" : true
29986         
29987     });
29988 };
29989
29990 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29991     
29992     showDownload : true,
29993     
29994     showTrash : true,
29995     
29996     getAutoCreate : function()
29997     {
29998         var cfg = {
29999             tag : 'div',
30000             cls : 'roo-document-viewer',
30001             cn : [
30002                 {
30003                     tag : 'div',
30004                     cls : 'roo-document-viewer-body',
30005                     cn : [
30006                         {
30007                             tag : 'div',
30008                             cls : 'roo-document-viewer-thumb',
30009                             cn : [
30010                                 {
30011                                     tag : 'img',
30012                                     cls : 'roo-document-viewer-image'
30013                                 }
30014                             ]
30015                         }
30016                     ]
30017                 },
30018                 {
30019                     tag : 'div',
30020                     cls : 'roo-document-viewer-footer',
30021                     cn : {
30022                         tag : 'div',
30023                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30024                         cn : [
30025                             {
30026                                 tag : 'div',
30027                                 cls : 'btn-group roo-document-viewer-download',
30028                                 cn : [
30029                                     {
30030                                         tag : 'button',
30031                                         cls : 'btn btn-default',
30032                                         html : '<i class="fa fa-download"></i>'
30033                                     }
30034                                 ]
30035                             },
30036                             {
30037                                 tag : 'div',
30038                                 cls : 'btn-group roo-document-viewer-trash',
30039                                 cn : [
30040                                     {
30041                                         tag : 'button',
30042                                         cls : 'btn btn-default',
30043                                         html : '<i class="fa fa-trash"></i>'
30044                                     }
30045                                 ]
30046                             }
30047                         ]
30048                     }
30049                 }
30050             ]
30051         };
30052         
30053         return cfg;
30054     },
30055     
30056     initEvents : function()
30057     {
30058         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30059         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30060         
30061         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30062         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30063         
30064         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30065         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30066         
30067         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30068         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30069         
30070         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30071         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30072         
30073         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30074         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30075         
30076         this.bodyEl.on('click', this.onClick, this);
30077         this.downloadBtn.on('click', this.onDownload, this);
30078         this.trashBtn.on('click', this.onTrash, this);
30079         
30080         this.downloadBtn.hide();
30081         this.trashBtn.hide();
30082         
30083         if(this.showDownload){
30084             this.downloadBtn.show();
30085         }
30086         
30087         if(this.showTrash){
30088             this.trashBtn.show();
30089         }
30090         
30091         if(!this.showDownload && !this.showTrash) {
30092             this.footerEl.hide();
30093         }
30094         
30095     },
30096     
30097     initial : function()
30098     {
30099         this.fireEvent('initial', this);
30100         
30101     },
30102     
30103     onClick : function(e)
30104     {
30105         e.preventDefault();
30106         
30107         this.fireEvent('click', this);
30108     },
30109     
30110     onDownload : function(e)
30111     {
30112         e.preventDefault();
30113         
30114         this.fireEvent('download', this);
30115     },
30116     
30117     onTrash : function(e)
30118     {
30119         e.preventDefault();
30120         
30121         this.fireEvent('trash', this);
30122     }
30123     
30124 });
30125 /*
30126  * - LGPL
30127  *
30128  * nav progress bar
30129  * 
30130  */
30131
30132 /**
30133  * @class Roo.bootstrap.NavProgressBar
30134  * @extends Roo.bootstrap.Component
30135  * Bootstrap NavProgressBar class
30136  * 
30137  * @constructor
30138  * Create a new nav progress bar
30139  * @param {Object} config The config object
30140  */
30141
30142 Roo.bootstrap.NavProgressBar = function(config){
30143     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30144
30145     this.bullets = this.bullets || [];
30146    
30147 //    Roo.bootstrap.NavProgressBar.register(this);
30148      this.addEvents({
30149         /**
30150              * @event changed
30151              * Fires when the active item changes
30152              * @param {Roo.bootstrap.NavProgressBar} this
30153              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30154              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30155          */
30156         'changed': true
30157      });
30158     
30159 };
30160
30161 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30162     
30163     bullets : [],
30164     barItems : [],
30165     
30166     getAutoCreate : function()
30167     {
30168         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30169         
30170         cfg = {
30171             tag : 'div',
30172             cls : 'roo-navigation-bar-group',
30173             cn : [
30174                 {
30175                     tag : 'div',
30176                     cls : 'roo-navigation-top-bar'
30177                 },
30178                 {
30179                     tag : 'div',
30180                     cls : 'roo-navigation-bullets-bar',
30181                     cn : [
30182                         {
30183                             tag : 'ul',
30184                             cls : 'roo-navigation-bar'
30185                         }
30186                     ]
30187                 },
30188                 
30189                 {
30190                     tag : 'div',
30191                     cls : 'roo-navigation-bottom-bar'
30192                 }
30193             ]
30194             
30195         };
30196         
30197         return cfg;
30198         
30199     },
30200     
30201     initEvents: function() 
30202     {
30203         
30204     },
30205     
30206     onRender : function(ct, position) 
30207     {
30208         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30209         
30210         if(this.bullets.length){
30211             Roo.each(this.bullets, function(b){
30212                this.addItem(b);
30213             }, this);
30214         }
30215         
30216         this.format();
30217         
30218     },
30219     
30220     addItem : function(cfg)
30221     {
30222         var item = new Roo.bootstrap.NavProgressItem(cfg);
30223         
30224         item.parentId = this.id;
30225         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30226         
30227         if(cfg.html){
30228             var top = new Roo.bootstrap.Element({
30229                 tag : 'div',
30230                 cls : 'roo-navigation-bar-text'
30231             });
30232             
30233             var bottom = new Roo.bootstrap.Element({
30234                 tag : 'div',
30235                 cls : 'roo-navigation-bar-text'
30236             });
30237             
30238             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30239             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30240             
30241             var topText = new Roo.bootstrap.Element({
30242                 tag : 'span',
30243                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30244             });
30245             
30246             var bottomText = new Roo.bootstrap.Element({
30247                 tag : 'span',
30248                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30249             });
30250             
30251             topText.onRender(top.el, null);
30252             bottomText.onRender(bottom.el, null);
30253             
30254             item.topEl = top;
30255             item.bottomEl = bottom;
30256         }
30257         
30258         this.barItems.push(item);
30259         
30260         return item;
30261     },
30262     
30263     getActive : function()
30264     {
30265         var active = false;
30266         
30267         Roo.each(this.barItems, function(v){
30268             
30269             if (!v.isActive()) {
30270                 return;
30271             }
30272             
30273             active = v;
30274             return false;
30275             
30276         });
30277         
30278         return active;
30279     },
30280     
30281     setActiveItem : function(item)
30282     {
30283         var prev = false;
30284         
30285         Roo.each(this.barItems, function(v){
30286             if (v.rid == item.rid) {
30287                 return ;
30288             }
30289             
30290             if (v.isActive()) {
30291                 v.setActive(false);
30292                 prev = v;
30293             }
30294         });
30295
30296         item.setActive(true);
30297         
30298         this.fireEvent('changed', this, item, prev);
30299     },
30300     
30301     getBarItem: function(rid)
30302     {
30303         var ret = false;
30304         
30305         Roo.each(this.barItems, function(e) {
30306             if (e.rid != rid) {
30307                 return;
30308             }
30309             
30310             ret =  e;
30311             return false;
30312         });
30313         
30314         return ret;
30315     },
30316     
30317     indexOfItem : function(item)
30318     {
30319         var index = false;
30320         
30321         Roo.each(this.barItems, function(v, i){
30322             
30323             if (v.rid != item.rid) {
30324                 return;
30325             }
30326             
30327             index = i;
30328             return false
30329         });
30330         
30331         return index;
30332     },
30333     
30334     setActiveNext : function()
30335     {
30336         var i = this.indexOfItem(this.getActive());
30337         
30338         if (i > this.barItems.length) {
30339             return;
30340         }
30341         
30342         this.setActiveItem(this.barItems[i+1]);
30343     },
30344     
30345     setActivePrev : function()
30346     {
30347         var i = this.indexOfItem(this.getActive());
30348         
30349         if (i  < 1) {
30350             return;
30351         }
30352         
30353         this.setActiveItem(this.barItems[i-1]);
30354     },
30355     
30356     format : function()
30357     {
30358         if(!this.barItems.length){
30359             return;
30360         }
30361      
30362         var width = 100 / this.barItems.length;
30363         
30364         Roo.each(this.barItems, function(i){
30365             i.el.setStyle('width', width + '%');
30366             i.topEl.el.setStyle('width', width + '%');
30367             i.bottomEl.el.setStyle('width', width + '%');
30368         }, this);
30369         
30370     }
30371     
30372 });
30373 /*
30374  * - LGPL
30375  *
30376  * Nav Progress Item
30377  * 
30378  */
30379
30380 /**
30381  * @class Roo.bootstrap.NavProgressItem
30382  * @extends Roo.bootstrap.Component
30383  * Bootstrap NavProgressItem class
30384  * @cfg {String} rid the reference id
30385  * @cfg {Boolean} active (true|false) Is item active default false
30386  * @cfg {Boolean} disabled (true|false) Is item active default false
30387  * @cfg {String} html
30388  * @cfg {String} position (top|bottom) text position default bottom
30389  * @cfg {String} icon show icon instead of number
30390  * 
30391  * @constructor
30392  * Create a new NavProgressItem
30393  * @param {Object} config The config object
30394  */
30395 Roo.bootstrap.NavProgressItem = function(config){
30396     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30397     this.addEvents({
30398         // raw events
30399         /**
30400          * @event click
30401          * The raw click event for the entire grid.
30402          * @param {Roo.bootstrap.NavProgressItem} this
30403          * @param {Roo.EventObject} e
30404          */
30405         "click" : true
30406     });
30407    
30408 };
30409
30410 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30411     
30412     rid : '',
30413     active : false,
30414     disabled : false,
30415     html : '',
30416     position : 'bottom',
30417     icon : false,
30418     
30419     getAutoCreate : function()
30420     {
30421         var iconCls = 'roo-navigation-bar-item-icon';
30422         
30423         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30424         
30425         var cfg = {
30426             tag: 'li',
30427             cls: 'roo-navigation-bar-item',
30428             cn : [
30429                 {
30430                     tag : 'i',
30431                     cls : iconCls
30432                 }
30433             ]
30434         };
30435         
30436         if(this.active){
30437             cfg.cls += ' active';
30438         }
30439         if(this.disabled){
30440             cfg.cls += ' disabled';
30441         }
30442         
30443         return cfg;
30444     },
30445     
30446     disable : function()
30447     {
30448         this.setDisabled(true);
30449     },
30450     
30451     enable : function()
30452     {
30453         this.setDisabled(false);
30454     },
30455     
30456     initEvents: function() 
30457     {
30458         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30459         
30460         this.iconEl.on('click', this.onClick, this);
30461     },
30462     
30463     onClick : function(e)
30464     {
30465         e.preventDefault();
30466         
30467         if(this.disabled){
30468             return;
30469         }
30470         
30471         if(this.fireEvent('click', this, e) === false){
30472             return;
30473         };
30474         
30475         this.parent().setActiveItem(this);
30476     },
30477     
30478     isActive: function () 
30479     {
30480         return this.active;
30481     },
30482     
30483     setActive : function(state)
30484     {
30485         if(this.active == state){
30486             return;
30487         }
30488         
30489         this.active = state;
30490         
30491         if (state) {
30492             this.el.addClass('active');
30493             return;
30494         }
30495         
30496         this.el.removeClass('active');
30497         
30498         return;
30499     },
30500     
30501     setDisabled : function(state)
30502     {
30503         if(this.disabled == state){
30504             return;
30505         }
30506         
30507         this.disabled = state;
30508         
30509         if (state) {
30510             this.el.addClass('disabled');
30511             return;
30512         }
30513         
30514         this.el.removeClass('disabled');
30515     },
30516     
30517     tooltipEl : function()
30518     {
30519         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30520     }
30521 });
30522  
30523
30524  /*
30525  * - LGPL
30526  *
30527  * FieldLabel
30528  * 
30529  */
30530
30531 /**
30532  * @class Roo.bootstrap.FieldLabel
30533  * @extends Roo.bootstrap.Component
30534  * Bootstrap FieldLabel class
30535  * @cfg {String} html contents of the element
30536  * @cfg {String} tag tag of the element default label
30537  * @cfg {String} cls class of the element
30538  * @cfg {String} target label target 
30539  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30540  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30541  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30542  * @cfg {String} iconTooltip default "This field is required"
30543  * @cfg {String} indicatorpos (left|right) default left
30544  * 
30545  * @constructor
30546  * Create a new FieldLabel
30547  * @param {Object} config The config object
30548  */
30549
30550 Roo.bootstrap.FieldLabel = function(config){
30551     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30552     
30553     this.addEvents({
30554             /**
30555              * @event invalid
30556              * Fires after the field has been marked as invalid.
30557              * @param {Roo.form.FieldLabel} this
30558              * @param {String} msg The validation message
30559              */
30560             invalid : true,
30561             /**
30562              * @event valid
30563              * Fires after the field has been validated with no errors.
30564              * @param {Roo.form.FieldLabel} this
30565              */
30566             valid : true
30567         });
30568 };
30569
30570 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30571     
30572     tag: 'label',
30573     cls: '',
30574     html: '',
30575     target: '',
30576     allowBlank : true,
30577     invalidClass : 'has-warning',
30578     validClass : 'has-success',
30579     iconTooltip : 'This field is required',
30580     indicatorpos : 'left',
30581     
30582     getAutoCreate : function(){
30583         
30584         var cls = "";
30585         if (!this.allowBlank) {
30586             cls  = "visible";
30587         }
30588         
30589         var cfg = {
30590             tag : this.tag,
30591             cls : 'roo-bootstrap-field-label ' + this.cls,
30592             for : this.target,
30593             cn : [
30594                 {
30595                     tag : 'i',
30596                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30597                     tooltip : this.iconTooltip
30598                 },
30599                 {
30600                     tag : 'span',
30601                     html : this.html
30602                 }
30603             ] 
30604         };
30605         
30606         if(this.indicatorpos == 'right'){
30607             var cfg = {
30608                 tag : this.tag,
30609                 cls : 'roo-bootstrap-field-label ' + this.cls,
30610                 for : this.target,
30611                 cn : [
30612                     {
30613                         tag : 'span',
30614                         html : this.html
30615                     },
30616                     {
30617                         tag : 'i',
30618                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30619                         tooltip : this.iconTooltip
30620                     }
30621                 ] 
30622             };
30623         }
30624         
30625         return cfg;
30626     },
30627     
30628     initEvents: function() 
30629     {
30630         Roo.bootstrap.Element.superclass.initEvents.call(this);
30631         
30632         this.indicator = this.indicatorEl();
30633         
30634         if(this.indicator){
30635             this.indicator.removeClass('visible');
30636             this.indicator.addClass('invisible');
30637         }
30638         
30639         Roo.bootstrap.FieldLabel.register(this);
30640     },
30641     
30642     indicatorEl : function()
30643     {
30644         var indicator = this.el.select('i.roo-required-indicator',true).first();
30645         
30646         if(!indicator){
30647             return false;
30648         }
30649         
30650         return indicator;
30651         
30652     },
30653     
30654     /**
30655      * Mark this field as valid
30656      */
30657     markValid : function()
30658     {
30659         if(this.indicator){
30660             this.indicator.removeClass('visible');
30661             this.indicator.addClass('invisible');
30662         }
30663         if (Roo.bootstrap.version == 3) {
30664             this.el.removeClass(this.invalidClass);
30665             this.el.addClass(this.validClass);
30666         } else {
30667             this.el.removeClass('is-invalid');
30668             this.el.addClass('is-valid');
30669         }
30670         
30671         
30672         this.fireEvent('valid', this);
30673     },
30674     
30675     /**
30676      * Mark this field as invalid
30677      * @param {String} msg The validation message
30678      */
30679     markInvalid : function(msg)
30680     {
30681         if(this.indicator){
30682             this.indicator.removeClass('invisible');
30683             this.indicator.addClass('visible');
30684         }
30685           if (Roo.bootstrap.version == 3) {
30686             this.el.removeClass(this.validClass);
30687             this.el.addClass(this.invalidClass);
30688         } else {
30689             this.el.removeClass('is-valid');
30690             this.el.addClass('is-invalid');
30691         }
30692         
30693         
30694         this.fireEvent('invalid', this, msg);
30695     }
30696     
30697    
30698 });
30699
30700 Roo.apply(Roo.bootstrap.FieldLabel, {
30701     
30702     groups: {},
30703     
30704      /**
30705     * register a FieldLabel Group
30706     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30707     */
30708     register : function(label)
30709     {
30710         if(this.groups.hasOwnProperty(label.target)){
30711             return;
30712         }
30713      
30714         this.groups[label.target] = label;
30715         
30716     },
30717     /**
30718     * fetch a FieldLabel Group based on the target
30719     * @param {string} target
30720     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30721     */
30722     get: function(target) {
30723         if (typeof(this.groups[target]) == 'undefined') {
30724             return false;
30725         }
30726         
30727         return this.groups[target] ;
30728     }
30729 });
30730
30731  
30732
30733  /*
30734  * - LGPL
30735  *
30736  * page DateSplitField.
30737  * 
30738  */
30739
30740
30741 /**
30742  * @class Roo.bootstrap.DateSplitField
30743  * @extends Roo.bootstrap.Component
30744  * Bootstrap DateSplitField class
30745  * @cfg {string} fieldLabel - the label associated
30746  * @cfg {Number} labelWidth set the width of label (0-12)
30747  * @cfg {String} labelAlign (top|left)
30748  * @cfg {Boolean} dayAllowBlank (true|false) default false
30749  * @cfg {Boolean} monthAllowBlank (true|false) default false
30750  * @cfg {Boolean} yearAllowBlank (true|false) default false
30751  * @cfg {string} dayPlaceholder 
30752  * @cfg {string} monthPlaceholder
30753  * @cfg {string} yearPlaceholder
30754  * @cfg {string} dayFormat default 'd'
30755  * @cfg {string} monthFormat default 'm'
30756  * @cfg {string} yearFormat default 'Y'
30757  * @cfg {Number} labellg set the width of label (1-12)
30758  * @cfg {Number} labelmd set the width of label (1-12)
30759  * @cfg {Number} labelsm set the width of label (1-12)
30760  * @cfg {Number} labelxs set the width of label (1-12)
30761
30762  *     
30763  * @constructor
30764  * Create a new DateSplitField
30765  * @param {Object} config The config object
30766  */
30767
30768 Roo.bootstrap.DateSplitField = function(config){
30769     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30770     
30771     this.addEvents({
30772         // raw events
30773          /**
30774          * @event years
30775          * getting the data of years
30776          * @param {Roo.bootstrap.DateSplitField} this
30777          * @param {Object} years
30778          */
30779         "years" : true,
30780         /**
30781          * @event days
30782          * getting the data of days
30783          * @param {Roo.bootstrap.DateSplitField} this
30784          * @param {Object} days
30785          */
30786         "days" : true,
30787         /**
30788          * @event invalid
30789          * Fires after the field has been marked as invalid.
30790          * @param {Roo.form.Field} this
30791          * @param {String} msg The validation message
30792          */
30793         invalid : true,
30794        /**
30795          * @event valid
30796          * Fires after the field has been validated with no errors.
30797          * @param {Roo.form.Field} this
30798          */
30799         valid : true
30800     });
30801 };
30802
30803 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30804     
30805     fieldLabel : '',
30806     labelAlign : 'top',
30807     labelWidth : 3,
30808     dayAllowBlank : false,
30809     monthAllowBlank : false,
30810     yearAllowBlank : false,
30811     dayPlaceholder : '',
30812     monthPlaceholder : '',
30813     yearPlaceholder : '',
30814     dayFormat : 'd',
30815     monthFormat : 'm',
30816     yearFormat : 'Y',
30817     isFormField : true,
30818     labellg : 0,
30819     labelmd : 0,
30820     labelsm : 0,
30821     labelxs : 0,
30822     
30823     getAutoCreate : function()
30824     {
30825         var cfg = {
30826             tag : 'div',
30827             cls : 'row roo-date-split-field-group',
30828             cn : [
30829                 {
30830                     tag : 'input',
30831                     type : 'hidden',
30832                     cls : 'form-hidden-field roo-date-split-field-group-value',
30833                     name : this.name
30834                 }
30835             ]
30836         };
30837         
30838         var labelCls = 'col-md-12';
30839         var contentCls = 'col-md-4';
30840         
30841         if(this.fieldLabel){
30842             
30843             var label = {
30844                 tag : 'div',
30845                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30846                 cn : [
30847                     {
30848                         tag : 'label',
30849                         html : this.fieldLabel
30850                     }
30851                 ]
30852             };
30853             
30854             if(this.labelAlign == 'left'){
30855             
30856                 if(this.labelWidth > 12){
30857                     label.style = "width: " + this.labelWidth + 'px';
30858                 }
30859
30860                 if(this.labelWidth < 13 && this.labelmd == 0){
30861                     this.labelmd = this.labelWidth;
30862                 }
30863
30864                 if(this.labellg > 0){
30865                     labelCls = ' col-lg-' + this.labellg;
30866                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30867                 }
30868
30869                 if(this.labelmd > 0){
30870                     labelCls = ' col-md-' + this.labelmd;
30871                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30872                 }
30873
30874                 if(this.labelsm > 0){
30875                     labelCls = ' col-sm-' + this.labelsm;
30876                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30877                 }
30878
30879                 if(this.labelxs > 0){
30880                     labelCls = ' col-xs-' + this.labelxs;
30881                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30882                 }
30883             }
30884             
30885             label.cls += ' ' + labelCls;
30886             
30887             cfg.cn.push(label);
30888         }
30889         
30890         Roo.each(['day', 'month', 'year'], function(t){
30891             cfg.cn.push({
30892                 tag : 'div',
30893                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30894             });
30895         }, this);
30896         
30897         return cfg;
30898     },
30899     
30900     inputEl: function ()
30901     {
30902         return this.el.select('.roo-date-split-field-group-value', true).first();
30903     },
30904     
30905     onRender : function(ct, position) 
30906     {
30907         var _this = this;
30908         
30909         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30910         
30911         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30912         
30913         this.dayField = new Roo.bootstrap.ComboBox({
30914             allowBlank : this.dayAllowBlank,
30915             alwaysQuery : true,
30916             displayField : 'value',
30917             editable : false,
30918             fieldLabel : '',
30919             forceSelection : true,
30920             mode : 'local',
30921             placeholder : this.dayPlaceholder,
30922             selectOnFocus : true,
30923             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30924             triggerAction : 'all',
30925             typeAhead : true,
30926             valueField : 'value',
30927             store : new Roo.data.SimpleStore({
30928                 data : (function() {    
30929                     var days = [];
30930                     _this.fireEvent('days', _this, days);
30931                     return days;
30932                 })(),
30933                 fields : [ 'value' ]
30934             }),
30935             listeners : {
30936                 select : function (_self, record, index)
30937                 {
30938                     _this.setValue(_this.getValue());
30939                 }
30940             }
30941         });
30942
30943         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30944         
30945         this.monthField = new Roo.bootstrap.MonthField({
30946             after : '<i class=\"fa fa-calendar\"></i>',
30947             allowBlank : this.monthAllowBlank,
30948             placeholder : this.monthPlaceholder,
30949             readOnly : true,
30950             listeners : {
30951                 render : function (_self)
30952                 {
30953                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30954                         e.preventDefault();
30955                         _self.focus();
30956                     });
30957                 },
30958                 select : function (_self, oldvalue, newvalue)
30959                 {
30960                     _this.setValue(_this.getValue());
30961                 }
30962             }
30963         });
30964         
30965         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30966         
30967         this.yearField = new Roo.bootstrap.ComboBox({
30968             allowBlank : this.yearAllowBlank,
30969             alwaysQuery : true,
30970             displayField : 'value',
30971             editable : false,
30972             fieldLabel : '',
30973             forceSelection : true,
30974             mode : 'local',
30975             placeholder : this.yearPlaceholder,
30976             selectOnFocus : true,
30977             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30978             triggerAction : 'all',
30979             typeAhead : true,
30980             valueField : 'value',
30981             store : new Roo.data.SimpleStore({
30982                 data : (function() {
30983                     var years = [];
30984                     _this.fireEvent('years', _this, years);
30985                     return years;
30986                 })(),
30987                 fields : [ 'value' ]
30988             }),
30989             listeners : {
30990                 select : function (_self, record, index)
30991                 {
30992                     _this.setValue(_this.getValue());
30993                 }
30994             }
30995         });
30996
30997         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30998     },
30999     
31000     setValue : function(v, format)
31001     {
31002         this.inputEl.dom.value = v;
31003         
31004         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31005         
31006         var d = Date.parseDate(v, f);
31007         
31008         if(!d){
31009             this.validate();
31010             return;
31011         }
31012         
31013         this.setDay(d.format(this.dayFormat));
31014         this.setMonth(d.format(this.monthFormat));
31015         this.setYear(d.format(this.yearFormat));
31016         
31017         this.validate();
31018         
31019         return;
31020     },
31021     
31022     setDay : function(v)
31023     {
31024         this.dayField.setValue(v);
31025         this.inputEl.dom.value = this.getValue();
31026         this.validate();
31027         return;
31028     },
31029     
31030     setMonth : function(v)
31031     {
31032         this.monthField.setValue(v, true);
31033         this.inputEl.dom.value = this.getValue();
31034         this.validate();
31035         return;
31036     },
31037     
31038     setYear : function(v)
31039     {
31040         this.yearField.setValue(v);
31041         this.inputEl.dom.value = this.getValue();
31042         this.validate();
31043         return;
31044     },
31045     
31046     getDay : function()
31047     {
31048         return this.dayField.getValue();
31049     },
31050     
31051     getMonth : function()
31052     {
31053         return this.monthField.getValue();
31054     },
31055     
31056     getYear : function()
31057     {
31058         return this.yearField.getValue();
31059     },
31060     
31061     getValue : function()
31062     {
31063         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31064         
31065         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31066         
31067         return date;
31068     },
31069     
31070     reset : function()
31071     {
31072         this.setDay('');
31073         this.setMonth('');
31074         this.setYear('');
31075         this.inputEl.dom.value = '';
31076         this.validate();
31077         return;
31078     },
31079     
31080     validate : function()
31081     {
31082         var d = this.dayField.validate();
31083         var m = this.monthField.validate();
31084         var y = this.yearField.validate();
31085         
31086         var valid = true;
31087         
31088         if(
31089                 (!this.dayAllowBlank && !d) ||
31090                 (!this.monthAllowBlank && !m) ||
31091                 (!this.yearAllowBlank && !y)
31092         ){
31093             valid = false;
31094         }
31095         
31096         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31097             return valid;
31098         }
31099         
31100         if(valid){
31101             this.markValid();
31102             return valid;
31103         }
31104         
31105         this.markInvalid();
31106         
31107         return valid;
31108     },
31109     
31110     markValid : function()
31111     {
31112         
31113         var label = this.el.select('label', true).first();
31114         var icon = this.el.select('i.fa-star', true).first();
31115
31116         if(label && icon){
31117             icon.remove();
31118         }
31119         
31120         this.fireEvent('valid', this);
31121     },
31122     
31123      /**
31124      * Mark this field as invalid
31125      * @param {String} msg The validation message
31126      */
31127     markInvalid : function(msg)
31128     {
31129         
31130         var label = this.el.select('label', true).first();
31131         var icon = this.el.select('i.fa-star', true).first();
31132
31133         if(label && !icon){
31134             this.el.select('.roo-date-split-field-label', true).createChild({
31135                 tag : 'i',
31136                 cls : 'text-danger fa fa-lg fa-star',
31137                 tooltip : 'This field is required',
31138                 style : 'margin-right:5px;'
31139             }, label, true);
31140         }
31141         
31142         this.fireEvent('invalid', this, msg);
31143     },
31144     
31145     clearInvalid : function()
31146     {
31147         var label = this.el.select('label', true).first();
31148         var icon = this.el.select('i.fa-star', true).first();
31149
31150         if(label && icon){
31151             icon.remove();
31152         }
31153         
31154         this.fireEvent('valid', this);
31155     },
31156     
31157     getName: function()
31158     {
31159         return this.name;
31160     }
31161     
31162 });
31163
31164  /**
31165  *
31166  * This is based on 
31167  * http://masonry.desandro.com
31168  *
31169  * The idea is to render all the bricks based on vertical width...
31170  *
31171  * The original code extends 'outlayer' - we might need to use that....
31172  * 
31173  */
31174
31175
31176 /**
31177  * @class Roo.bootstrap.LayoutMasonry
31178  * @extends Roo.bootstrap.Component
31179  * Bootstrap Layout Masonry class
31180  * 
31181  * @constructor
31182  * Create a new Element
31183  * @param {Object} config The config object
31184  */
31185
31186 Roo.bootstrap.LayoutMasonry = function(config){
31187     
31188     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31189     
31190     this.bricks = [];
31191     
31192     Roo.bootstrap.LayoutMasonry.register(this);
31193     
31194     this.addEvents({
31195         // raw events
31196         /**
31197          * @event layout
31198          * Fire after layout the items
31199          * @param {Roo.bootstrap.LayoutMasonry} this
31200          * @param {Roo.EventObject} e
31201          */
31202         "layout" : true
31203     });
31204     
31205 };
31206
31207 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31208     
31209     /**
31210      * @cfg {Boolean} isLayoutInstant = no animation?
31211      */   
31212     isLayoutInstant : false, // needed?
31213    
31214     /**
31215      * @cfg {Number} boxWidth  width of the columns
31216      */   
31217     boxWidth : 450,
31218     
31219       /**
31220      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31221      */   
31222     boxHeight : 0,
31223     
31224     /**
31225      * @cfg {Number} padWidth padding below box..
31226      */   
31227     padWidth : 10, 
31228     
31229     /**
31230      * @cfg {Number} gutter gutter width..
31231      */   
31232     gutter : 10,
31233     
31234      /**
31235      * @cfg {Number} maxCols maximum number of columns
31236      */   
31237     
31238     maxCols: 0,
31239     
31240     /**
31241      * @cfg {Boolean} isAutoInitial defalut true
31242      */   
31243     isAutoInitial : true, 
31244     
31245     containerWidth: 0,
31246     
31247     /**
31248      * @cfg {Boolean} isHorizontal defalut false
31249      */   
31250     isHorizontal : false, 
31251
31252     currentSize : null,
31253     
31254     tag: 'div',
31255     
31256     cls: '',
31257     
31258     bricks: null, //CompositeElement
31259     
31260     cols : 1,
31261     
31262     _isLayoutInited : false,
31263     
31264 //    isAlternative : false, // only use for vertical layout...
31265     
31266     /**
31267      * @cfg {Number} alternativePadWidth padding below box..
31268      */   
31269     alternativePadWidth : 50,
31270     
31271     selectedBrick : [],
31272     
31273     getAutoCreate : function(){
31274         
31275         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31276         
31277         var cfg = {
31278             tag: this.tag,
31279             cls: 'blog-masonary-wrapper ' + this.cls,
31280             cn : {
31281                 cls : 'mas-boxes masonary'
31282             }
31283         };
31284         
31285         return cfg;
31286     },
31287     
31288     getChildContainer: function( )
31289     {
31290         if (this.boxesEl) {
31291             return this.boxesEl;
31292         }
31293         
31294         this.boxesEl = this.el.select('.mas-boxes').first();
31295         
31296         return this.boxesEl;
31297     },
31298     
31299     
31300     initEvents : function()
31301     {
31302         var _this = this;
31303         
31304         if(this.isAutoInitial){
31305             Roo.log('hook children rendered');
31306             this.on('childrenrendered', function() {
31307                 Roo.log('children rendered');
31308                 _this.initial();
31309             } ,this);
31310         }
31311     },
31312     
31313     initial : function()
31314     {
31315         this.selectedBrick = [];
31316         
31317         this.currentSize = this.el.getBox(true);
31318         
31319         Roo.EventManager.onWindowResize(this.resize, this); 
31320
31321         if(!this.isAutoInitial){
31322             this.layout();
31323             return;
31324         }
31325         
31326         this.layout();
31327         
31328         return;
31329         //this.layout.defer(500,this);
31330         
31331     },
31332     
31333     resize : function()
31334     {
31335         var cs = this.el.getBox(true);
31336         
31337         if (
31338                 this.currentSize.width == cs.width && 
31339                 this.currentSize.x == cs.x && 
31340                 this.currentSize.height == cs.height && 
31341                 this.currentSize.y == cs.y 
31342         ) {
31343             Roo.log("no change in with or X or Y");
31344             return;
31345         }
31346         
31347         this.currentSize = cs;
31348         
31349         this.layout();
31350         
31351     },
31352     
31353     layout : function()
31354     {   
31355         this._resetLayout();
31356         
31357         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31358         
31359         this.layoutItems( isInstant );
31360       
31361         this._isLayoutInited = true;
31362         
31363         this.fireEvent('layout', this);
31364         
31365     },
31366     
31367     _resetLayout : function()
31368     {
31369         if(this.isHorizontal){
31370             this.horizontalMeasureColumns();
31371             return;
31372         }
31373         
31374         this.verticalMeasureColumns();
31375         
31376     },
31377     
31378     verticalMeasureColumns : function()
31379     {
31380         this.getContainerWidth();
31381         
31382 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31383 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31384 //            return;
31385 //        }
31386         
31387         var boxWidth = this.boxWidth + this.padWidth;
31388         
31389         if(this.containerWidth < this.boxWidth){
31390             boxWidth = this.containerWidth
31391         }
31392         
31393         var containerWidth = this.containerWidth;
31394         
31395         var cols = Math.floor(containerWidth / boxWidth);
31396         
31397         this.cols = Math.max( cols, 1 );
31398         
31399         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31400         
31401         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31402         
31403         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31404         
31405         this.colWidth = boxWidth + avail - this.padWidth;
31406         
31407         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31408         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31409     },
31410     
31411     horizontalMeasureColumns : function()
31412     {
31413         this.getContainerWidth();
31414         
31415         var boxWidth = this.boxWidth;
31416         
31417         if(this.containerWidth < boxWidth){
31418             boxWidth = this.containerWidth;
31419         }
31420         
31421         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31422         
31423         this.el.setHeight(boxWidth);
31424         
31425     },
31426     
31427     getContainerWidth : function()
31428     {
31429         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31430     },
31431     
31432     layoutItems : function( isInstant )
31433     {
31434         Roo.log(this.bricks);
31435         
31436         var items = Roo.apply([], this.bricks);
31437         
31438         if(this.isHorizontal){
31439             this._horizontalLayoutItems( items , isInstant );
31440             return;
31441         }
31442         
31443 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31444 //            this._verticalAlternativeLayoutItems( items , isInstant );
31445 //            return;
31446 //        }
31447         
31448         this._verticalLayoutItems( items , isInstant );
31449         
31450     },
31451     
31452     _verticalLayoutItems : function ( items , isInstant)
31453     {
31454         if ( !items || !items.length ) {
31455             return;
31456         }
31457         
31458         var standard = [
31459             ['xs', 'xs', 'xs', 'tall'],
31460             ['xs', 'xs', 'tall'],
31461             ['xs', 'xs', 'sm'],
31462             ['xs', 'xs', 'xs'],
31463             ['xs', 'tall'],
31464             ['xs', 'sm'],
31465             ['xs', 'xs'],
31466             ['xs'],
31467             
31468             ['sm', 'xs', 'xs'],
31469             ['sm', 'xs'],
31470             ['sm'],
31471             
31472             ['tall', 'xs', 'xs', 'xs'],
31473             ['tall', 'xs', 'xs'],
31474             ['tall', 'xs'],
31475             ['tall']
31476             
31477         ];
31478         
31479         var queue = [];
31480         
31481         var boxes = [];
31482         
31483         var box = [];
31484         
31485         Roo.each(items, function(item, k){
31486             
31487             switch (item.size) {
31488                 // these layouts take up a full box,
31489                 case 'md' :
31490                 case 'md-left' :
31491                 case 'md-right' :
31492                 case 'wide' :
31493                     
31494                     if(box.length){
31495                         boxes.push(box);
31496                         box = [];
31497                     }
31498                     
31499                     boxes.push([item]);
31500                     
31501                     break;
31502                     
31503                 case 'xs' :
31504                 case 'sm' :
31505                 case 'tall' :
31506                     
31507                     box.push(item);
31508                     
31509                     break;
31510                 default :
31511                     break;
31512                     
31513             }
31514             
31515         }, this);
31516         
31517         if(box.length){
31518             boxes.push(box);
31519             box = [];
31520         }
31521         
31522         var filterPattern = function(box, length)
31523         {
31524             if(!box.length){
31525                 return;
31526             }
31527             
31528             var match = false;
31529             
31530             var pattern = box.slice(0, length);
31531             
31532             var format = [];
31533             
31534             Roo.each(pattern, function(i){
31535                 format.push(i.size);
31536             }, this);
31537             
31538             Roo.each(standard, function(s){
31539                 
31540                 if(String(s) != String(format)){
31541                     return;
31542                 }
31543                 
31544                 match = true;
31545                 return false;
31546                 
31547             }, this);
31548             
31549             if(!match && length == 1){
31550                 return;
31551             }
31552             
31553             if(!match){
31554                 filterPattern(box, length - 1);
31555                 return;
31556             }
31557                 
31558             queue.push(pattern);
31559
31560             box = box.slice(length, box.length);
31561
31562             filterPattern(box, 4);
31563
31564             return;
31565             
31566         }
31567         
31568         Roo.each(boxes, function(box, k){
31569             
31570             if(!box.length){
31571                 return;
31572             }
31573             
31574             if(box.length == 1){
31575                 queue.push(box);
31576                 return;
31577             }
31578             
31579             filterPattern(box, 4);
31580             
31581         }, this);
31582         
31583         this._processVerticalLayoutQueue( queue, isInstant );
31584         
31585     },
31586     
31587 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31588 //    {
31589 //        if ( !items || !items.length ) {
31590 //            return;
31591 //        }
31592 //
31593 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31594 //        
31595 //    },
31596     
31597     _horizontalLayoutItems : function ( items , isInstant)
31598     {
31599         if ( !items || !items.length || items.length < 3) {
31600             return;
31601         }
31602         
31603         items.reverse();
31604         
31605         var eItems = items.slice(0, 3);
31606         
31607         items = items.slice(3, items.length);
31608         
31609         var standard = [
31610             ['xs', 'xs', 'xs', 'wide'],
31611             ['xs', 'xs', 'wide'],
31612             ['xs', 'xs', 'sm'],
31613             ['xs', 'xs', 'xs'],
31614             ['xs', 'wide'],
31615             ['xs', 'sm'],
31616             ['xs', 'xs'],
31617             ['xs'],
31618             
31619             ['sm', 'xs', 'xs'],
31620             ['sm', 'xs'],
31621             ['sm'],
31622             
31623             ['wide', 'xs', 'xs', 'xs'],
31624             ['wide', 'xs', 'xs'],
31625             ['wide', 'xs'],
31626             ['wide'],
31627             
31628             ['wide-thin']
31629         ];
31630         
31631         var queue = [];
31632         
31633         var boxes = [];
31634         
31635         var box = [];
31636         
31637         Roo.each(items, function(item, k){
31638             
31639             switch (item.size) {
31640                 case 'md' :
31641                 case 'md-left' :
31642                 case 'md-right' :
31643                 case 'tall' :
31644                     
31645                     if(box.length){
31646                         boxes.push(box);
31647                         box = [];
31648                     }
31649                     
31650                     boxes.push([item]);
31651                     
31652                     break;
31653                     
31654                 case 'xs' :
31655                 case 'sm' :
31656                 case 'wide' :
31657                 case 'wide-thin' :
31658                     
31659                     box.push(item);
31660                     
31661                     break;
31662                 default :
31663                     break;
31664                     
31665             }
31666             
31667         }, this);
31668         
31669         if(box.length){
31670             boxes.push(box);
31671             box = [];
31672         }
31673         
31674         var filterPattern = function(box, length)
31675         {
31676             if(!box.length){
31677                 return;
31678             }
31679             
31680             var match = false;
31681             
31682             var pattern = box.slice(0, length);
31683             
31684             var format = [];
31685             
31686             Roo.each(pattern, function(i){
31687                 format.push(i.size);
31688             }, this);
31689             
31690             Roo.each(standard, function(s){
31691                 
31692                 if(String(s) != String(format)){
31693                     return;
31694                 }
31695                 
31696                 match = true;
31697                 return false;
31698                 
31699             }, this);
31700             
31701             if(!match && length == 1){
31702                 return;
31703             }
31704             
31705             if(!match){
31706                 filterPattern(box, length - 1);
31707                 return;
31708             }
31709                 
31710             queue.push(pattern);
31711
31712             box = box.slice(length, box.length);
31713
31714             filterPattern(box, 4);
31715
31716             return;
31717             
31718         }
31719         
31720         Roo.each(boxes, function(box, k){
31721             
31722             if(!box.length){
31723                 return;
31724             }
31725             
31726             if(box.length == 1){
31727                 queue.push(box);
31728                 return;
31729             }
31730             
31731             filterPattern(box, 4);
31732             
31733         }, this);
31734         
31735         
31736         var prune = [];
31737         
31738         var pos = this.el.getBox(true);
31739         
31740         var minX = pos.x;
31741         
31742         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31743         
31744         var hit_end = false;
31745         
31746         Roo.each(queue, function(box){
31747             
31748             if(hit_end){
31749                 
31750                 Roo.each(box, function(b){
31751                 
31752                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31753                     b.el.hide();
31754
31755                 }, this);
31756
31757                 return;
31758             }
31759             
31760             var mx = 0;
31761             
31762             Roo.each(box, function(b){
31763                 
31764                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31765                 b.el.show();
31766
31767                 mx = Math.max(mx, b.x);
31768                 
31769             }, this);
31770             
31771             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31772             
31773             if(maxX < minX){
31774                 
31775                 Roo.each(box, function(b){
31776                 
31777                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31778                     b.el.hide();
31779                     
31780                 }, this);
31781                 
31782                 hit_end = true;
31783                 
31784                 return;
31785             }
31786             
31787             prune.push(box);
31788             
31789         }, this);
31790         
31791         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31792     },
31793     
31794     /** Sets position of item in DOM
31795     * @param {Element} item
31796     * @param {Number} x - horizontal position
31797     * @param {Number} y - vertical position
31798     * @param {Boolean} isInstant - disables transitions
31799     */
31800     _processVerticalLayoutQueue : function( queue, isInstant )
31801     {
31802         var pos = this.el.getBox(true);
31803         var x = pos.x;
31804         var y = pos.y;
31805         var maxY = [];
31806         
31807         for (var i = 0; i < this.cols; i++){
31808             maxY[i] = pos.y;
31809         }
31810         
31811         Roo.each(queue, function(box, k){
31812             
31813             var col = k % this.cols;
31814             
31815             Roo.each(box, function(b,kk){
31816                 
31817                 b.el.position('absolute');
31818                 
31819                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31820                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31821                 
31822                 if(b.size == 'md-left' || b.size == 'md-right'){
31823                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31824                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31825                 }
31826                 
31827                 b.el.setWidth(width);
31828                 b.el.setHeight(height);
31829                 // iframe?
31830                 b.el.select('iframe',true).setSize(width,height);
31831                 
31832             }, this);
31833             
31834             for (var i = 0; i < this.cols; i++){
31835                 
31836                 if(maxY[i] < maxY[col]){
31837                     col = i;
31838                     continue;
31839                 }
31840                 
31841                 col = Math.min(col, i);
31842                 
31843             }
31844             
31845             x = pos.x + col * (this.colWidth + this.padWidth);
31846             
31847             y = maxY[col];
31848             
31849             var positions = [];
31850             
31851             switch (box.length){
31852                 case 1 :
31853                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31854                     break;
31855                 case 2 :
31856                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31857                     break;
31858                 case 3 :
31859                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31860                     break;
31861                 case 4 :
31862                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31863                     break;
31864                 default :
31865                     break;
31866             }
31867             
31868             Roo.each(box, function(b,kk){
31869                 
31870                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31871                 
31872                 var sz = b.el.getSize();
31873                 
31874                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31875                 
31876             }, this);
31877             
31878         }, this);
31879         
31880         var mY = 0;
31881         
31882         for (var i = 0; i < this.cols; i++){
31883             mY = Math.max(mY, maxY[i]);
31884         }
31885         
31886         this.el.setHeight(mY - pos.y);
31887         
31888     },
31889     
31890 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31891 //    {
31892 //        var pos = this.el.getBox(true);
31893 //        var x = pos.x;
31894 //        var y = pos.y;
31895 //        var maxX = pos.right;
31896 //        
31897 //        var maxHeight = 0;
31898 //        
31899 //        Roo.each(items, function(item, k){
31900 //            
31901 //            var c = k % 2;
31902 //            
31903 //            item.el.position('absolute');
31904 //                
31905 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31906 //
31907 //            item.el.setWidth(width);
31908 //
31909 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31910 //
31911 //            item.el.setHeight(height);
31912 //            
31913 //            if(c == 0){
31914 //                item.el.setXY([x, y], isInstant ? false : true);
31915 //            } else {
31916 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31917 //            }
31918 //            
31919 //            y = y + height + this.alternativePadWidth;
31920 //            
31921 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31922 //            
31923 //        }, this);
31924 //        
31925 //        this.el.setHeight(maxHeight);
31926 //        
31927 //    },
31928     
31929     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31930     {
31931         var pos = this.el.getBox(true);
31932         
31933         var minX = pos.x;
31934         var minY = pos.y;
31935         
31936         var maxX = pos.right;
31937         
31938         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31939         
31940         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31941         
31942         Roo.each(queue, function(box, k){
31943             
31944             Roo.each(box, function(b, kk){
31945                 
31946                 b.el.position('absolute');
31947                 
31948                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31949                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31950                 
31951                 if(b.size == 'md-left' || b.size == 'md-right'){
31952                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31953                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31954                 }
31955                 
31956                 b.el.setWidth(width);
31957                 b.el.setHeight(height);
31958                 
31959             }, this);
31960             
31961             if(!box.length){
31962                 return;
31963             }
31964             
31965             var positions = [];
31966             
31967             switch (box.length){
31968                 case 1 :
31969                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31970                     break;
31971                 case 2 :
31972                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31973                     break;
31974                 case 3 :
31975                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31976                     break;
31977                 case 4 :
31978                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31979                     break;
31980                 default :
31981                     break;
31982             }
31983             
31984             Roo.each(box, function(b,kk){
31985                 
31986                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31987                 
31988                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31989                 
31990             }, this);
31991             
31992         }, this);
31993         
31994     },
31995     
31996     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31997     {
31998         Roo.each(eItems, function(b,k){
31999             
32000             b.size = (k == 0) ? 'sm' : 'xs';
32001             b.x = (k == 0) ? 2 : 1;
32002             b.y = (k == 0) ? 2 : 1;
32003             
32004             b.el.position('absolute');
32005             
32006             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32007                 
32008             b.el.setWidth(width);
32009             
32010             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32011             
32012             b.el.setHeight(height);
32013             
32014         }, this);
32015
32016         var positions = [];
32017         
32018         positions.push({
32019             x : maxX - this.unitWidth * 2 - this.gutter,
32020             y : minY
32021         });
32022         
32023         positions.push({
32024             x : maxX - this.unitWidth,
32025             y : minY + (this.unitWidth + this.gutter) * 2
32026         });
32027         
32028         positions.push({
32029             x : maxX - this.unitWidth * 3 - this.gutter * 2,
32030             y : minY
32031         });
32032         
32033         Roo.each(eItems, function(b,k){
32034             
32035             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32036
32037         }, this);
32038         
32039     },
32040     
32041     getVerticalOneBoxColPositions : function(x, y, box)
32042     {
32043         var pos = [];
32044         
32045         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32046         
32047         if(box[0].size == 'md-left'){
32048             rand = 0;
32049         }
32050         
32051         if(box[0].size == 'md-right'){
32052             rand = 1;
32053         }
32054         
32055         pos.push({
32056             x : x + (this.unitWidth + this.gutter) * rand,
32057             y : y
32058         });
32059         
32060         return pos;
32061     },
32062     
32063     getVerticalTwoBoxColPositions : function(x, y, box)
32064     {
32065         var pos = [];
32066         
32067         if(box[0].size == 'xs'){
32068             
32069             pos.push({
32070                 x : x,
32071                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32072             });
32073
32074             pos.push({
32075                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32076                 y : y
32077             });
32078             
32079             return pos;
32080             
32081         }
32082         
32083         pos.push({
32084             x : x,
32085             y : y
32086         });
32087
32088         pos.push({
32089             x : x + (this.unitWidth + this.gutter) * 2,
32090             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32091         });
32092         
32093         return pos;
32094         
32095     },
32096     
32097     getVerticalThreeBoxColPositions : function(x, y, box)
32098     {
32099         var pos = [];
32100         
32101         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32102             
32103             pos.push({
32104                 x : x,
32105                 y : y
32106             });
32107
32108             pos.push({
32109                 x : x + (this.unitWidth + this.gutter) * 1,
32110                 y : y
32111             });
32112             
32113             pos.push({
32114                 x : x + (this.unitWidth + this.gutter) * 2,
32115                 y : y
32116             });
32117             
32118             return pos;
32119             
32120         }
32121         
32122         if(box[0].size == 'xs' && box[1].size == 'xs'){
32123             
32124             pos.push({
32125                 x : x,
32126                 y : y
32127             });
32128
32129             pos.push({
32130                 x : x,
32131                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32132             });
32133             
32134             pos.push({
32135                 x : x + (this.unitWidth + this.gutter) * 1,
32136                 y : y
32137             });
32138             
32139             return pos;
32140             
32141         }
32142         
32143         pos.push({
32144             x : x,
32145             y : y
32146         });
32147
32148         pos.push({
32149             x : x + (this.unitWidth + this.gutter) * 2,
32150             y : y
32151         });
32152
32153         pos.push({
32154             x : x + (this.unitWidth + this.gutter) * 2,
32155             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32156         });
32157             
32158         return pos;
32159         
32160     },
32161     
32162     getVerticalFourBoxColPositions : function(x, y, box)
32163     {
32164         var pos = [];
32165         
32166         if(box[0].size == 'xs'){
32167             
32168             pos.push({
32169                 x : x,
32170                 y : y
32171             });
32172
32173             pos.push({
32174                 x : x,
32175                 y : y + (this.unitHeight + this.gutter) * 1
32176             });
32177             
32178             pos.push({
32179                 x : x,
32180                 y : y + (this.unitHeight + this.gutter) * 2
32181             });
32182             
32183             pos.push({
32184                 x : x + (this.unitWidth + this.gutter) * 1,
32185                 y : y
32186             });
32187             
32188             return pos;
32189             
32190         }
32191         
32192         pos.push({
32193             x : x,
32194             y : y
32195         });
32196
32197         pos.push({
32198             x : x + (this.unitWidth + this.gutter) * 2,
32199             y : y
32200         });
32201
32202         pos.push({
32203             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32204             y : y + (this.unitHeight + this.gutter) * 1
32205         });
32206
32207         pos.push({
32208             x : x + (this.unitWidth + this.gutter) * 2,
32209             y : y + (this.unitWidth + this.gutter) * 2
32210         });
32211
32212         return pos;
32213         
32214     },
32215     
32216     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32217     {
32218         var pos = [];
32219         
32220         if(box[0].size == 'md-left'){
32221             pos.push({
32222                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32223                 y : minY
32224             });
32225             
32226             return pos;
32227         }
32228         
32229         if(box[0].size == 'md-right'){
32230             pos.push({
32231                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32232                 y : minY + (this.unitWidth + this.gutter) * 1
32233             });
32234             
32235             return pos;
32236         }
32237         
32238         var rand = Math.floor(Math.random() * (4 - box[0].y));
32239         
32240         pos.push({
32241             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32242             y : minY + (this.unitWidth + this.gutter) * rand
32243         });
32244         
32245         return pos;
32246         
32247     },
32248     
32249     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32250     {
32251         var pos = [];
32252         
32253         if(box[0].size == 'xs'){
32254             
32255             pos.push({
32256                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32257                 y : minY
32258             });
32259
32260             pos.push({
32261                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32262                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32263             });
32264             
32265             return pos;
32266             
32267         }
32268         
32269         pos.push({
32270             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32271             y : minY
32272         });
32273
32274         pos.push({
32275             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32276             y : minY + (this.unitWidth + this.gutter) * 2
32277         });
32278         
32279         return pos;
32280         
32281     },
32282     
32283     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32284     {
32285         var pos = [];
32286         
32287         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32288             
32289             pos.push({
32290                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32291                 y : minY
32292             });
32293
32294             pos.push({
32295                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32296                 y : minY + (this.unitWidth + this.gutter) * 1
32297             });
32298             
32299             pos.push({
32300                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32301                 y : minY + (this.unitWidth + this.gutter) * 2
32302             });
32303             
32304             return pos;
32305             
32306         }
32307         
32308         if(box[0].size == 'xs' && box[1].size == 'xs'){
32309             
32310             pos.push({
32311                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32312                 y : minY
32313             });
32314
32315             pos.push({
32316                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32317                 y : minY
32318             });
32319             
32320             pos.push({
32321                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32322                 y : minY + (this.unitWidth + this.gutter) * 1
32323             });
32324             
32325             return pos;
32326             
32327         }
32328         
32329         pos.push({
32330             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32331             y : minY
32332         });
32333
32334         pos.push({
32335             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32336             y : minY + (this.unitWidth + this.gutter) * 2
32337         });
32338
32339         pos.push({
32340             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32341             y : minY + (this.unitWidth + this.gutter) * 2
32342         });
32343             
32344         return pos;
32345         
32346     },
32347     
32348     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32349     {
32350         var pos = [];
32351         
32352         if(box[0].size == 'xs'){
32353             
32354             pos.push({
32355                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32356                 y : minY
32357             });
32358
32359             pos.push({
32360                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32361                 y : minY
32362             });
32363             
32364             pos.push({
32365                 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),
32366                 y : minY
32367             });
32368             
32369             pos.push({
32370                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32371                 y : minY + (this.unitWidth + this.gutter) * 1
32372             });
32373             
32374             return pos;
32375             
32376         }
32377         
32378         pos.push({
32379             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32380             y : minY
32381         });
32382         
32383         pos.push({
32384             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32385             y : minY + (this.unitWidth + this.gutter) * 2
32386         });
32387         
32388         pos.push({
32389             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32390             y : minY + (this.unitWidth + this.gutter) * 2
32391         });
32392         
32393         pos.push({
32394             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),
32395             y : minY + (this.unitWidth + this.gutter) * 2
32396         });
32397
32398         return pos;
32399         
32400     },
32401     
32402     /**
32403     * remove a Masonry Brick
32404     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32405     */
32406     removeBrick : function(brick_id)
32407     {
32408         if (!brick_id) {
32409             return;
32410         }
32411         
32412         for (var i = 0; i<this.bricks.length; i++) {
32413             if (this.bricks[i].id == brick_id) {
32414                 this.bricks.splice(i,1);
32415                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32416                 this.initial();
32417             }
32418         }
32419     },
32420     
32421     /**
32422     * adds a Masonry Brick
32423     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32424     */
32425     addBrick : function(cfg)
32426     {
32427         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32428         //this.register(cn);
32429         cn.parentId = this.id;
32430         cn.render(this.el);
32431         return cn;
32432     },
32433     
32434     /**
32435     * register a Masonry Brick
32436     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32437     */
32438     
32439     register : function(brick)
32440     {
32441         this.bricks.push(brick);
32442         brick.masonryId = this.id;
32443     },
32444     
32445     /**
32446     * clear all the Masonry Brick
32447     */
32448     clearAll : function()
32449     {
32450         this.bricks = [];
32451         //this.getChildContainer().dom.innerHTML = "";
32452         this.el.dom.innerHTML = '';
32453     },
32454     
32455     getSelected : function()
32456     {
32457         if (!this.selectedBrick) {
32458             return false;
32459         }
32460         
32461         return this.selectedBrick;
32462     }
32463 });
32464
32465 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32466     
32467     groups: {},
32468      /**
32469     * register a Masonry Layout
32470     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32471     */
32472     
32473     register : function(layout)
32474     {
32475         this.groups[layout.id] = layout;
32476     },
32477     /**
32478     * fetch a  Masonry Layout based on the masonry layout ID
32479     * @param {string} the masonry layout to add
32480     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32481     */
32482     
32483     get: function(layout_id) {
32484         if (typeof(this.groups[layout_id]) == 'undefined') {
32485             return false;
32486         }
32487         return this.groups[layout_id] ;
32488     }
32489     
32490     
32491     
32492 });
32493
32494  
32495
32496  /**
32497  *
32498  * This is based on 
32499  * http://masonry.desandro.com
32500  *
32501  * The idea is to render all the bricks based on vertical width...
32502  *
32503  * The original code extends 'outlayer' - we might need to use that....
32504  * 
32505  */
32506
32507
32508 /**
32509  * @class Roo.bootstrap.LayoutMasonryAuto
32510  * @extends Roo.bootstrap.Component
32511  * Bootstrap Layout Masonry class
32512  * 
32513  * @constructor
32514  * Create a new Element
32515  * @param {Object} config The config object
32516  */
32517
32518 Roo.bootstrap.LayoutMasonryAuto = function(config){
32519     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32520 };
32521
32522 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32523     
32524       /**
32525      * @cfg {Boolean} isFitWidth  - resize the width..
32526      */   
32527     isFitWidth : false,  // options..
32528     /**
32529      * @cfg {Boolean} isOriginLeft = left align?
32530      */   
32531     isOriginLeft : true,
32532     /**
32533      * @cfg {Boolean} isOriginTop = top align?
32534      */   
32535     isOriginTop : false,
32536     /**
32537      * @cfg {Boolean} isLayoutInstant = no animation?
32538      */   
32539     isLayoutInstant : false, // needed?
32540     /**
32541      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32542      */   
32543     isResizingContainer : true,
32544     /**
32545      * @cfg {Number} columnWidth  width of the columns 
32546      */   
32547     
32548     columnWidth : 0,
32549     
32550     /**
32551      * @cfg {Number} maxCols maximum number of columns
32552      */   
32553     
32554     maxCols: 0,
32555     /**
32556      * @cfg {Number} padHeight padding below box..
32557      */   
32558     
32559     padHeight : 10, 
32560     
32561     /**
32562      * @cfg {Boolean} isAutoInitial defalut true
32563      */   
32564     
32565     isAutoInitial : true, 
32566     
32567     // private?
32568     gutter : 0,
32569     
32570     containerWidth: 0,
32571     initialColumnWidth : 0,
32572     currentSize : null,
32573     
32574     colYs : null, // array.
32575     maxY : 0,
32576     padWidth: 10,
32577     
32578     
32579     tag: 'div',
32580     cls: '',
32581     bricks: null, //CompositeElement
32582     cols : 0, // array?
32583     // element : null, // wrapped now this.el
32584     _isLayoutInited : null, 
32585     
32586     
32587     getAutoCreate : function(){
32588         
32589         var cfg = {
32590             tag: this.tag,
32591             cls: 'blog-masonary-wrapper ' + this.cls,
32592             cn : {
32593                 cls : 'mas-boxes masonary'
32594             }
32595         };
32596         
32597         return cfg;
32598     },
32599     
32600     getChildContainer: function( )
32601     {
32602         if (this.boxesEl) {
32603             return this.boxesEl;
32604         }
32605         
32606         this.boxesEl = this.el.select('.mas-boxes').first();
32607         
32608         return this.boxesEl;
32609     },
32610     
32611     
32612     initEvents : function()
32613     {
32614         var _this = this;
32615         
32616         if(this.isAutoInitial){
32617             Roo.log('hook children rendered');
32618             this.on('childrenrendered', function() {
32619                 Roo.log('children rendered');
32620                 _this.initial();
32621             } ,this);
32622         }
32623         
32624     },
32625     
32626     initial : function()
32627     {
32628         this.reloadItems();
32629
32630         this.currentSize = this.el.getBox(true);
32631
32632         /// was window resize... - let's see if this works..
32633         Roo.EventManager.onWindowResize(this.resize, this); 
32634
32635         if(!this.isAutoInitial){
32636             this.layout();
32637             return;
32638         }
32639         
32640         this.layout.defer(500,this);
32641     },
32642     
32643     reloadItems: function()
32644     {
32645         this.bricks = this.el.select('.masonry-brick', true);
32646         
32647         this.bricks.each(function(b) {
32648             //Roo.log(b.getSize());
32649             if (!b.attr('originalwidth')) {
32650                 b.attr('originalwidth',  b.getSize().width);
32651             }
32652             
32653         });
32654         
32655         Roo.log(this.bricks.elements.length);
32656     },
32657     
32658     resize : function()
32659     {
32660         Roo.log('resize');
32661         var cs = this.el.getBox(true);
32662         
32663         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32664             Roo.log("no change in with or X");
32665             return;
32666         }
32667         this.currentSize = cs;
32668         this.layout();
32669     },
32670     
32671     layout : function()
32672     {
32673          Roo.log('layout');
32674         this._resetLayout();
32675         //this._manageStamps();
32676       
32677         // don't animate first layout
32678         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32679         this.layoutItems( isInstant );
32680       
32681         // flag for initalized
32682         this._isLayoutInited = true;
32683     },
32684     
32685     layoutItems : function( isInstant )
32686     {
32687         //var items = this._getItemsForLayout( this.items );
32688         // original code supports filtering layout items.. we just ignore it..
32689         
32690         this._layoutItems( this.bricks , isInstant );
32691       
32692         this._postLayout();
32693     },
32694     _layoutItems : function ( items , isInstant)
32695     {
32696        //this.fireEvent( 'layout', this, items );
32697     
32698
32699         if ( !items || !items.elements.length ) {
32700           // no items, emit event with empty array
32701             return;
32702         }
32703
32704         var queue = [];
32705         items.each(function(item) {
32706             Roo.log("layout item");
32707             Roo.log(item);
32708             // get x/y object from method
32709             var position = this._getItemLayoutPosition( item );
32710             // enqueue
32711             position.item = item;
32712             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32713             queue.push( position );
32714         }, this);
32715       
32716         this._processLayoutQueue( queue );
32717     },
32718     /** Sets position of item in DOM
32719     * @param {Element} item
32720     * @param {Number} x - horizontal position
32721     * @param {Number} y - vertical position
32722     * @param {Boolean} isInstant - disables transitions
32723     */
32724     _processLayoutQueue : function( queue )
32725     {
32726         for ( var i=0, len = queue.length; i < len; i++ ) {
32727             var obj = queue[i];
32728             obj.item.position('absolute');
32729             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32730         }
32731     },
32732       
32733     
32734     /**
32735     * Any logic you want to do after each layout,
32736     * i.e. size the container
32737     */
32738     _postLayout : function()
32739     {
32740         this.resizeContainer();
32741     },
32742     
32743     resizeContainer : function()
32744     {
32745         if ( !this.isResizingContainer ) {
32746             return;
32747         }
32748         var size = this._getContainerSize();
32749         if ( size ) {
32750             this.el.setSize(size.width,size.height);
32751             this.boxesEl.setSize(size.width,size.height);
32752         }
32753     },
32754     
32755     
32756     
32757     _resetLayout : function()
32758     {
32759         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32760         this.colWidth = this.el.getWidth();
32761         //this.gutter = this.el.getWidth(); 
32762         
32763         this.measureColumns();
32764
32765         // reset column Y
32766         var i = this.cols;
32767         this.colYs = [];
32768         while (i--) {
32769             this.colYs.push( 0 );
32770         }
32771     
32772         this.maxY = 0;
32773     },
32774
32775     measureColumns : function()
32776     {
32777         this.getContainerWidth();
32778       // if columnWidth is 0, default to outerWidth of first item
32779         if ( !this.columnWidth ) {
32780             var firstItem = this.bricks.first();
32781             Roo.log(firstItem);
32782             this.columnWidth  = this.containerWidth;
32783             if (firstItem && firstItem.attr('originalwidth') ) {
32784                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32785             }
32786             // columnWidth fall back to item of first element
32787             Roo.log("set column width?");
32788                         this.initialColumnWidth = this.columnWidth  ;
32789
32790             // if first elem has no width, default to size of container
32791             
32792         }
32793         
32794         
32795         if (this.initialColumnWidth) {
32796             this.columnWidth = this.initialColumnWidth;
32797         }
32798         
32799         
32800             
32801         // column width is fixed at the top - however if container width get's smaller we should
32802         // reduce it...
32803         
32804         // this bit calcs how man columns..
32805             
32806         var columnWidth = this.columnWidth += this.gutter;
32807       
32808         // calculate columns
32809         var containerWidth = this.containerWidth + this.gutter;
32810         
32811         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32812         // fix rounding errors, typically with gutters
32813         var excess = columnWidth - containerWidth % columnWidth;
32814         
32815         
32816         // if overshoot is less than a pixel, round up, otherwise floor it
32817         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32818         cols = Math[ mathMethod ]( cols );
32819         this.cols = Math.max( cols, 1 );
32820         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32821         
32822          // padding positioning..
32823         var totalColWidth = this.cols * this.columnWidth;
32824         var padavail = this.containerWidth - totalColWidth;
32825         // so for 2 columns - we need 3 'pads'
32826         
32827         var padNeeded = (1+this.cols) * this.padWidth;
32828         
32829         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32830         
32831         this.columnWidth += padExtra
32832         //this.padWidth = Math.floor(padavail /  ( this.cols));
32833         
32834         // adjust colum width so that padding is fixed??
32835         
32836         // we have 3 columns ... total = width * 3
32837         // we have X left over... that should be used by 
32838         
32839         //if (this.expandC) {
32840             
32841         //}
32842         
32843         
32844         
32845     },
32846     
32847     getContainerWidth : function()
32848     {
32849        /* // container is parent if fit width
32850         var container = this.isFitWidth ? this.element.parentNode : this.element;
32851         // check that this.size and size are there
32852         // IE8 triggers resize on body size change, so they might not be
32853         
32854         var size = getSize( container );  //FIXME
32855         this.containerWidth = size && size.innerWidth; //FIXME
32856         */
32857          
32858         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32859         
32860     },
32861     
32862     _getItemLayoutPosition : function( item )  // what is item?
32863     {
32864         // we resize the item to our columnWidth..
32865       
32866         item.setWidth(this.columnWidth);
32867         item.autoBoxAdjust  = false;
32868         
32869         var sz = item.getSize();
32870  
32871         // how many columns does this brick span
32872         var remainder = this.containerWidth % this.columnWidth;
32873         
32874         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32875         // round if off by 1 pixel, otherwise use ceil
32876         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32877         colSpan = Math.min( colSpan, this.cols );
32878         
32879         // normally this should be '1' as we dont' currently allow multi width columns..
32880         
32881         var colGroup = this._getColGroup( colSpan );
32882         // get the minimum Y value from the columns
32883         var minimumY = Math.min.apply( Math, colGroup );
32884         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32885         
32886         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32887          
32888         // position the brick
32889         var position = {
32890             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32891             y: this.currentSize.y + minimumY + this.padHeight
32892         };
32893         
32894         Roo.log(position);
32895         // apply setHeight to necessary columns
32896         var setHeight = minimumY + sz.height + this.padHeight;
32897         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32898         
32899         var setSpan = this.cols + 1 - colGroup.length;
32900         for ( var i = 0; i < setSpan; i++ ) {
32901           this.colYs[ shortColIndex + i ] = setHeight ;
32902         }
32903       
32904         return position;
32905     },
32906     
32907     /**
32908      * @param {Number} colSpan - number of columns the element spans
32909      * @returns {Array} colGroup
32910      */
32911     _getColGroup : function( colSpan )
32912     {
32913         if ( colSpan < 2 ) {
32914           // if brick spans only one column, use all the column Ys
32915           return this.colYs;
32916         }
32917       
32918         var colGroup = [];
32919         // how many different places could this brick fit horizontally
32920         var groupCount = this.cols + 1 - colSpan;
32921         // for each group potential horizontal position
32922         for ( var i = 0; i < groupCount; i++ ) {
32923           // make an array of colY values for that one group
32924           var groupColYs = this.colYs.slice( i, i + colSpan );
32925           // and get the max value of the array
32926           colGroup[i] = Math.max.apply( Math, groupColYs );
32927         }
32928         return colGroup;
32929     },
32930     /*
32931     _manageStamp : function( stamp )
32932     {
32933         var stampSize =  stamp.getSize();
32934         var offset = stamp.getBox();
32935         // get the columns that this stamp affects
32936         var firstX = this.isOriginLeft ? offset.x : offset.right;
32937         var lastX = firstX + stampSize.width;
32938         var firstCol = Math.floor( firstX / this.columnWidth );
32939         firstCol = Math.max( 0, firstCol );
32940         
32941         var lastCol = Math.floor( lastX / this.columnWidth );
32942         // lastCol should not go over if multiple of columnWidth #425
32943         lastCol -= lastX % this.columnWidth ? 0 : 1;
32944         lastCol = Math.min( this.cols - 1, lastCol );
32945         
32946         // set colYs to bottom of the stamp
32947         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32948             stampSize.height;
32949             
32950         for ( var i = firstCol; i <= lastCol; i++ ) {
32951           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32952         }
32953     },
32954     */
32955     
32956     _getContainerSize : function()
32957     {
32958         this.maxY = Math.max.apply( Math, this.colYs );
32959         var size = {
32960             height: this.maxY
32961         };
32962       
32963         if ( this.isFitWidth ) {
32964             size.width = this._getContainerFitWidth();
32965         }
32966       
32967         return size;
32968     },
32969     
32970     _getContainerFitWidth : function()
32971     {
32972         var unusedCols = 0;
32973         // count unused columns
32974         var i = this.cols;
32975         while ( --i ) {
32976           if ( this.colYs[i] !== 0 ) {
32977             break;
32978           }
32979           unusedCols++;
32980         }
32981         // fit container to columns that have been used
32982         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32983     },
32984     
32985     needsResizeLayout : function()
32986     {
32987         var previousWidth = this.containerWidth;
32988         this.getContainerWidth();
32989         return previousWidth !== this.containerWidth;
32990     }
32991  
32992 });
32993
32994  
32995
32996  /*
32997  * - LGPL
32998  *
32999  * element
33000  * 
33001  */
33002
33003 /**
33004  * @class Roo.bootstrap.MasonryBrick
33005  * @extends Roo.bootstrap.Component
33006  * Bootstrap MasonryBrick class
33007  * 
33008  * @constructor
33009  * Create a new MasonryBrick
33010  * @param {Object} config The config object
33011  */
33012
33013 Roo.bootstrap.MasonryBrick = function(config){
33014     
33015     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33016     
33017     Roo.bootstrap.MasonryBrick.register(this);
33018     
33019     this.addEvents({
33020         // raw events
33021         /**
33022          * @event click
33023          * When a MasonryBrick is clcik
33024          * @param {Roo.bootstrap.MasonryBrick} this
33025          * @param {Roo.EventObject} e
33026          */
33027         "click" : true
33028     });
33029 };
33030
33031 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
33032     
33033     /**
33034      * @cfg {String} title
33035      */   
33036     title : '',
33037     /**
33038      * @cfg {String} html
33039      */   
33040     html : '',
33041     /**
33042      * @cfg {String} bgimage
33043      */   
33044     bgimage : '',
33045     /**
33046      * @cfg {String} videourl
33047      */   
33048     videourl : '',
33049     /**
33050      * @cfg {String} cls
33051      */   
33052     cls : '',
33053     /**
33054      * @cfg {String} href
33055      */   
33056     href : '',
33057     /**
33058      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33059      */   
33060     size : 'xs',
33061     
33062     /**
33063      * @cfg {String} placetitle (center|bottom)
33064      */   
33065     placetitle : '',
33066     
33067     /**
33068      * @cfg {Boolean} isFitContainer defalut true
33069      */   
33070     isFitContainer : true, 
33071     
33072     /**
33073      * @cfg {Boolean} preventDefault defalut false
33074      */   
33075     preventDefault : false, 
33076     
33077     /**
33078      * @cfg {Boolean} inverse defalut false
33079      */   
33080     maskInverse : false, 
33081     
33082     getAutoCreate : function()
33083     {
33084         if(!this.isFitContainer){
33085             return this.getSplitAutoCreate();
33086         }
33087         
33088         var cls = 'masonry-brick masonry-brick-full';
33089         
33090         if(this.href.length){
33091             cls += ' masonry-brick-link';
33092         }
33093         
33094         if(this.bgimage.length){
33095             cls += ' masonry-brick-image';
33096         }
33097         
33098         if(this.maskInverse){
33099             cls += ' mask-inverse';
33100         }
33101         
33102         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33103             cls += ' enable-mask';
33104         }
33105         
33106         if(this.size){
33107             cls += ' masonry-' + this.size + '-brick';
33108         }
33109         
33110         if(this.placetitle.length){
33111             
33112             switch (this.placetitle) {
33113                 case 'center' :
33114                     cls += ' masonry-center-title';
33115                     break;
33116                 case 'bottom' :
33117                     cls += ' masonry-bottom-title';
33118                     break;
33119                 default:
33120                     break;
33121             }
33122             
33123         } else {
33124             if(!this.html.length && !this.bgimage.length){
33125                 cls += ' masonry-center-title';
33126             }
33127
33128             if(!this.html.length && this.bgimage.length){
33129                 cls += ' masonry-bottom-title';
33130             }
33131         }
33132         
33133         if(this.cls){
33134             cls += ' ' + this.cls;
33135         }
33136         
33137         var cfg = {
33138             tag: (this.href.length) ? 'a' : 'div',
33139             cls: cls,
33140             cn: [
33141                 {
33142                     tag: 'div',
33143                     cls: 'masonry-brick-mask'
33144                 },
33145                 {
33146                     tag: 'div',
33147                     cls: 'masonry-brick-paragraph',
33148                     cn: []
33149                 }
33150             ]
33151         };
33152         
33153         if(this.href.length){
33154             cfg.href = this.href;
33155         }
33156         
33157         var cn = cfg.cn[1].cn;
33158         
33159         if(this.title.length){
33160             cn.push({
33161                 tag: 'h4',
33162                 cls: 'masonry-brick-title',
33163                 html: this.title
33164             });
33165         }
33166         
33167         if(this.html.length){
33168             cn.push({
33169                 tag: 'p',
33170                 cls: 'masonry-brick-text',
33171                 html: this.html
33172             });
33173         }
33174         
33175         if (!this.title.length && !this.html.length) {
33176             cfg.cn[1].cls += ' hide';
33177         }
33178         
33179         if(this.bgimage.length){
33180             cfg.cn.push({
33181                 tag: 'img',
33182                 cls: 'masonry-brick-image-view',
33183                 src: this.bgimage
33184             });
33185         }
33186         
33187         if(this.videourl.length){
33188             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33189             // youtube support only?
33190             cfg.cn.push({
33191                 tag: 'iframe',
33192                 cls: 'masonry-brick-image-view',
33193                 src: vurl,
33194                 frameborder : 0,
33195                 allowfullscreen : true
33196             });
33197         }
33198         
33199         return cfg;
33200         
33201     },
33202     
33203     getSplitAutoCreate : function()
33204     {
33205         var cls = 'masonry-brick masonry-brick-split';
33206         
33207         if(this.href.length){
33208             cls += ' masonry-brick-link';
33209         }
33210         
33211         if(this.bgimage.length){
33212             cls += ' masonry-brick-image';
33213         }
33214         
33215         if(this.size){
33216             cls += ' masonry-' + this.size + '-brick';
33217         }
33218         
33219         switch (this.placetitle) {
33220             case 'center' :
33221                 cls += ' masonry-center-title';
33222                 break;
33223             case 'bottom' :
33224                 cls += ' masonry-bottom-title';
33225                 break;
33226             default:
33227                 if(!this.bgimage.length){
33228                     cls += ' masonry-center-title';
33229                 }
33230
33231                 if(this.bgimage.length){
33232                     cls += ' masonry-bottom-title';
33233                 }
33234                 break;
33235         }
33236         
33237         if(this.cls){
33238             cls += ' ' + this.cls;
33239         }
33240         
33241         var cfg = {
33242             tag: (this.href.length) ? 'a' : 'div',
33243             cls: cls,
33244             cn: [
33245                 {
33246                     tag: 'div',
33247                     cls: 'masonry-brick-split-head',
33248                     cn: [
33249                         {
33250                             tag: 'div',
33251                             cls: 'masonry-brick-paragraph',
33252                             cn: []
33253                         }
33254                     ]
33255                 },
33256                 {
33257                     tag: 'div',
33258                     cls: 'masonry-brick-split-body',
33259                     cn: []
33260                 }
33261             ]
33262         };
33263         
33264         if(this.href.length){
33265             cfg.href = this.href;
33266         }
33267         
33268         if(this.title.length){
33269             cfg.cn[0].cn[0].cn.push({
33270                 tag: 'h4',
33271                 cls: 'masonry-brick-title',
33272                 html: this.title
33273             });
33274         }
33275         
33276         if(this.html.length){
33277             cfg.cn[1].cn.push({
33278                 tag: 'p',
33279                 cls: 'masonry-brick-text',
33280                 html: this.html
33281             });
33282         }
33283
33284         if(this.bgimage.length){
33285             cfg.cn[0].cn.push({
33286                 tag: 'img',
33287                 cls: 'masonry-brick-image-view',
33288                 src: this.bgimage
33289             });
33290         }
33291         
33292         if(this.videourl.length){
33293             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33294             // youtube support only?
33295             cfg.cn[0].cn.cn.push({
33296                 tag: 'iframe',
33297                 cls: 'masonry-brick-image-view',
33298                 src: vurl,
33299                 frameborder : 0,
33300                 allowfullscreen : true
33301             });
33302         }
33303         
33304         return cfg;
33305     },
33306     
33307     initEvents: function() 
33308     {
33309         switch (this.size) {
33310             case 'xs' :
33311                 this.x = 1;
33312                 this.y = 1;
33313                 break;
33314             case 'sm' :
33315                 this.x = 2;
33316                 this.y = 2;
33317                 break;
33318             case 'md' :
33319             case 'md-left' :
33320             case 'md-right' :
33321                 this.x = 3;
33322                 this.y = 3;
33323                 break;
33324             case 'tall' :
33325                 this.x = 2;
33326                 this.y = 3;
33327                 break;
33328             case 'wide' :
33329                 this.x = 3;
33330                 this.y = 2;
33331                 break;
33332             case 'wide-thin' :
33333                 this.x = 3;
33334                 this.y = 1;
33335                 break;
33336                         
33337             default :
33338                 break;
33339         }
33340         
33341         if(Roo.isTouch){
33342             this.el.on('touchstart', this.onTouchStart, this);
33343             this.el.on('touchmove', this.onTouchMove, this);
33344             this.el.on('touchend', this.onTouchEnd, this);
33345             this.el.on('contextmenu', this.onContextMenu, this);
33346         } else {
33347             this.el.on('mouseenter'  ,this.enter, this);
33348             this.el.on('mouseleave', this.leave, this);
33349             this.el.on('click', this.onClick, this);
33350         }
33351         
33352         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33353             this.parent().bricks.push(this);   
33354         }
33355         
33356     },
33357     
33358     onClick: function(e, el)
33359     {
33360         var time = this.endTimer - this.startTimer;
33361         // Roo.log(e.preventDefault());
33362         if(Roo.isTouch){
33363             if(time > 1000){
33364                 e.preventDefault();
33365                 return;
33366             }
33367         }
33368         
33369         if(!this.preventDefault){
33370             return;
33371         }
33372         
33373         e.preventDefault();
33374         
33375         if (this.activeClass != '') {
33376             this.selectBrick();
33377         }
33378         
33379         this.fireEvent('click', this, e);
33380     },
33381     
33382     enter: function(e, el)
33383     {
33384         e.preventDefault();
33385         
33386         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33387             return;
33388         }
33389         
33390         if(this.bgimage.length && this.html.length){
33391             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33392         }
33393     },
33394     
33395     leave: function(e, el)
33396     {
33397         e.preventDefault();
33398         
33399         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33400             return;
33401         }
33402         
33403         if(this.bgimage.length && this.html.length){
33404             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33405         }
33406     },
33407     
33408     onTouchStart: function(e, el)
33409     {
33410 //        e.preventDefault();
33411         
33412         this.touchmoved = false;
33413         
33414         if(!this.isFitContainer){
33415             return;
33416         }
33417         
33418         if(!this.bgimage.length || !this.html.length){
33419             return;
33420         }
33421         
33422         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33423         
33424         this.timer = new Date().getTime();
33425         
33426     },
33427     
33428     onTouchMove: function(e, el)
33429     {
33430         this.touchmoved = true;
33431     },
33432     
33433     onContextMenu : function(e,el)
33434     {
33435         e.preventDefault();
33436         e.stopPropagation();
33437         return false;
33438     },
33439     
33440     onTouchEnd: function(e, el)
33441     {
33442 //        e.preventDefault();
33443         
33444         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33445         
33446             this.leave(e,el);
33447             
33448             return;
33449         }
33450         
33451         if(!this.bgimage.length || !this.html.length){
33452             
33453             if(this.href.length){
33454                 window.location.href = this.href;
33455             }
33456             
33457             return;
33458         }
33459         
33460         if(!this.isFitContainer){
33461             return;
33462         }
33463         
33464         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33465         
33466         window.location.href = this.href;
33467     },
33468     
33469     //selection on single brick only
33470     selectBrick : function() {
33471         
33472         if (!this.parentId) {
33473             return;
33474         }
33475         
33476         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33477         var index = m.selectedBrick.indexOf(this.id);
33478         
33479         if ( index > -1) {
33480             m.selectedBrick.splice(index,1);
33481             this.el.removeClass(this.activeClass);
33482             return;
33483         }
33484         
33485         for(var i = 0; i < m.selectedBrick.length; i++) {
33486             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33487             b.el.removeClass(b.activeClass);
33488         }
33489         
33490         m.selectedBrick = [];
33491         
33492         m.selectedBrick.push(this.id);
33493         this.el.addClass(this.activeClass);
33494         return;
33495     },
33496     
33497     isSelected : function(){
33498         return this.el.hasClass(this.activeClass);
33499         
33500     }
33501 });
33502
33503 Roo.apply(Roo.bootstrap.MasonryBrick, {
33504     
33505     //groups: {},
33506     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33507      /**
33508     * register a Masonry Brick
33509     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33510     */
33511     
33512     register : function(brick)
33513     {
33514         //this.groups[brick.id] = brick;
33515         this.groups.add(brick.id, brick);
33516     },
33517     /**
33518     * fetch a  masonry brick based on the masonry brick ID
33519     * @param {string} the masonry brick to add
33520     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33521     */
33522     
33523     get: function(brick_id) 
33524     {
33525         // if (typeof(this.groups[brick_id]) == 'undefined') {
33526         //     return false;
33527         // }
33528         // return this.groups[brick_id] ;
33529         
33530         if(this.groups.key(brick_id)) {
33531             return this.groups.key(brick_id);
33532         }
33533         
33534         return false;
33535     }
33536     
33537     
33538     
33539 });
33540
33541  /*
33542  * - LGPL
33543  *
33544  * element
33545  * 
33546  */
33547
33548 /**
33549  * @class Roo.bootstrap.Brick
33550  * @extends Roo.bootstrap.Component
33551  * Bootstrap Brick class
33552  * 
33553  * @constructor
33554  * Create a new Brick
33555  * @param {Object} config The config object
33556  */
33557
33558 Roo.bootstrap.Brick = function(config){
33559     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33560     
33561     this.addEvents({
33562         // raw events
33563         /**
33564          * @event click
33565          * When a Brick is click
33566          * @param {Roo.bootstrap.Brick} this
33567          * @param {Roo.EventObject} e
33568          */
33569         "click" : true
33570     });
33571 };
33572
33573 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33574     
33575     /**
33576      * @cfg {String} title
33577      */   
33578     title : '',
33579     /**
33580      * @cfg {String} html
33581      */   
33582     html : '',
33583     /**
33584      * @cfg {String} bgimage
33585      */   
33586     bgimage : '',
33587     /**
33588      * @cfg {String} cls
33589      */   
33590     cls : '',
33591     /**
33592      * @cfg {String} href
33593      */   
33594     href : '',
33595     /**
33596      * @cfg {String} video
33597      */   
33598     video : '',
33599     /**
33600      * @cfg {Boolean} square
33601      */   
33602     square : true,
33603     
33604     getAutoCreate : function()
33605     {
33606         var cls = 'roo-brick';
33607         
33608         if(this.href.length){
33609             cls += ' roo-brick-link';
33610         }
33611         
33612         if(this.bgimage.length){
33613             cls += ' roo-brick-image';
33614         }
33615         
33616         if(!this.html.length && !this.bgimage.length){
33617             cls += ' roo-brick-center-title';
33618         }
33619         
33620         if(!this.html.length && this.bgimage.length){
33621             cls += ' roo-brick-bottom-title';
33622         }
33623         
33624         if(this.cls){
33625             cls += ' ' + this.cls;
33626         }
33627         
33628         var cfg = {
33629             tag: (this.href.length) ? 'a' : 'div',
33630             cls: cls,
33631             cn: [
33632                 {
33633                     tag: 'div',
33634                     cls: 'roo-brick-paragraph',
33635                     cn: []
33636                 }
33637             ]
33638         };
33639         
33640         if(this.href.length){
33641             cfg.href = this.href;
33642         }
33643         
33644         var cn = cfg.cn[0].cn;
33645         
33646         if(this.title.length){
33647             cn.push({
33648                 tag: 'h4',
33649                 cls: 'roo-brick-title',
33650                 html: this.title
33651             });
33652         }
33653         
33654         if(this.html.length){
33655             cn.push({
33656                 tag: 'p',
33657                 cls: 'roo-brick-text',
33658                 html: this.html
33659             });
33660         } else {
33661             cn.cls += ' hide';
33662         }
33663         
33664         if(this.bgimage.length){
33665             cfg.cn.push({
33666                 tag: 'img',
33667                 cls: 'roo-brick-image-view',
33668                 src: this.bgimage
33669             });
33670         }
33671         
33672         return cfg;
33673     },
33674     
33675     initEvents: function() 
33676     {
33677         if(this.title.length || this.html.length){
33678             this.el.on('mouseenter'  ,this.enter, this);
33679             this.el.on('mouseleave', this.leave, this);
33680         }
33681         
33682         Roo.EventManager.onWindowResize(this.resize, this); 
33683         
33684         if(this.bgimage.length){
33685             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33686             this.imageEl.on('load', this.onImageLoad, this);
33687             return;
33688         }
33689         
33690         this.resize();
33691     },
33692     
33693     onImageLoad : function()
33694     {
33695         this.resize();
33696     },
33697     
33698     resize : function()
33699     {
33700         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33701         
33702         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33703         
33704         if(this.bgimage.length){
33705             var image = this.el.select('.roo-brick-image-view', true).first();
33706             
33707             image.setWidth(paragraph.getWidth());
33708             
33709             if(this.square){
33710                 image.setHeight(paragraph.getWidth());
33711             }
33712             
33713             this.el.setHeight(image.getHeight());
33714             paragraph.setHeight(image.getHeight());
33715             
33716         }
33717         
33718     },
33719     
33720     enter: function(e, el)
33721     {
33722         e.preventDefault();
33723         
33724         if(this.bgimage.length){
33725             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33726             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33727         }
33728     },
33729     
33730     leave: function(e, el)
33731     {
33732         e.preventDefault();
33733         
33734         if(this.bgimage.length){
33735             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33736             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33737         }
33738     }
33739     
33740 });
33741
33742  
33743
33744  /*
33745  * - LGPL
33746  *
33747  * Number field 
33748  */
33749
33750 /**
33751  * @class Roo.bootstrap.NumberField
33752  * @extends Roo.bootstrap.Input
33753  * Bootstrap NumberField class
33754  * 
33755  * 
33756  * 
33757  * 
33758  * @constructor
33759  * Create a new NumberField
33760  * @param {Object} config The config object
33761  */
33762
33763 Roo.bootstrap.NumberField = function(config){
33764     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33765 };
33766
33767 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33768     
33769     /**
33770      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33771      */
33772     allowDecimals : true,
33773     /**
33774      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33775      */
33776     decimalSeparator : ".",
33777     /**
33778      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33779      */
33780     decimalPrecision : 2,
33781     /**
33782      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33783      */
33784     allowNegative : true,
33785     
33786     /**
33787      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33788      */
33789     allowZero: true,
33790     /**
33791      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33792      */
33793     minValue : Number.NEGATIVE_INFINITY,
33794     /**
33795      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33796      */
33797     maxValue : Number.MAX_VALUE,
33798     /**
33799      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33800      */
33801     minText : "The minimum value for this field is {0}",
33802     /**
33803      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33804      */
33805     maxText : "The maximum value for this field is {0}",
33806     /**
33807      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33808      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33809      */
33810     nanText : "{0} is not a valid number",
33811     /**
33812      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33813      */
33814     thousandsDelimiter : false,
33815     /**
33816      * @cfg {String} valueAlign alignment of value
33817      */
33818     valueAlign : "left",
33819
33820     getAutoCreate : function()
33821     {
33822         var hiddenInput = {
33823             tag: 'input',
33824             type: 'hidden',
33825             id: Roo.id(),
33826             cls: 'hidden-number-input'
33827         };
33828         
33829         if (this.name) {
33830             hiddenInput.name = this.name;
33831         }
33832         
33833         this.name = '';
33834         
33835         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33836         
33837         this.name = hiddenInput.name;
33838         
33839         if(cfg.cn.length > 0) {
33840             cfg.cn.push(hiddenInput);
33841         }
33842         
33843         return cfg;
33844     },
33845
33846     // private
33847     initEvents : function()
33848     {   
33849         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33850         
33851         var allowed = "0123456789";
33852         
33853         if(this.allowDecimals){
33854             allowed += this.decimalSeparator;
33855         }
33856         
33857         if(this.allowNegative){
33858             allowed += "-";
33859         }
33860         
33861         if(this.thousandsDelimiter) {
33862             allowed += ",";
33863         }
33864         
33865         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33866         
33867         var keyPress = function(e){
33868             
33869             var k = e.getKey();
33870             
33871             var c = e.getCharCode();
33872             
33873             if(
33874                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33875                     allowed.indexOf(String.fromCharCode(c)) === -1
33876             ){
33877                 e.stopEvent();
33878                 return;
33879             }
33880             
33881             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33882                 return;
33883             }
33884             
33885             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33886                 e.stopEvent();
33887             }
33888         };
33889         
33890         this.el.on("keypress", keyPress, this);
33891     },
33892     
33893     validateValue : function(value)
33894     {
33895         
33896         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33897             return false;
33898         }
33899         
33900         var num = this.parseValue(value);
33901         
33902         if(isNaN(num)){
33903             this.markInvalid(String.format(this.nanText, value));
33904             return false;
33905         }
33906         
33907         if(num < this.minValue){
33908             this.markInvalid(String.format(this.minText, this.minValue));
33909             return false;
33910         }
33911         
33912         if(num > this.maxValue){
33913             this.markInvalid(String.format(this.maxText, this.maxValue));
33914             return false;
33915         }
33916         
33917         return true;
33918     },
33919
33920     getValue : function()
33921     {
33922         var v = this.hiddenEl().getValue();
33923         
33924         return this.fixPrecision(this.parseValue(v));
33925     },
33926
33927     parseValue : function(value)
33928     {
33929         if(this.thousandsDelimiter) {
33930             value += "";
33931             r = new RegExp(",", "g");
33932             value = value.replace(r, "");
33933         }
33934         
33935         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33936         return isNaN(value) ? '' : value;
33937     },
33938
33939     fixPrecision : function(value)
33940     {
33941         if(this.thousandsDelimiter) {
33942             value += "";
33943             r = new RegExp(",", "g");
33944             value = value.replace(r, "");
33945         }
33946         
33947         var nan = isNaN(value);
33948         
33949         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33950             return nan ? '' : value;
33951         }
33952         return parseFloat(value).toFixed(this.decimalPrecision);
33953     },
33954
33955     setValue : function(v)
33956     {
33957         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33958         
33959         this.value = v;
33960         
33961         if(this.rendered){
33962             
33963             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33964             
33965             this.inputEl().dom.value = (v == '') ? '' :
33966                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33967             
33968             if(!this.allowZero && v === '0') {
33969                 this.hiddenEl().dom.value = '';
33970                 this.inputEl().dom.value = '';
33971             }
33972             
33973             this.validate();
33974         }
33975     },
33976
33977     decimalPrecisionFcn : function(v)
33978     {
33979         return Math.floor(v);
33980     },
33981
33982     beforeBlur : function()
33983     {
33984         var v = this.parseValue(this.getRawValue());
33985         
33986         if(v || v === 0 || v === ''){
33987             this.setValue(v);
33988         }
33989     },
33990     
33991     hiddenEl : function()
33992     {
33993         return this.el.select('input.hidden-number-input',true).first();
33994     }
33995     
33996 });
33997
33998  
33999
34000 /*
34001 * Licence: LGPL
34002 */
34003
34004 /**
34005  * @class Roo.bootstrap.DocumentSlider
34006  * @extends Roo.bootstrap.Component
34007  * Bootstrap DocumentSlider class
34008  * 
34009  * @constructor
34010  * Create a new DocumentViewer
34011  * @param {Object} config The config object
34012  */
34013
34014 Roo.bootstrap.DocumentSlider = function(config){
34015     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34016     
34017     this.files = [];
34018     
34019     this.addEvents({
34020         /**
34021          * @event initial
34022          * Fire after initEvent
34023          * @param {Roo.bootstrap.DocumentSlider} this
34024          */
34025         "initial" : true,
34026         /**
34027          * @event update
34028          * Fire after update
34029          * @param {Roo.bootstrap.DocumentSlider} this
34030          */
34031         "update" : true,
34032         /**
34033          * @event click
34034          * Fire after click
34035          * @param {Roo.bootstrap.DocumentSlider} this
34036          */
34037         "click" : true
34038     });
34039 };
34040
34041 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
34042     
34043     files : false,
34044     
34045     indicator : 0,
34046     
34047     getAutoCreate : function()
34048     {
34049         var cfg = {
34050             tag : 'div',
34051             cls : 'roo-document-slider',
34052             cn : [
34053                 {
34054                     tag : 'div',
34055                     cls : 'roo-document-slider-header',
34056                     cn : [
34057                         {
34058                             tag : 'div',
34059                             cls : 'roo-document-slider-header-title'
34060                         }
34061                     ]
34062                 },
34063                 {
34064                     tag : 'div',
34065                     cls : 'roo-document-slider-body',
34066                     cn : [
34067                         {
34068                             tag : 'div',
34069                             cls : 'roo-document-slider-prev',
34070                             cn : [
34071                                 {
34072                                     tag : 'i',
34073                                     cls : 'fa fa-chevron-left'
34074                                 }
34075                             ]
34076                         },
34077                         {
34078                             tag : 'div',
34079                             cls : 'roo-document-slider-thumb',
34080                             cn : [
34081                                 {
34082                                     tag : 'img',
34083                                     cls : 'roo-document-slider-image'
34084                                 }
34085                             ]
34086                         },
34087                         {
34088                             tag : 'div',
34089                             cls : 'roo-document-slider-next',
34090                             cn : [
34091                                 {
34092                                     tag : 'i',
34093                                     cls : 'fa fa-chevron-right'
34094                                 }
34095                             ]
34096                         }
34097                     ]
34098                 }
34099             ]
34100         };
34101         
34102         return cfg;
34103     },
34104     
34105     initEvents : function()
34106     {
34107         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34108         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34109         
34110         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34111         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34112         
34113         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34114         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34115         
34116         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34117         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34118         
34119         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34120         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34121         
34122         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34123         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34124         
34125         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34126         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34127         
34128         this.thumbEl.on('click', this.onClick, this);
34129         
34130         this.prevIndicator.on('click', this.prev, this);
34131         
34132         this.nextIndicator.on('click', this.next, this);
34133         
34134     },
34135     
34136     initial : function()
34137     {
34138         if(this.files.length){
34139             this.indicator = 1;
34140             this.update()
34141         }
34142         
34143         this.fireEvent('initial', this);
34144     },
34145     
34146     update : function()
34147     {
34148         this.imageEl.attr('src', this.files[this.indicator - 1]);
34149         
34150         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34151         
34152         this.prevIndicator.show();
34153         
34154         if(this.indicator == 1){
34155             this.prevIndicator.hide();
34156         }
34157         
34158         this.nextIndicator.show();
34159         
34160         if(this.indicator == this.files.length){
34161             this.nextIndicator.hide();
34162         }
34163         
34164         this.thumbEl.scrollTo('top');
34165         
34166         this.fireEvent('update', this);
34167     },
34168     
34169     onClick : function(e)
34170     {
34171         e.preventDefault();
34172         
34173         this.fireEvent('click', this);
34174     },
34175     
34176     prev : function(e)
34177     {
34178         e.preventDefault();
34179         
34180         this.indicator = Math.max(1, this.indicator - 1);
34181         
34182         this.update();
34183     },
34184     
34185     next : function(e)
34186     {
34187         e.preventDefault();
34188         
34189         this.indicator = Math.min(this.files.length, this.indicator + 1);
34190         
34191         this.update();
34192     }
34193 });
34194 /*
34195  * - LGPL
34196  *
34197  * RadioSet
34198  *
34199  *
34200  */
34201
34202 /**
34203  * @class Roo.bootstrap.RadioSet
34204  * @extends Roo.bootstrap.Input
34205  * Bootstrap RadioSet class
34206  * @cfg {String} indicatorpos (left|right) default left
34207  * @cfg {Boolean} inline (true|false) inline the element (default true)
34208  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34209  * @constructor
34210  * Create a new RadioSet
34211  * @param {Object} config The config object
34212  */
34213
34214 Roo.bootstrap.RadioSet = function(config){
34215     
34216     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34217     
34218     this.radioes = [];
34219     
34220     Roo.bootstrap.RadioSet.register(this);
34221     
34222     this.addEvents({
34223         /**
34224         * @event check
34225         * Fires when the element is checked or unchecked.
34226         * @param {Roo.bootstrap.RadioSet} this This radio
34227         * @param {Roo.bootstrap.Radio} item The checked item
34228         */
34229        check : true,
34230        /**
34231         * @event click
34232         * Fires when the element is click.
34233         * @param {Roo.bootstrap.RadioSet} this This radio set
34234         * @param {Roo.bootstrap.Radio} item The checked item
34235         * @param {Roo.EventObject} e The event object
34236         */
34237        click : true
34238     });
34239     
34240 };
34241
34242 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34243
34244     radioes : false,
34245     
34246     inline : true,
34247     
34248     weight : '',
34249     
34250     indicatorpos : 'left',
34251     
34252     getAutoCreate : function()
34253     {
34254         var label = {
34255             tag : 'label',
34256             cls : 'roo-radio-set-label',
34257             cn : [
34258                 {
34259                     tag : 'span',
34260                     html : this.fieldLabel
34261                 }
34262             ]
34263         };
34264         if (Roo.bootstrap.version == 3) {
34265             
34266             
34267             if(this.indicatorpos == 'left'){
34268                 label.cn.unshift({
34269                     tag : 'i',
34270                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34271                     tooltip : 'This field is required'
34272                 });
34273             } else {
34274                 label.cn.push({
34275                     tag : 'i',
34276                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34277                     tooltip : 'This field is required'
34278                 });
34279             }
34280         }
34281         var items = {
34282             tag : 'div',
34283             cls : 'roo-radio-set-items'
34284         };
34285         
34286         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34287         
34288         if (align === 'left' && this.fieldLabel.length) {
34289             
34290             items = {
34291                 cls : "roo-radio-set-right", 
34292                 cn: [
34293                     items
34294                 ]
34295             };
34296             
34297             if(this.labelWidth > 12){
34298                 label.style = "width: " + this.labelWidth + 'px';
34299             }
34300             
34301             if(this.labelWidth < 13 && this.labelmd == 0){
34302                 this.labelmd = this.labelWidth;
34303             }
34304             
34305             if(this.labellg > 0){
34306                 label.cls += ' col-lg-' + this.labellg;
34307                 items.cls += ' col-lg-' + (12 - this.labellg);
34308             }
34309             
34310             if(this.labelmd > 0){
34311                 label.cls += ' col-md-' + this.labelmd;
34312                 items.cls += ' col-md-' + (12 - this.labelmd);
34313             }
34314             
34315             if(this.labelsm > 0){
34316                 label.cls += ' col-sm-' + this.labelsm;
34317                 items.cls += ' col-sm-' + (12 - this.labelsm);
34318             }
34319             
34320             if(this.labelxs > 0){
34321                 label.cls += ' col-xs-' + this.labelxs;
34322                 items.cls += ' col-xs-' + (12 - this.labelxs);
34323             }
34324         }
34325         
34326         var cfg = {
34327             tag : 'div',
34328             cls : 'roo-radio-set',
34329             cn : [
34330                 {
34331                     tag : 'input',
34332                     cls : 'roo-radio-set-input',
34333                     type : 'hidden',
34334                     name : this.name,
34335                     value : this.value ? this.value :  ''
34336                 },
34337                 label,
34338                 items
34339             ]
34340         };
34341         
34342         if(this.weight.length){
34343             cfg.cls += ' roo-radio-' + this.weight;
34344         }
34345         
34346         if(this.inline) {
34347             cfg.cls += ' roo-radio-set-inline';
34348         }
34349         
34350         var settings=this;
34351         ['xs','sm','md','lg'].map(function(size){
34352             if (settings[size]) {
34353                 cfg.cls += ' col-' + size + '-' + settings[size];
34354             }
34355         });
34356         
34357         return cfg;
34358         
34359     },
34360
34361     initEvents : function()
34362     {
34363         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34364         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34365         
34366         if(!this.fieldLabel.length){
34367             this.labelEl.hide();
34368         }
34369         
34370         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34371         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34372         
34373         this.indicator = this.indicatorEl();
34374         
34375         if(this.indicator){
34376             this.indicator.addClass('invisible');
34377         }
34378         
34379         this.originalValue = this.getValue();
34380         
34381     },
34382     
34383     inputEl: function ()
34384     {
34385         return this.el.select('.roo-radio-set-input', true).first();
34386     },
34387     
34388     getChildContainer : function()
34389     {
34390         return this.itemsEl;
34391     },
34392     
34393     register : function(item)
34394     {
34395         this.radioes.push(item);
34396         
34397     },
34398     
34399     validate : function()
34400     {   
34401         if(this.getVisibilityEl().hasClass('hidden')){
34402             return true;
34403         }
34404         
34405         var valid = false;
34406         
34407         Roo.each(this.radioes, function(i){
34408             if(!i.checked){
34409                 return;
34410             }
34411             
34412             valid = true;
34413             return false;
34414         });
34415         
34416         if(this.allowBlank) {
34417             return true;
34418         }
34419         
34420         if(this.disabled || valid){
34421             this.markValid();
34422             return true;
34423         }
34424         
34425         this.markInvalid();
34426         return false;
34427         
34428     },
34429     
34430     markValid : function()
34431     {
34432         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34433             this.indicatorEl().removeClass('visible');
34434             this.indicatorEl().addClass('invisible');
34435         }
34436         
34437         
34438         if (Roo.bootstrap.version == 3) {
34439             this.el.removeClass([this.invalidClass, this.validClass]);
34440             this.el.addClass(this.validClass);
34441         } else {
34442             this.el.removeClass(['is-invalid','is-valid']);
34443             this.el.addClass(['is-valid']);
34444         }
34445         this.fireEvent('valid', this);
34446     },
34447     
34448     markInvalid : function(msg)
34449     {
34450         if(this.allowBlank || this.disabled){
34451             return;
34452         }
34453         
34454         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34455             this.indicatorEl().removeClass('invisible');
34456             this.indicatorEl().addClass('visible');
34457         }
34458         if (Roo.bootstrap.version == 3) {
34459             this.el.removeClass([this.invalidClass, this.validClass]);
34460             this.el.addClass(this.invalidClass);
34461         } else {
34462             this.el.removeClass(['is-invalid','is-valid']);
34463             this.el.addClass(['is-invalid']);
34464         }
34465         
34466         this.fireEvent('invalid', this, msg);
34467         
34468     },
34469     
34470     setValue : function(v, suppressEvent)
34471     {   
34472         if(this.value === v){
34473             return;
34474         }
34475         
34476         this.value = v;
34477         
34478         if(this.rendered){
34479             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34480         }
34481         
34482         Roo.each(this.radioes, function(i){
34483             i.checked = false;
34484             i.el.removeClass('checked');
34485         });
34486         
34487         Roo.each(this.radioes, function(i){
34488             
34489             if(i.value === v || i.value.toString() === v.toString()){
34490                 i.checked = true;
34491                 i.el.addClass('checked');
34492                 
34493                 if(suppressEvent !== true){
34494                     this.fireEvent('check', this, i);
34495                 }
34496                 
34497                 return false;
34498             }
34499             
34500         }, this);
34501         
34502         this.validate();
34503     },
34504     
34505     clearInvalid : function(){
34506         
34507         if(!this.el || this.preventMark){
34508             return;
34509         }
34510         
34511         this.el.removeClass([this.invalidClass]);
34512         
34513         this.fireEvent('valid', this);
34514     }
34515     
34516 });
34517
34518 Roo.apply(Roo.bootstrap.RadioSet, {
34519     
34520     groups: {},
34521     
34522     register : function(set)
34523     {
34524         this.groups[set.name] = set;
34525     },
34526     
34527     get: function(name) 
34528     {
34529         if (typeof(this.groups[name]) == 'undefined') {
34530             return false;
34531         }
34532         
34533         return this.groups[name] ;
34534     }
34535     
34536 });
34537 /*
34538  * Based on:
34539  * Ext JS Library 1.1.1
34540  * Copyright(c) 2006-2007, Ext JS, LLC.
34541  *
34542  * Originally Released Under LGPL - original licence link has changed is not relivant.
34543  *
34544  * Fork - LGPL
34545  * <script type="text/javascript">
34546  */
34547
34548
34549 /**
34550  * @class Roo.bootstrap.SplitBar
34551  * @extends Roo.util.Observable
34552  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34553  * <br><br>
34554  * Usage:
34555  * <pre><code>
34556 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34557                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34558 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34559 split.minSize = 100;
34560 split.maxSize = 600;
34561 split.animate = true;
34562 split.on('moved', splitterMoved);
34563 </code></pre>
34564  * @constructor
34565  * Create a new SplitBar
34566  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34567  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34568  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34569  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34570                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34571                         position of the SplitBar).
34572  */
34573 Roo.bootstrap.SplitBar = function(cfg){
34574     
34575     /** @private */
34576     
34577     //{
34578     //  dragElement : elm
34579     //  resizingElement: el,
34580         // optional..
34581     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34582     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34583         // existingProxy ???
34584     //}
34585     
34586     this.el = Roo.get(cfg.dragElement, true);
34587     this.el.dom.unselectable = "on";
34588     /** @private */
34589     this.resizingEl = Roo.get(cfg.resizingElement, true);
34590
34591     /**
34592      * @private
34593      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34594      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34595      * @type Number
34596      */
34597     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34598     
34599     /**
34600      * The minimum size of the resizing element. (Defaults to 0)
34601      * @type Number
34602      */
34603     this.minSize = 0;
34604     
34605     /**
34606      * The maximum size of the resizing element. (Defaults to 2000)
34607      * @type Number
34608      */
34609     this.maxSize = 2000;
34610     
34611     /**
34612      * Whether to animate the transition to the new size
34613      * @type Boolean
34614      */
34615     this.animate = false;
34616     
34617     /**
34618      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34619      * @type Boolean
34620      */
34621     this.useShim = false;
34622     
34623     /** @private */
34624     this.shim = null;
34625     
34626     if(!cfg.existingProxy){
34627         /** @private */
34628         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34629     }else{
34630         this.proxy = Roo.get(cfg.existingProxy).dom;
34631     }
34632     /** @private */
34633     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34634     
34635     /** @private */
34636     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34637     
34638     /** @private */
34639     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34640     
34641     /** @private */
34642     this.dragSpecs = {};
34643     
34644     /**
34645      * @private The adapter to use to positon and resize elements
34646      */
34647     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34648     this.adapter.init(this);
34649     
34650     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34651         /** @private */
34652         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34653         this.el.addClass("roo-splitbar-h");
34654     }else{
34655         /** @private */
34656         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34657         this.el.addClass("roo-splitbar-v");
34658     }
34659     
34660     this.addEvents({
34661         /**
34662          * @event resize
34663          * Fires when the splitter is moved (alias for {@link #event-moved})
34664          * @param {Roo.bootstrap.SplitBar} this
34665          * @param {Number} newSize the new width or height
34666          */
34667         "resize" : true,
34668         /**
34669          * @event moved
34670          * Fires when the splitter is moved
34671          * @param {Roo.bootstrap.SplitBar} this
34672          * @param {Number} newSize the new width or height
34673          */
34674         "moved" : true,
34675         /**
34676          * @event beforeresize
34677          * Fires before the splitter is dragged
34678          * @param {Roo.bootstrap.SplitBar} this
34679          */
34680         "beforeresize" : true,
34681
34682         "beforeapply" : true
34683     });
34684
34685     Roo.util.Observable.call(this);
34686 };
34687
34688 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34689     onStartProxyDrag : function(x, y){
34690         this.fireEvent("beforeresize", this);
34691         if(!this.overlay){
34692             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34693             o.unselectable();
34694             o.enableDisplayMode("block");
34695             // all splitbars share the same overlay
34696             Roo.bootstrap.SplitBar.prototype.overlay = o;
34697         }
34698         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34699         this.overlay.show();
34700         Roo.get(this.proxy).setDisplayed("block");
34701         var size = this.adapter.getElementSize(this);
34702         this.activeMinSize = this.getMinimumSize();;
34703         this.activeMaxSize = this.getMaximumSize();;
34704         var c1 = size - this.activeMinSize;
34705         var c2 = Math.max(this.activeMaxSize - size, 0);
34706         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34707             this.dd.resetConstraints();
34708             this.dd.setXConstraint(
34709                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34710                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34711             );
34712             this.dd.setYConstraint(0, 0);
34713         }else{
34714             this.dd.resetConstraints();
34715             this.dd.setXConstraint(0, 0);
34716             this.dd.setYConstraint(
34717                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34718                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34719             );
34720          }
34721         this.dragSpecs.startSize = size;
34722         this.dragSpecs.startPoint = [x, y];
34723         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34724     },
34725     
34726     /** 
34727      * @private Called after the drag operation by the DDProxy
34728      */
34729     onEndProxyDrag : function(e){
34730         Roo.get(this.proxy).setDisplayed(false);
34731         var endPoint = Roo.lib.Event.getXY(e);
34732         if(this.overlay){
34733             this.overlay.hide();
34734         }
34735         var newSize;
34736         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34737             newSize = this.dragSpecs.startSize + 
34738                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34739                     endPoint[0] - this.dragSpecs.startPoint[0] :
34740                     this.dragSpecs.startPoint[0] - endPoint[0]
34741                 );
34742         }else{
34743             newSize = this.dragSpecs.startSize + 
34744                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34745                     endPoint[1] - this.dragSpecs.startPoint[1] :
34746                     this.dragSpecs.startPoint[1] - endPoint[1]
34747                 );
34748         }
34749         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34750         if(newSize != this.dragSpecs.startSize){
34751             if(this.fireEvent('beforeapply', this, newSize) !== false){
34752                 this.adapter.setElementSize(this, newSize);
34753                 this.fireEvent("moved", this, newSize);
34754                 this.fireEvent("resize", this, newSize);
34755             }
34756         }
34757     },
34758     
34759     /**
34760      * Get the adapter this SplitBar uses
34761      * @return The adapter object
34762      */
34763     getAdapter : function(){
34764         return this.adapter;
34765     },
34766     
34767     /**
34768      * Set the adapter this SplitBar uses
34769      * @param {Object} adapter A SplitBar adapter object
34770      */
34771     setAdapter : function(adapter){
34772         this.adapter = adapter;
34773         this.adapter.init(this);
34774     },
34775     
34776     /**
34777      * Gets the minimum size for the resizing element
34778      * @return {Number} The minimum size
34779      */
34780     getMinimumSize : function(){
34781         return this.minSize;
34782     },
34783     
34784     /**
34785      * Sets the minimum size for the resizing element
34786      * @param {Number} minSize The minimum size
34787      */
34788     setMinimumSize : function(minSize){
34789         this.minSize = minSize;
34790     },
34791     
34792     /**
34793      * Gets the maximum size for the resizing element
34794      * @return {Number} The maximum size
34795      */
34796     getMaximumSize : function(){
34797         return this.maxSize;
34798     },
34799     
34800     /**
34801      * Sets the maximum size for the resizing element
34802      * @param {Number} maxSize The maximum size
34803      */
34804     setMaximumSize : function(maxSize){
34805         this.maxSize = maxSize;
34806     },
34807     
34808     /**
34809      * Sets the initialize size for the resizing element
34810      * @param {Number} size The initial size
34811      */
34812     setCurrentSize : function(size){
34813         var oldAnimate = this.animate;
34814         this.animate = false;
34815         this.adapter.setElementSize(this, size);
34816         this.animate = oldAnimate;
34817     },
34818     
34819     /**
34820      * Destroy this splitbar. 
34821      * @param {Boolean} removeEl True to remove the element
34822      */
34823     destroy : function(removeEl){
34824         if(this.shim){
34825             this.shim.remove();
34826         }
34827         this.dd.unreg();
34828         this.proxy.parentNode.removeChild(this.proxy);
34829         if(removeEl){
34830             this.el.remove();
34831         }
34832     }
34833 });
34834
34835 /**
34836  * @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.
34837  */
34838 Roo.bootstrap.SplitBar.createProxy = function(dir){
34839     var proxy = new Roo.Element(document.createElement("div"));
34840     proxy.unselectable();
34841     var cls = 'roo-splitbar-proxy';
34842     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34843     document.body.appendChild(proxy.dom);
34844     return proxy.dom;
34845 };
34846
34847 /** 
34848  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34849  * Default Adapter. It assumes the splitter and resizing element are not positioned
34850  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34851  */
34852 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34853 };
34854
34855 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34856     // do nothing for now
34857     init : function(s){
34858     
34859     },
34860     /**
34861      * Called before drag operations to get the current size of the resizing element. 
34862      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34863      */
34864      getElementSize : function(s){
34865         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34866             return s.resizingEl.getWidth();
34867         }else{
34868             return s.resizingEl.getHeight();
34869         }
34870     },
34871     
34872     /**
34873      * Called after drag operations to set the size of the resizing element.
34874      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34875      * @param {Number} newSize The new size to set
34876      * @param {Function} onComplete A function to be invoked when resizing is complete
34877      */
34878     setElementSize : function(s, newSize, onComplete){
34879         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34880             if(!s.animate){
34881                 s.resizingEl.setWidth(newSize);
34882                 if(onComplete){
34883                     onComplete(s, newSize);
34884                 }
34885             }else{
34886                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34887             }
34888         }else{
34889             
34890             if(!s.animate){
34891                 s.resizingEl.setHeight(newSize);
34892                 if(onComplete){
34893                     onComplete(s, newSize);
34894                 }
34895             }else{
34896                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34897             }
34898         }
34899     }
34900 };
34901
34902 /** 
34903  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34904  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34905  * Adapter that  moves the splitter element to align with the resized sizing element. 
34906  * Used with an absolute positioned SplitBar.
34907  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34908  * document.body, make sure you assign an id to the body element.
34909  */
34910 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34911     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34912     this.container = Roo.get(container);
34913 };
34914
34915 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34916     init : function(s){
34917         this.basic.init(s);
34918     },
34919     
34920     getElementSize : function(s){
34921         return this.basic.getElementSize(s);
34922     },
34923     
34924     setElementSize : function(s, newSize, onComplete){
34925         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34926     },
34927     
34928     moveSplitter : function(s){
34929         var yes = Roo.bootstrap.SplitBar;
34930         switch(s.placement){
34931             case yes.LEFT:
34932                 s.el.setX(s.resizingEl.getRight());
34933                 break;
34934             case yes.RIGHT:
34935                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34936                 break;
34937             case yes.TOP:
34938                 s.el.setY(s.resizingEl.getBottom());
34939                 break;
34940             case yes.BOTTOM:
34941                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34942                 break;
34943         }
34944     }
34945 };
34946
34947 /**
34948  * Orientation constant - Create a vertical SplitBar
34949  * @static
34950  * @type Number
34951  */
34952 Roo.bootstrap.SplitBar.VERTICAL = 1;
34953
34954 /**
34955  * Orientation constant - Create a horizontal SplitBar
34956  * @static
34957  * @type Number
34958  */
34959 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34960
34961 /**
34962  * Placement constant - The resizing element is to the left of the splitter element
34963  * @static
34964  * @type Number
34965  */
34966 Roo.bootstrap.SplitBar.LEFT = 1;
34967
34968 /**
34969  * Placement constant - The resizing element is to the right of the splitter element
34970  * @static
34971  * @type Number
34972  */
34973 Roo.bootstrap.SplitBar.RIGHT = 2;
34974
34975 /**
34976  * Placement constant - The resizing element is positioned above the splitter element
34977  * @static
34978  * @type Number
34979  */
34980 Roo.bootstrap.SplitBar.TOP = 3;
34981
34982 /**
34983  * Placement constant - The resizing element is positioned under splitter element
34984  * @static
34985  * @type Number
34986  */
34987 Roo.bootstrap.SplitBar.BOTTOM = 4;
34988 Roo.namespace("Roo.bootstrap.layout");/*
34989  * Based on:
34990  * Ext JS Library 1.1.1
34991  * Copyright(c) 2006-2007, Ext JS, LLC.
34992  *
34993  * Originally Released Under LGPL - original licence link has changed is not relivant.
34994  *
34995  * Fork - LGPL
34996  * <script type="text/javascript">
34997  */
34998
34999 /**
35000  * @class Roo.bootstrap.layout.Manager
35001  * @extends Roo.bootstrap.Component
35002  * Base class for layout managers.
35003  */
35004 Roo.bootstrap.layout.Manager = function(config)
35005 {
35006     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35007
35008
35009
35010
35011
35012     /** false to disable window resize monitoring @type Boolean */
35013     this.monitorWindowResize = true;
35014     this.regions = {};
35015     this.addEvents({
35016         /**
35017          * @event layout
35018          * Fires when a layout is performed.
35019          * @param {Roo.LayoutManager} this
35020          */
35021         "layout" : true,
35022         /**
35023          * @event regionresized
35024          * Fires when the user resizes a region.
35025          * @param {Roo.LayoutRegion} region The resized region
35026          * @param {Number} newSize The new size (width for east/west, height for north/south)
35027          */
35028         "regionresized" : true,
35029         /**
35030          * @event regioncollapsed
35031          * Fires when a region is collapsed.
35032          * @param {Roo.LayoutRegion} region The collapsed region
35033          */
35034         "regioncollapsed" : true,
35035         /**
35036          * @event regionexpanded
35037          * Fires when a region is expanded.
35038          * @param {Roo.LayoutRegion} region The expanded region
35039          */
35040         "regionexpanded" : true
35041     });
35042     this.updating = false;
35043
35044     if (config.el) {
35045         this.el = Roo.get(config.el);
35046         this.initEvents();
35047     }
35048
35049 };
35050
35051 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35052
35053
35054     regions : null,
35055
35056     monitorWindowResize : true,
35057
35058
35059     updating : false,
35060
35061
35062     onRender : function(ct, position)
35063     {
35064         if(!this.el){
35065             this.el = Roo.get(ct);
35066             this.initEvents();
35067         }
35068         //this.fireEvent('render',this);
35069     },
35070
35071
35072     initEvents: function()
35073     {
35074
35075
35076         // ie scrollbar fix
35077         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35078             document.body.scroll = "no";
35079         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35080             this.el.position('relative');
35081         }
35082         this.id = this.el.id;
35083         this.el.addClass("roo-layout-container");
35084         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35085         if(this.el.dom != document.body ) {
35086             this.el.on('resize', this.layout,this);
35087             this.el.on('show', this.layout,this);
35088         }
35089
35090     },
35091
35092     /**
35093      * Returns true if this layout is currently being updated
35094      * @return {Boolean}
35095      */
35096     isUpdating : function(){
35097         return this.updating;
35098     },
35099
35100     /**
35101      * Suspend the LayoutManager from doing auto-layouts while
35102      * making multiple add or remove calls
35103      */
35104     beginUpdate : function(){
35105         this.updating = true;
35106     },
35107
35108     /**
35109      * Restore auto-layouts and optionally disable the manager from performing a layout
35110      * @param {Boolean} noLayout true to disable a layout update
35111      */
35112     endUpdate : function(noLayout){
35113         this.updating = false;
35114         if(!noLayout){
35115             this.layout();
35116         }
35117     },
35118
35119     layout: function(){
35120         // abstract...
35121     },
35122
35123     onRegionResized : function(region, newSize){
35124         this.fireEvent("regionresized", region, newSize);
35125         this.layout();
35126     },
35127
35128     onRegionCollapsed : function(region){
35129         this.fireEvent("regioncollapsed", region);
35130     },
35131
35132     onRegionExpanded : function(region){
35133         this.fireEvent("regionexpanded", region);
35134     },
35135
35136     /**
35137      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35138      * performs box-model adjustments.
35139      * @return {Object} The size as an object {width: (the width), height: (the height)}
35140      */
35141     getViewSize : function()
35142     {
35143         var size;
35144         if(this.el.dom != document.body){
35145             size = this.el.getSize();
35146         }else{
35147             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35148         }
35149         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35150         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35151         return size;
35152     },
35153
35154     /**
35155      * Returns the Element this layout is bound to.
35156      * @return {Roo.Element}
35157      */
35158     getEl : function(){
35159         return this.el;
35160     },
35161
35162     /**
35163      * Returns the specified region.
35164      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35165      * @return {Roo.LayoutRegion}
35166      */
35167     getRegion : function(target){
35168         return this.regions[target.toLowerCase()];
35169     },
35170
35171     onWindowResize : function(){
35172         if(this.monitorWindowResize){
35173             this.layout();
35174         }
35175     }
35176 });
35177 /*
35178  * Based on:
35179  * Ext JS Library 1.1.1
35180  * Copyright(c) 2006-2007, Ext JS, LLC.
35181  *
35182  * Originally Released Under LGPL - original licence link has changed is not relivant.
35183  *
35184  * Fork - LGPL
35185  * <script type="text/javascript">
35186  */
35187 /**
35188  * @class Roo.bootstrap.layout.Border
35189  * @extends Roo.bootstrap.layout.Manager
35190  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35191  * please see: examples/bootstrap/nested.html<br><br>
35192  
35193 <b>The container the layout is rendered into can be either the body element or any other element.
35194 If it is not the body element, the container needs to either be an absolute positioned element,
35195 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35196 the container size if it is not the body element.</b>
35197
35198 * @constructor
35199 * Create a new Border
35200 * @param {Object} config Configuration options
35201  */
35202 Roo.bootstrap.layout.Border = function(config){
35203     config = config || {};
35204     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35205     
35206     
35207     
35208     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35209         if(config[region]){
35210             config[region].region = region;
35211             this.addRegion(config[region]);
35212         }
35213     },this);
35214     
35215 };
35216
35217 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35218
35219 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35220     
35221     parent : false, // this might point to a 'nest' or a ???
35222     
35223     /**
35224      * Creates and adds a new region if it doesn't already exist.
35225      * @param {String} target The target region key (north, south, east, west or center).
35226      * @param {Object} config The regions config object
35227      * @return {BorderLayoutRegion} The new region
35228      */
35229     addRegion : function(config)
35230     {
35231         if(!this.regions[config.region]){
35232             var r = this.factory(config);
35233             this.bindRegion(r);
35234         }
35235         return this.regions[config.region];
35236     },
35237
35238     // private (kinda)
35239     bindRegion : function(r){
35240         this.regions[r.config.region] = r;
35241         
35242         r.on("visibilitychange",    this.layout, this);
35243         r.on("paneladded",          this.layout, this);
35244         r.on("panelremoved",        this.layout, this);
35245         r.on("invalidated",         this.layout, this);
35246         r.on("resized",             this.onRegionResized, this);
35247         r.on("collapsed",           this.onRegionCollapsed, this);
35248         r.on("expanded",            this.onRegionExpanded, this);
35249     },
35250
35251     /**
35252      * Performs a layout update.
35253      */
35254     layout : function()
35255     {
35256         if(this.updating) {
35257             return;
35258         }
35259         
35260         // render all the rebions if they have not been done alreayd?
35261         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35262             if(this.regions[region] && !this.regions[region].bodyEl){
35263                 this.regions[region].onRender(this.el)
35264             }
35265         },this);
35266         
35267         var size = this.getViewSize();
35268         var w = size.width;
35269         var h = size.height;
35270         var centerW = w;
35271         var centerH = h;
35272         var centerY = 0;
35273         var centerX = 0;
35274         //var x = 0, y = 0;
35275
35276         var rs = this.regions;
35277         var north = rs["north"];
35278         var south = rs["south"]; 
35279         var west = rs["west"];
35280         var east = rs["east"];
35281         var center = rs["center"];
35282         //if(this.hideOnLayout){ // not supported anymore
35283             //c.el.setStyle("display", "none");
35284         //}
35285         if(north && north.isVisible()){
35286             var b = north.getBox();
35287             var m = north.getMargins();
35288             b.width = w - (m.left+m.right);
35289             b.x = m.left;
35290             b.y = m.top;
35291             centerY = b.height + b.y + m.bottom;
35292             centerH -= centerY;
35293             north.updateBox(this.safeBox(b));
35294         }
35295         if(south && south.isVisible()){
35296             var b = south.getBox();
35297             var m = south.getMargins();
35298             b.width = w - (m.left+m.right);
35299             b.x = m.left;
35300             var totalHeight = (b.height + m.top + m.bottom);
35301             b.y = h - totalHeight + m.top;
35302             centerH -= totalHeight;
35303             south.updateBox(this.safeBox(b));
35304         }
35305         if(west && west.isVisible()){
35306             var b = west.getBox();
35307             var m = west.getMargins();
35308             b.height = centerH - (m.top+m.bottom);
35309             b.x = m.left;
35310             b.y = centerY + m.top;
35311             var totalWidth = (b.width + m.left + m.right);
35312             centerX += totalWidth;
35313             centerW -= totalWidth;
35314             west.updateBox(this.safeBox(b));
35315         }
35316         if(east && east.isVisible()){
35317             var b = east.getBox();
35318             var m = east.getMargins();
35319             b.height = centerH - (m.top+m.bottom);
35320             var totalWidth = (b.width + m.left + m.right);
35321             b.x = w - totalWidth + m.left;
35322             b.y = centerY + m.top;
35323             centerW -= totalWidth;
35324             east.updateBox(this.safeBox(b));
35325         }
35326         if(center){
35327             var m = center.getMargins();
35328             var centerBox = {
35329                 x: centerX + m.left,
35330                 y: centerY + m.top,
35331                 width: centerW - (m.left+m.right),
35332                 height: centerH - (m.top+m.bottom)
35333             };
35334             //if(this.hideOnLayout){
35335                 //center.el.setStyle("display", "block");
35336             //}
35337             center.updateBox(this.safeBox(centerBox));
35338         }
35339         this.el.repaint();
35340         this.fireEvent("layout", this);
35341     },
35342
35343     // private
35344     safeBox : function(box){
35345         box.width = Math.max(0, box.width);
35346         box.height = Math.max(0, box.height);
35347         return box;
35348     },
35349
35350     /**
35351      * Adds a ContentPanel (or subclass) to this layout.
35352      * @param {String} target The target region key (north, south, east, west or center).
35353      * @param {Roo.ContentPanel} panel The panel to add
35354      * @return {Roo.ContentPanel} The added panel
35355      */
35356     add : function(target, panel){
35357          
35358         target = target.toLowerCase();
35359         return this.regions[target].add(panel);
35360     },
35361
35362     /**
35363      * Remove a ContentPanel (or subclass) to this layout.
35364      * @param {String} target The target region key (north, south, east, west or center).
35365      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35366      * @return {Roo.ContentPanel} The removed panel
35367      */
35368     remove : function(target, panel){
35369         target = target.toLowerCase();
35370         return this.regions[target].remove(panel);
35371     },
35372
35373     /**
35374      * Searches all regions for a panel with the specified id
35375      * @param {String} panelId
35376      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35377      */
35378     findPanel : function(panelId){
35379         var rs = this.regions;
35380         for(var target in rs){
35381             if(typeof rs[target] != "function"){
35382                 var p = rs[target].getPanel(panelId);
35383                 if(p){
35384                     return p;
35385                 }
35386             }
35387         }
35388         return null;
35389     },
35390
35391     /**
35392      * Searches all regions for a panel with the specified id and activates (shows) it.
35393      * @param {String/ContentPanel} panelId The panels id or the panel itself
35394      * @return {Roo.ContentPanel} The shown panel or null
35395      */
35396     showPanel : function(panelId) {
35397       var rs = this.regions;
35398       for(var target in rs){
35399          var r = rs[target];
35400          if(typeof r != "function"){
35401             if(r.hasPanel(panelId)){
35402                return r.showPanel(panelId);
35403             }
35404          }
35405       }
35406       return null;
35407    },
35408
35409    /**
35410      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35411      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35412      */
35413    /*
35414     restoreState : function(provider){
35415         if(!provider){
35416             provider = Roo.state.Manager;
35417         }
35418         var sm = new Roo.LayoutStateManager();
35419         sm.init(this, provider);
35420     },
35421 */
35422  
35423  
35424     /**
35425      * Adds a xtype elements to the layout.
35426      * <pre><code>
35427
35428 layout.addxtype({
35429        xtype : 'ContentPanel',
35430        region: 'west',
35431        items: [ .... ]
35432    }
35433 );
35434
35435 layout.addxtype({
35436         xtype : 'NestedLayoutPanel',
35437         region: 'west',
35438         layout: {
35439            center: { },
35440            west: { }   
35441         },
35442         items : [ ... list of content panels or nested layout panels.. ]
35443    }
35444 );
35445 </code></pre>
35446      * @param {Object} cfg Xtype definition of item to add.
35447      */
35448     addxtype : function(cfg)
35449     {
35450         // basically accepts a pannel...
35451         // can accept a layout region..!?!?
35452         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35453         
35454         
35455         // theory?  children can only be panels??
35456         
35457         //if (!cfg.xtype.match(/Panel$/)) {
35458         //    return false;
35459         //}
35460         var ret = false;
35461         
35462         if (typeof(cfg.region) == 'undefined') {
35463             Roo.log("Failed to add Panel, region was not set");
35464             Roo.log(cfg);
35465             return false;
35466         }
35467         var region = cfg.region;
35468         delete cfg.region;
35469         
35470           
35471         var xitems = [];
35472         if (cfg.items) {
35473             xitems = cfg.items;
35474             delete cfg.items;
35475         }
35476         var nb = false;
35477         
35478         if ( region == 'center') {
35479             Roo.log("Center: " + cfg.title);
35480         }
35481         
35482         
35483         switch(cfg.xtype) 
35484         {
35485             case 'Content':  // ContentPanel (el, cfg)
35486             case 'Scroll':  // ContentPanel (el, cfg)
35487             case 'View': 
35488                 cfg.autoCreate = cfg.autoCreate || true;
35489                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35490                 //} else {
35491                 //    var el = this.el.createChild();
35492                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35493                 //}
35494                 
35495                 this.add(region, ret);
35496                 break;
35497             
35498             /*
35499             case 'TreePanel': // our new panel!
35500                 cfg.el = this.el.createChild();
35501                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35502                 this.add(region, ret);
35503                 break;
35504             */
35505             
35506             case 'Nest': 
35507                 // create a new Layout (which is  a Border Layout...
35508                 
35509                 var clayout = cfg.layout;
35510                 clayout.el  = this.el.createChild();
35511                 clayout.items   = clayout.items  || [];
35512                 
35513                 delete cfg.layout;
35514                 
35515                 // replace this exitems with the clayout ones..
35516                 xitems = clayout.items;
35517                  
35518                 // force background off if it's in center...
35519                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35520                     cfg.background = false;
35521                 }
35522                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35523                 
35524                 
35525                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35526                 //console.log('adding nested layout panel '  + cfg.toSource());
35527                 this.add(region, ret);
35528                 nb = {}; /// find first...
35529                 break;
35530             
35531             case 'Grid':
35532                 
35533                 // needs grid and region
35534                 
35535                 //var el = this.getRegion(region).el.createChild();
35536                 /*
35537                  *var el = this.el.createChild();
35538                 // create the grid first...
35539                 cfg.grid.container = el;
35540                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35541                 */
35542                 
35543                 if (region == 'center' && this.active ) {
35544                     cfg.background = false;
35545                 }
35546                 
35547                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35548                 
35549                 this.add(region, ret);
35550                 /*
35551                 if (cfg.background) {
35552                     // render grid on panel activation (if panel background)
35553                     ret.on('activate', function(gp) {
35554                         if (!gp.grid.rendered) {
35555                     //        gp.grid.render(el);
35556                         }
35557                     });
35558                 } else {
35559                   //  cfg.grid.render(el);
35560                 }
35561                 */
35562                 break;
35563            
35564            
35565             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35566                 // it was the old xcomponent building that caused this before.
35567                 // espeically if border is the top element in the tree.
35568                 ret = this;
35569                 break; 
35570                 
35571                     
35572                 
35573                 
35574                 
35575             default:
35576                 /*
35577                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35578                     
35579                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35580                     this.add(region, ret);
35581                 } else {
35582                 */
35583                     Roo.log(cfg);
35584                     throw "Can not add '" + cfg.xtype + "' to Border";
35585                     return null;
35586              
35587                                 
35588              
35589         }
35590         this.beginUpdate();
35591         // add children..
35592         var region = '';
35593         var abn = {};
35594         Roo.each(xitems, function(i)  {
35595             region = nb && i.region ? i.region : false;
35596             
35597             var add = ret.addxtype(i);
35598            
35599             if (region) {
35600                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35601                 if (!i.background) {
35602                     abn[region] = nb[region] ;
35603                 }
35604             }
35605             
35606         });
35607         this.endUpdate();
35608
35609         // make the last non-background panel active..
35610         //if (nb) { Roo.log(abn); }
35611         if (nb) {
35612             
35613             for(var r in abn) {
35614                 region = this.getRegion(r);
35615                 if (region) {
35616                     // tried using nb[r], but it does not work..
35617                      
35618                     region.showPanel(abn[r]);
35619                    
35620                 }
35621             }
35622         }
35623         return ret;
35624         
35625     },
35626     
35627     
35628 // private
35629     factory : function(cfg)
35630     {
35631         
35632         var validRegions = Roo.bootstrap.layout.Border.regions;
35633
35634         var target = cfg.region;
35635         cfg.mgr = this;
35636         
35637         var r = Roo.bootstrap.layout;
35638         Roo.log(target);
35639         switch(target){
35640             case "north":
35641                 return new r.North(cfg);
35642             case "south":
35643                 return new r.South(cfg);
35644             case "east":
35645                 return new r.East(cfg);
35646             case "west":
35647                 return new r.West(cfg);
35648             case "center":
35649                 return new r.Center(cfg);
35650         }
35651         throw 'Layout region "'+target+'" not supported.';
35652     }
35653     
35654     
35655 });
35656  /*
35657  * Based on:
35658  * Ext JS Library 1.1.1
35659  * Copyright(c) 2006-2007, Ext JS, LLC.
35660  *
35661  * Originally Released Under LGPL - original licence link has changed is not relivant.
35662  *
35663  * Fork - LGPL
35664  * <script type="text/javascript">
35665  */
35666  
35667 /**
35668  * @class Roo.bootstrap.layout.Basic
35669  * @extends Roo.util.Observable
35670  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35671  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35672  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35673  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35674  * @cfg {string}   region  the region that it inhabits..
35675  * @cfg {bool}   skipConfig skip config?
35676  * 
35677
35678  */
35679 Roo.bootstrap.layout.Basic = function(config){
35680     
35681     this.mgr = config.mgr;
35682     
35683     this.position = config.region;
35684     
35685     var skipConfig = config.skipConfig;
35686     
35687     this.events = {
35688         /**
35689          * @scope Roo.BasicLayoutRegion
35690          */
35691         
35692         /**
35693          * @event beforeremove
35694          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35695          * @param {Roo.LayoutRegion} this
35696          * @param {Roo.ContentPanel} panel The panel
35697          * @param {Object} e The cancel event object
35698          */
35699         "beforeremove" : true,
35700         /**
35701          * @event invalidated
35702          * Fires when the layout for this region is changed.
35703          * @param {Roo.LayoutRegion} this
35704          */
35705         "invalidated" : true,
35706         /**
35707          * @event visibilitychange
35708          * Fires when this region is shown or hidden 
35709          * @param {Roo.LayoutRegion} this
35710          * @param {Boolean} visibility true or false
35711          */
35712         "visibilitychange" : true,
35713         /**
35714          * @event paneladded
35715          * Fires when a panel is added. 
35716          * @param {Roo.LayoutRegion} this
35717          * @param {Roo.ContentPanel} panel The panel
35718          */
35719         "paneladded" : true,
35720         /**
35721          * @event panelremoved
35722          * Fires when a panel is removed. 
35723          * @param {Roo.LayoutRegion} this
35724          * @param {Roo.ContentPanel} panel The panel
35725          */
35726         "panelremoved" : true,
35727         /**
35728          * @event beforecollapse
35729          * Fires when this region before collapse.
35730          * @param {Roo.LayoutRegion} this
35731          */
35732         "beforecollapse" : true,
35733         /**
35734          * @event collapsed
35735          * Fires when this region is collapsed.
35736          * @param {Roo.LayoutRegion} this
35737          */
35738         "collapsed" : true,
35739         /**
35740          * @event expanded
35741          * Fires when this region is expanded.
35742          * @param {Roo.LayoutRegion} this
35743          */
35744         "expanded" : true,
35745         /**
35746          * @event slideshow
35747          * Fires when this region is slid into view.
35748          * @param {Roo.LayoutRegion} this
35749          */
35750         "slideshow" : true,
35751         /**
35752          * @event slidehide
35753          * Fires when this region slides out of view. 
35754          * @param {Roo.LayoutRegion} this
35755          */
35756         "slidehide" : true,
35757         /**
35758          * @event panelactivated
35759          * Fires when a panel is activated. 
35760          * @param {Roo.LayoutRegion} this
35761          * @param {Roo.ContentPanel} panel The activated panel
35762          */
35763         "panelactivated" : true,
35764         /**
35765          * @event resized
35766          * Fires when the user resizes this region. 
35767          * @param {Roo.LayoutRegion} this
35768          * @param {Number} newSize The new size (width for east/west, height for north/south)
35769          */
35770         "resized" : true
35771     };
35772     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35773     this.panels = new Roo.util.MixedCollection();
35774     this.panels.getKey = this.getPanelId.createDelegate(this);
35775     this.box = null;
35776     this.activePanel = null;
35777     // ensure listeners are added...
35778     
35779     if (config.listeners || config.events) {
35780         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35781             listeners : config.listeners || {},
35782             events : config.events || {}
35783         });
35784     }
35785     
35786     if(skipConfig !== true){
35787         this.applyConfig(config);
35788     }
35789 };
35790
35791 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35792 {
35793     getPanelId : function(p){
35794         return p.getId();
35795     },
35796     
35797     applyConfig : function(config){
35798         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35799         this.config = config;
35800         
35801     },
35802     
35803     /**
35804      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35805      * the width, for horizontal (north, south) the height.
35806      * @param {Number} newSize The new width or height
35807      */
35808     resizeTo : function(newSize){
35809         var el = this.el ? this.el :
35810                  (this.activePanel ? this.activePanel.getEl() : null);
35811         if(el){
35812             switch(this.position){
35813                 case "east":
35814                 case "west":
35815                     el.setWidth(newSize);
35816                     this.fireEvent("resized", this, newSize);
35817                 break;
35818                 case "north":
35819                 case "south":
35820                     el.setHeight(newSize);
35821                     this.fireEvent("resized", this, newSize);
35822                 break;                
35823             }
35824         }
35825     },
35826     
35827     getBox : function(){
35828         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35829     },
35830     
35831     getMargins : function(){
35832         return this.margins;
35833     },
35834     
35835     updateBox : function(box){
35836         this.box = box;
35837         var el = this.activePanel.getEl();
35838         el.dom.style.left = box.x + "px";
35839         el.dom.style.top = box.y + "px";
35840         this.activePanel.setSize(box.width, box.height);
35841     },
35842     
35843     /**
35844      * Returns the container element for this region.
35845      * @return {Roo.Element}
35846      */
35847     getEl : function(){
35848         return this.activePanel;
35849     },
35850     
35851     /**
35852      * Returns true if this region is currently visible.
35853      * @return {Boolean}
35854      */
35855     isVisible : function(){
35856         return this.activePanel ? true : false;
35857     },
35858     
35859     setActivePanel : function(panel){
35860         panel = this.getPanel(panel);
35861         if(this.activePanel && this.activePanel != panel){
35862             this.activePanel.setActiveState(false);
35863             this.activePanel.getEl().setLeftTop(-10000,-10000);
35864         }
35865         this.activePanel = panel;
35866         panel.setActiveState(true);
35867         if(this.box){
35868             panel.setSize(this.box.width, this.box.height);
35869         }
35870         this.fireEvent("panelactivated", this, panel);
35871         this.fireEvent("invalidated");
35872     },
35873     
35874     /**
35875      * Show the specified panel.
35876      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35877      * @return {Roo.ContentPanel} The shown panel or null
35878      */
35879     showPanel : function(panel){
35880         panel = this.getPanel(panel);
35881         if(panel){
35882             this.setActivePanel(panel);
35883         }
35884         return panel;
35885     },
35886     
35887     /**
35888      * Get the active panel for this region.
35889      * @return {Roo.ContentPanel} The active panel or null
35890      */
35891     getActivePanel : function(){
35892         return this.activePanel;
35893     },
35894     
35895     /**
35896      * Add the passed ContentPanel(s)
35897      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35898      * @return {Roo.ContentPanel} The panel added (if only one was added)
35899      */
35900     add : function(panel){
35901         if(arguments.length > 1){
35902             for(var i = 0, len = arguments.length; i < len; i++) {
35903                 this.add(arguments[i]);
35904             }
35905             return null;
35906         }
35907         if(this.hasPanel(panel)){
35908             this.showPanel(panel);
35909             return panel;
35910         }
35911         var el = panel.getEl();
35912         if(el.dom.parentNode != this.mgr.el.dom){
35913             this.mgr.el.dom.appendChild(el.dom);
35914         }
35915         if(panel.setRegion){
35916             panel.setRegion(this);
35917         }
35918         this.panels.add(panel);
35919         el.setStyle("position", "absolute");
35920         if(!panel.background){
35921             this.setActivePanel(panel);
35922             if(this.config.initialSize && this.panels.getCount()==1){
35923                 this.resizeTo(this.config.initialSize);
35924             }
35925         }
35926         this.fireEvent("paneladded", this, panel);
35927         return panel;
35928     },
35929     
35930     /**
35931      * Returns true if the panel is in this region.
35932      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35933      * @return {Boolean}
35934      */
35935     hasPanel : function(panel){
35936         if(typeof panel == "object"){ // must be panel obj
35937             panel = panel.getId();
35938         }
35939         return this.getPanel(panel) ? true : false;
35940     },
35941     
35942     /**
35943      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35944      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35945      * @param {Boolean} preservePanel Overrides the config preservePanel option
35946      * @return {Roo.ContentPanel} The panel that was removed
35947      */
35948     remove : function(panel, preservePanel){
35949         panel = this.getPanel(panel);
35950         if(!panel){
35951             return null;
35952         }
35953         var e = {};
35954         this.fireEvent("beforeremove", this, panel, e);
35955         if(e.cancel === true){
35956             return null;
35957         }
35958         var panelId = panel.getId();
35959         this.panels.removeKey(panelId);
35960         return panel;
35961     },
35962     
35963     /**
35964      * Returns the panel specified or null if it's not in this region.
35965      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35966      * @return {Roo.ContentPanel}
35967      */
35968     getPanel : function(id){
35969         if(typeof id == "object"){ // must be panel obj
35970             return id;
35971         }
35972         return this.panels.get(id);
35973     },
35974     
35975     /**
35976      * Returns this regions position (north/south/east/west/center).
35977      * @return {String} 
35978      */
35979     getPosition: function(){
35980         return this.position;    
35981     }
35982 });/*
35983  * Based on:
35984  * Ext JS Library 1.1.1
35985  * Copyright(c) 2006-2007, Ext JS, LLC.
35986  *
35987  * Originally Released Under LGPL - original licence link has changed is not relivant.
35988  *
35989  * Fork - LGPL
35990  * <script type="text/javascript">
35991  */
35992  
35993 /**
35994  * @class Roo.bootstrap.layout.Region
35995  * @extends Roo.bootstrap.layout.Basic
35996  * This class represents a region in a layout manager.
35997  
35998  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35999  * @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})
36000  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
36001  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
36002  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
36003  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
36004  * @cfg {String}    title           The title for the region (overrides panel titles)
36005  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
36006  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36007  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
36008  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36009  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
36010  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36011  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
36012  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
36013  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
36014  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
36015
36016  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
36017  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
36018  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
36019  * @cfg {Number}    width           For East/West panels
36020  * @cfg {Number}    height          For North/South panels
36021  * @cfg {Boolean}   split           To show the splitter
36022  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
36023  * 
36024  * @cfg {string}   cls             Extra CSS classes to add to region
36025  * 
36026  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36027  * @cfg {string}   region  the region that it inhabits..
36028  *
36029
36030  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
36031  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
36032
36033  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
36034  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
36035  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
36036  */
36037 Roo.bootstrap.layout.Region = function(config)
36038 {
36039     this.applyConfig(config);
36040
36041     var mgr = config.mgr;
36042     var pos = config.region;
36043     config.skipConfig = true;
36044     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36045     
36046     if (mgr.el) {
36047         this.onRender(mgr.el);   
36048     }
36049      
36050     this.visible = true;
36051     this.collapsed = false;
36052     this.unrendered_panels = [];
36053 };
36054
36055 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36056
36057     position: '', // set by wrapper (eg. north/south etc..)
36058     unrendered_panels : null,  // unrendered panels.
36059     
36060     tabPosition : false,
36061     
36062     mgr: false, // points to 'Border'
36063     
36064     
36065     createBody : function(){
36066         /** This region's body element 
36067         * @type Roo.Element */
36068         this.bodyEl = this.el.createChild({
36069                 tag: "div",
36070                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36071         });
36072     },
36073
36074     onRender: function(ctr, pos)
36075     {
36076         var dh = Roo.DomHelper;
36077         /** This region's container element 
36078         * @type Roo.Element */
36079         this.el = dh.append(ctr.dom, {
36080                 tag: "div",
36081                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36082             }, true);
36083         /** This region's title element 
36084         * @type Roo.Element */
36085     
36086         this.titleEl = dh.append(this.el.dom,  {
36087                 tag: "div",
36088                 unselectable: "on",
36089                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36090                 children:[
36091                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36092                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36093                 ]
36094             }, true);
36095         
36096         this.titleEl.enableDisplayMode();
36097         /** This region's title text element 
36098         * @type HTMLElement */
36099         this.titleTextEl = this.titleEl.dom.firstChild;
36100         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36101         /*
36102         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36103         this.closeBtn.enableDisplayMode();
36104         this.closeBtn.on("click", this.closeClicked, this);
36105         this.closeBtn.hide();
36106     */
36107         this.createBody(this.config);
36108         if(this.config.hideWhenEmpty){
36109             this.hide();
36110             this.on("paneladded", this.validateVisibility, this);
36111             this.on("panelremoved", this.validateVisibility, this);
36112         }
36113         if(this.autoScroll){
36114             this.bodyEl.setStyle("overflow", "auto");
36115         }else{
36116             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36117         }
36118         //if(c.titlebar !== false){
36119             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36120                 this.titleEl.hide();
36121             }else{
36122                 this.titleEl.show();
36123                 if(this.config.title){
36124                     this.titleTextEl.innerHTML = this.config.title;
36125                 }
36126             }
36127         //}
36128         if(this.config.collapsed){
36129             this.collapse(true);
36130         }
36131         if(this.config.hidden){
36132             this.hide();
36133         }
36134         
36135         if (this.unrendered_panels && this.unrendered_panels.length) {
36136             for (var i =0;i< this.unrendered_panels.length; i++) {
36137                 this.add(this.unrendered_panels[i]);
36138             }
36139             this.unrendered_panels = null;
36140             
36141         }
36142         
36143     },
36144     
36145     applyConfig : function(c)
36146     {
36147         /*
36148          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36149             var dh = Roo.DomHelper;
36150             if(c.titlebar !== false){
36151                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36152                 this.collapseBtn.on("click", this.collapse, this);
36153                 this.collapseBtn.enableDisplayMode();
36154                 /*
36155                 if(c.showPin === true || this.showPin){
36156                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36157                     this.stickBtn.enableDisplayMode();
36158                     this.stickBtn.on("click", this.expand, this);
36159                     this.stickBtn.hide();
36160                 }
36161                 
36162             }
36163             */
36164             /** This region's collapsed element
36165             * @type Roo.Element */
36166             /*
36167              *
36168             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36169                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36170             ]}, true);
36171             
36172             if(c.floatable !== false){
36173                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36174                this.collapsedEl.on("click", this.collapseClick, this);
36175             }
36176
36177             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36178                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36179                    id: "message", unselectable: "on", style:{"float":"left"}});
36180                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36181              }
36182             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36183             this.expandBtn.on("click", this.expand, this);
36184             
36185         }
36186         
36187         if(this.collapseBtn){
36188             this.collapseBtn.setVisible(c.collapsible == true);
36189         }
36190         
36191         this.cmargins = c.cmargins || this.cmargins ||
36192                          (this.position == "west" || this.position == "east" ?
36193                              {top: 0, left: 2, right:2, bottom: 0} :
36194                              {top: 2, left: 0, right:0, bottom: 2});
36195         */
36196         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36197         
36198         
36199         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36200         
36201         this.autoScroll = c.autoScroll || false;
36202         
36203         
36204        
36205         
36206         this.duration = c.duration || .30;
36207         this.slideDuration = c.slideDuration || .45;
36208         this.config = c;
36209        
36210     },
36211     /**
36212      * Returns true if this region is currently visible.
36213      * @return {Boolean}
36214      */
36215     isVisible : function(){
36216         return this.visible;
36217     },
36218
36219     /**
36220      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36221      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36222      */
36223     //setCollapsedTitle : function(title){
36224     //    title = title || "&#160;";
36225      //   if(this.collapsedTitleTextEl){
36226       //      this.collapsedTitleTextEl.innerHTML = title;
36227        // }
36228     //},
36229
36230     getBox : function(){
36231         var b;
36232       //  if(!this.collapsed){
36233             b = this.el.getBox(false, true);
36234        // }else{
36235           //  b = this.collapsedEl.getBox(false, true);
36236         //}
36237         return b;
36238     },
36239
36240     getMargins : function(){
36241         return this.margins;
36242         //return this.collapsed ? this.cmargins : this.margins;
36243     },
36244 /*
36245     highlight : function(){
36246         this.el.addClass("x-layout-panel-dragover");
36247     },
36248
36249     unhighlight : function(){
36250         this.el.removeClass("x-layout-panel-dragover");
36251     },
36252 */
36253     updateBox : function(box)
36254     {
36255         if (!this.bodyEl) {
36256             return; // not rendered yet..
36257         }
36258         
36259         this.box = box;
36260         if(!this.collapsed){
36261             this.el.dom.style.left = box.x + "px";
36262             this.el.dom.style.top = box.y + "px";
36263             this.updateBody(box.width, box.height);
36264         }else{
36265             this.collapsedEl.dom.style.left = box.x + "px";
36266             this.collapsedEl.dom.style.top = box.y + "px";
36267             this.collapsedEl.setSize(box.width, box.height);
36268         }
36269         if(this.tabs){
36270             this.tabs.autoSizeTabs();
36271         }
36272     },
36273
36274     updateBody : function(w, h)
36275     {
36276         if(w !== null){
36277             this.el.setWidth(w);
36278             w -= this.el.getBorderWidth("rl");
36279             if(this.config.adjustments){
36280                 w += this.config.adjustments[0];
36281             }
36282         }
36283         if(h !== null && h > 0){
36284             this.el.setHeight(h);
36285             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36286             h -= this.el.getBorderWidth("tb");
36287             if(this.config.adjustments){
36288                 h += this.config.adjustments[1];
36289             }
36290             this.bodyEl.setHeight(h);
36291             if(this.tabs){
36292                 h = this.tabs.syncHeight(h);
36293             }
36294         }
36295         if(this.panelSize){
36296             w = w !== null ? w : this.panelSize.width;
36297             h = h !== null ? h : this.panelSize.height;
36298         }
36299         if(this.activePanel){
36300             var el = this.activePanel.getEl();
36301             w = w !== null ? w : el.getWidth();
36302             h = h !== null ? h : el.getHeight();
36303             this.panelSize = {width: w, height: h};
36304             this.activePanel.setSize(w, h);
36305         }
36306         if(Roo.isIE && this.tabs){
36307             this.tabs.el.repaint();
36308         }
36309     },
36310
36311     /**
36312      * Returns the container element for this region.
36313      * @return {Roo.Element}
36314      */
36315     getEl : function(){
36316         return this.el;
36317     },
36318
36319     /**
36320      * Hides this region.
36321      */
36322     hide : function(){
36323         //if(!this.collapsed){
36324             this.el.dom.style.left = "-2000px";
36325             this.el.hide();
36326         //}else{
36327          //   this.collapsedEl.dom.style.left = "-2000px";
36328          //   this.collapsedEl.hide();
36329        // }
36330         this.visible = false;
36331         this.fireEvent("visibilitychange", this, false);
36332     },
36333
36334     /**
36335      * Shows this region if it was previously hidden.
36336      */
36337     show : function(){
36338         //if(!this.collapsed){
36339             this.el.show();
36340         //}else{
36341         //    this.collapsedEl.show();
36342        // }
36343         this.visible = true;
36344         this.fireEvent("visibilitychange", this, true);
36345     },
36346 /*
36347     closeClicked : function(){
36348         if(this.activePanel){
36349             this.remove(this.activePanel);
36350         }
36351     },
36352
36353     collapseClick : function(e){
36354         if(this.isSlid){
36355            e.stopPropagation();
36356            this.slideIn();
36357         }else{
36358            e.stopPropagation();
36359            this.slideOut();
36360         }
36361     },
36362 */
36363     /**
36364      * Collapses this region.
36365      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36366      */
36367     /*
36368     collapse : function(skipAnim, skipCheck = false){
36369         if(this.collapsed) {
36370             return;
36371         }
36372         
36373         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36374             
36375             this.collapsed = true;
36376             if(this.split){
36377                 this.split.el.hide();
36378             }
36379             if(this.config.animate && skipAnim !== true){
36380                 this.fireEvent("invalidated", this);
36381                 this.animateCollapse();
36382             }else{
36383                 this.el.setLocation(-20000,-20000);
36384                 this.el.hide();
36385                 this.collapsedEl.show();
36386                 this.fireEvent("collapsed", this);
36387                 this.fireEvent("invalidated", this);
36388             }
36389         }
36390         
36391     },
36392 */
36393     animateCollapse : function(){
36394         // overridden
36395     },
36396
36397     /**
36398      * Expands this region if it was previously collapsed.
36399      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36400      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36401      */
36402     /*
36403     expand : function(e, skipAnim){
36404         if(e) {
36405             e.stopPropagation();
36406         }
36407         if(!this.collapsed || this.el.hasActiveFx()) {
36408             return;
36409         }
36410         if(this.isSlid){
36411             this.afterSlideIn();
36412             skipAnim = true;
36413         }
36414         this.collapsed = false;
36415         if(this.config.animate && skipAnim !== true){
36416             this.animateExpand();
36417         }else{
36418             this.el.show();
36419             if(this.split){
36420                 this.split.el.show();
36421             }
36422             this.collapsedEl.setLocation(-2000,-2000);
36423             this.collapsedEl.hide();
36424             this.fireEvent("invalidated", this);
36425             this.fireEvent("expanded", this);
36426         }
36427     },
36428 */
36429     animateExpand : function(){
36430         // overridden
36431     },
36432
36433     initTabs : function()
36434     {
36435         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36436         
36437         var ts = new Roo.bootstrap.panel.Tabs({
36438             el: this.bodyEl.dom,
36439             region : this,
36440             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36441             disableTooltips: this.config.disableTabTips,
36442             toolbar : this.config.toolbar
36443         });
36444         
36445         if(this.config.hideTabs){
36446             ts.stripWrap.setDisplayed(false);
36447         }
36448         this.tabs = ts;
36449         ts.resizeTabs = this.config.resizeTabs === true;
36450         ts.minTabWidth = this.config.minTabWidth || 40;
36451         ts.maxTabWidth = this.config.maxTabWidth || 250;
36452         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36453         ts.monitorResize = false;
36454         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36455         ts.bodyEl.addClass('roo-layout-tabs-body');
36456         this.panels.each(this.initPanelAsTab, this);
36457     },
36458
36459     initPanelAsTab : function(panel){
36460         var ti = this.tabs.addTab(
36461             panel.getEl().id,
36462             panel.getTitle(),
36463             null,
36464             this.config.closeOnTab && panel.isClosable(),
36465             panel.tpl
36466         );
36467         if(panel.tabTip !== undefined){
36468             ti.setTooltip(panel.tabTip);
36469         }
36470         ti.on("activate", function(){
36471               this.setActivePanel(panel);
36472         }, this);
36473         
36474         if(this.config.closeOnTab){
36475             ti.on("beforeclose", function(t, e){
36476                 e.cancel = true;
36477                 this.remove(panel);
36478             }, this);
36479         }
36480         
36481         panel.tabItem = ti;
36482         
36483         return ti;
36484     },
36485
36486     updatePanelTitle : function(panel, title)
36487     {
36488         if(this.activePanel == panel){
36489             this.updateTitle(title);
36490         }
36491         if(this.tabs){
36492             var ti = this.tabs.getTab(panel.getEl().id);
36493             ti.setText(title);
36494             if(panel.tabTip !== undefined){
36495                 ti.setTooltip(panel.tabTip);
36496             }
36497         }
36498     },
36499
36500     updateTitle : function(title){
36501         if(this.titleTextEl && !this.config.title){
36502             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36503         }
36504     },
36505
36506     setActivePanel : function(panel)
36507     {
36508         panel = this.getPanel(panel);
36509         if(this.activePanel && this.activePanel != panel){
36510             if(this.activePanel.setActiveState(false) === false){
36511                 return;
36512             }
36513         }
36514         this.activePanel = panel;
36515         panel.setActiveState(true);
36516         if(this.panelSize){
36517             panel.setSize(this.panelSize.width, this.panelSize.height);
36518         }
36519         if(this.closeBtn){
36520             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36521         }
36522         this.updateTitle(panel.getTitle());
36523         if(this.tabs){
36524             this.fireEvent("invalidated", this);
36525         }
36526         this.fireEvent("panelactivated", this, panel);
36527     },
36528
36529     /**
36530      * Shows the specified panel.
36531      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36532      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36533      */
36534     showPanel : function(panel)
36535     {
36536         panel = this.getPanel(panel);
36537         if(panel){
36538             if(this.tabs){
36539                 var tab = this.tabs.getTab(panel.getEl().id);
36540                 if(tab.isHidden()){
36541                     this.tabs.unhideTab(tab.id);
36542                 }
36543                 tab.activate();
36544             }else{
36545                 this.setActivePanel(panel);
36546             }
36547         }
36548         return panel;
36549     },
36550
36551     /**
36552      * Get the active panel for this region.
36553      * @return {Roo.ContentPanel} The active panel or null
36554      */
36555     getActivePanel : function(){
36556         return this.activePanel;
36557     },
36558
36559     validateVisibility : function(){
36560         if(this.panels.getCount() < 1){
36561             this.updateTitle("&#160;");
36562             this.closeBtn.hide();
36563             this.hide();
36564         }else{
36565             if(!this.isVisible()){
36566                 this.show();
36567             }
36568         }
36569     },
36570
36571     /**
36572      * Adds the passed ContentPanel(s) to this region.
36573      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36574      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36575      */
36576     add : function(panel)
36577     {
36578         if(arguments.length > 1){
36579             for(var i = 0, len = arguments.length; i < len; i++) {
36580                 this.add(arguments[i]);
36581             }
36582             return null;
36583         }
36584         
36585         // if we have not been rendered yet, then we can not really do much of this..
36586         if (!this.bodyEl) {
36587             this.unrendered_panels.push(panel);
36588             return panel;
36589         }
36590         
36591         
36592         
36593         
36594         if(this.hasPanel(panel)){
36595             this.showPanel(panel);
36596             return panel;
36597         }
36598         panel.setRegion(this);
36599         this.panels.add(panel);
36600        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36601             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36602             // and hide them... ???
36603             this.bodyEl.dom.appendChild(panel.getEl().dom);
36604             if(panel.background !== true){
36605                 this.setActivePanel(panel);
36606             }
36607             this.fireEvent("paneladded", this, panel);
36608             return panel;
36609         }
36610         */
36611         if(!this.tabs){
36612             this.initTabs();
36613         }else{
36614             this.initPanelAsTab(panel);
36615         }
36616         
36617         
36618         if(panel.background !== true){
36619             this.tabs.activate(panel.getEl().id);
36620         }
36621         this.fireEvent("paneladded", this, panel);
36622         return panel;
36623     },
36624
36625     /**
36626      * Hides the tab for the specified panel.
36627      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36628      */
36629     hidePanel : function(panel){
36630         if(this.tabs && (panel = this.getPanel(panel))){
36631             this.tabs.hideTab(panel.getEl().id);
36632         }
36633     },
36634
36635     /**
36636      * Unhides the tab for a previously hidden panel.
36637      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36638      */
36639     unhidePanel : function(panel){
36640         if(this.tabs && (panel = this.getPanel(panel))){
36641             this.tabs.unhideTab(panel.getEl().id);
36642         }
36643     },
36644
36645     clearPanels : function(){
36646         while(this.panels.getCount() > 0){
36647              this.remove(this.panels.first());
36648         }
36649     },
36650
36651     /**
36652      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36653      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36654      * @param {Boolean} preservePanel Overrides the config preservePanel option
36655      * @return {Roo.ContentPanel} The panel that was removed
36656      */
36657     remove : function(panel, preservePanel)
36658     {
36659         panel = this.getPanel(panel);
36660         if(!panel){
36661             return null;
36662         }
36663         var e = {};
36664         this.fireEvent("beforeremove", this, panel, e);
36665         if(e.cancel === true){
36666             return null;
36667         }
36668         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36669         var panelId = panel.getId();
36670         this.panels.removeKey(panelId);
36671         if(preservePanel){
36672             document.body.appendChild(panel.getEl().dom);
36673         }
36674         if(this.tabs){
36675             this.tabs.removeTab(panel.getEl().id);
36676         }else if (!preservePanel){
36677             this.bodyEl.dom.removeChild(panel.getEl().dom);
36678         }
36679         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36680             var p = this.panels.first();
36681             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36682             tempEl.appendChild(p.getEl().dom);
36683             this.bodyEl.update("");
36684             this.bodyEl.dom.appendChild(p.getEl().dom);
36685             tempEl = null;
36686             this.updateTitle(p.getTitle());
36687             this.tabs = null;
36688             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36689             this.setActivePanel(p);
36690         }
36691         panel.setRegion(null);
36692         if(this.activePanel == panel){
36693             this.activePanel = null;
36694         }
36695         if(this.config.autoDestroy !== false && preservePanel !== true){
36696             try{panel.destroy();}catch(e){}
36697         }
36698         this.fireEvent("panelremoved", this, panel);
36699         return panel;
36700     },
36701
36702     /**
36703      * Returns the TabPanel component used by this region
36704      * @return {Roo.TabPanel}
36705      */
36706     getTabs : function(){
36707         return this.tabs;
36708     },
36709
36710     createTool : function(parentEl, className){
36711         var btn = Roo.DomHelper.append(parentEl, {
36712             tag: "div",
36713             cls: "x-layout-tools-button",
36714             children: [ {
36715                 tag: "div",
36716                 cls: "roo-layout-tools-button-inner " + className,
36717                 html: "&#160;"
36718             }]
36719         }, true);
36720         btn.addClassOnOver("roo-layout-tools-button-over");
36721         return btn;
36722     }
36723 });/*
36724  * Based on:
36725  * Ext JS Library 1.1.1
36726  * Copyright(c) 2006-2007, Ext JS, LLC.
36727  *
36728  * Originally Released Under LGPL - original licence link has changed is not relivant.
36729  *
36730  * Fork - LGPL
36731  * <script type="text/javascript">
36732  */
36733  
36734
36735
36736 /**
36737  * @class Roo.SplitLayoutRegion
36738  * @extends Roo.LayoutRegion
36739  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36740  */
36741 Roo.bootstrap.layout.Split = function(config){
36742     this.cursor = config.cursor;
36743     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36744 };
36745
36746 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36747 {
36748     splitTip : "Drag to resize.",
36749     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36750     useSplitTips : false,
36751
36752     applyConfig : function(config){
36753         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36754     },
36755     
36756     onRender : function(ctr,pos) {
36757         
36758         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36759         if(!this.config.split){
36760             return;
36761         }
36762         if(!this.split){
36763             
36764             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36765                             tag: "div",
36766                             id: this.el.id + "-split",
36767                             cls: "roo-layout-split roo-layout-split-"+this.position,
36768                             html: "&#160;"
36769             });
36770             /** The SplitBar for this region 
36771             * @type Roo.SplitBar */
36772             // does not exist yet...
36773             Roo.log([this.position, this.orientation]);
36774             
36775             this.split = new Roo.bootstrap.SplitBar({
36776                 dragElement : splitEl,
36777                 resizingElement: this.el,
36778                 orientation : this.orientation
36779             });
36780             
36781             this.split.on("moved", this.onSplitMove, this);
36782             this.split.useShim = this.config.useShim === true;
36783             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36784             if(this.useSplitTips){
36785                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36786             }
36787             //if(config.collapsible){
36788             //    this.split.el.on("dblclick", this.collapse,  this);
36789             //}
36790         }
36791         if(typeof this.config.minSize != "undefined"){
36792             this.split.minSize = this.config.minSize;
36793         }
36794         if(typeof this.config.maxSize != "undefined"){
36795             this.split.maxSize = this.config.maxSize;
36796         }
36797         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36798             this.hideSplitter();
36799         }
36800         
36801     },
36802
36803     getHMaxSize : function(){
36804          var cmax = this.config.maxSize || 10000;
36805          var center = this.mgr.getRegion("center");
36806          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36807     },
36808
36809     getVMaxSize : function(){
36810          var cmax = this.config.maxSize || 10000;
36811          var center = this.mgr.getRegion("center");
36812          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36813     },
36814
36815     onSplitMove : function(split, newSize){
36816         this.fireEvent("resized", this, newSize);
36817     },
36818     
36819     /** 
36820      * Returns the {@link Roo.SplitBar} for this region.
36821      * @return {Roo.SplitBar}
36822      */
36823     getSplitBar : function(){
36824         return this.split;
36825     },
36826     
36827     hide : function(){
36828         this.hideSplitter();
36829         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36830     },
36831
36832     hideSplitter : function(){
36833         if(this.split){
36834             this.split.el.setLocation(-2000,-2000);
36835             this.split.el.hide();
36836         }
36837     },
36838
36839     show : function(){
36840         if(this.split){
36841             this.split.el.show();
36842         }
36843         Roo.bootstrap.layout.Split.superclass.show.call(this);
36844     },
36845     
36846     beforeSlide: function(){
36847         if(Roo.isGecko){// firefox overflow auto bug workaround
36848             this.bodyEl.clip();
36849             if(this.tabs) {
36850                 this.tabs.bodyEl.clip();
36851             }
36852             if(this.activePanel){
36853                 this.activePanel.getEl().clip();
36854                 
36855                 if(this.activePanel.beforeSlide){
36856                     this.activePanel.beforeSlide();
36857                 }
36858             }
36859         }
36860     },
36861     
36862     afterSlide : function(){
36863         if(Roo.isGecko){// firefox overflow auto bug workaround
36864             this.bodyEl.unclip();
36865             if(this.tabs) {
36866                 this.tabs.bodyEl.unclip();
36867             }
36868             if(this.activePanel){
36869                 this.activePanel.getEl().unclip();
36870                 if(this.activePanel.afterSlide){
36871                     this.activePanel.afterSlide();
36872                 }
36873             }
36874         }
36875     },
36876
36877     initAutoHide : function(){
36878         if(this.autoHide !== false){
36879             if(!this.autoHideHd){
36880                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36881                 this.autoHideHd = {
36882                     "mouseout": function(e){
36883                         if(!e.within(this.el, true)){
36884                             st.delay(500);
36885                         }
36886                     },
36887                     "mouseover" : function(e){
36888                         st.cancel();
36889                     },
36890                     scope : this
36891                 };
36892             }
36893             this.el.on(this.autoHideHd);
36894         }
36895     },
36896
36897     clearAutoHide : function(){
36898         if(this.autoHide !== false){
36899             this.el.un("mouseout", this.autoHideHd.mouseout);
36900             this.el.un("mouseover", this.autoHideHd.mouseover);
36901         }
36902     },
36903
36904     clearMonitor : function(){
36905         Roo.get(document).un("click", this.slideInIf, this);
36906     },
36907
36908     // these names are backwards but not changed for compat
36909     slideOut : function(){
36910         if(this.isSlid || this.el.hasActiveFx()){
36911             return;
36912         }
36913         this.isSlid = true;
36914         if(this.collapseBtn){
36915             this.collapseBtn.hide();
36916         }
36917         this.closeBtnState = this.closeBtn.getStyle('display');
36918         this.closeBtn.hide();
36919         if(this.stickBtn){
36920             this.stickBtn.show();
36921         }
36922         this.el.show();
36923         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36924         this.beforeSlide();
36925         this.el.setStyle("z-index", 10001);
36926         this.el.slideIn(this.getSlideAnchor(), {
36927             callback: function(){
36928                 this.afterSlide();
36929                 this.initAutoHide();
36930                 Roo.get(document).on("click", this.slideInIf, this);
36931                 this.fireEvent("slideshow", this);
36932             },
36933             scope: this,
36934             block: true
36935         });
36936     },
36937
36938     afterSlideIn : function(){
36939         this.clearAutoHide();
36940         this.isSlid = false;
36941         this.clearMonitor();
36942         this.el.setStyle("z-index", "");
36943         if(this.collapseBtn){
36944             this.collapseBtn.show();
36945         }
36946         this.closeBtn.setStyle('display', this.closeBtnState);
36947         if(this.stickBtn){
36948             this.stickBtn.hide();
36949         }
36950         this.fireEvent("slidehide", this);
36951     },
36952
36953     slideIn : function(cb){
36954         if(!this.isSlid || this.el.hasActiveFx()){
36955             Roo.callback(cb);
36956             return;
36957         }
36958         this.isSlid = false;
36959         this.beforeSlide();
36960         this.el.slideOut(this.getSlideAnchor(), {
36961             callback: function(){
36962                 this.el.setLeftTop(-10000, -10000);
36963                 this.afterSlide();
36964                 this.afterSlideIn();
36965                 Roo.callback(cb);
36966             },
36967             scope: this,
36968             block: true
36969         });
36970     },
36971     
36972     slideInIf : function(e){
36973         if(!e.within(this.el)){
36974             this.slideIn();
36975         }
36976     },
36977
36978     animateCollapse : function(){
36979         this.beforeSlide();
36980         this.el.setStyle("z-index", 20000);
36981         var anchor = this.getSlideAnchor();
36982         this.el.slideOut(anchor, {
36983             callback : function(){
36984                 this.el.setStyle("z-index", "");
36985                 this.collapsedEl.slideIn(anchor, {duration:.3});
36986                 this.afterSlide();
36987                 this.el.setLocation(-10000,-10000);
36988                 this.el.hide();
36989                 this.fireEvent("collapsed", this);
36990             },
36991             scope: this,
36992             block: true
36993         });
36994     },
36995
36996     animateExpand : function(){
36997         this.beforeSlide();
36998         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36999         this.el.setStyle("z-index", 20000);
37000         this.collapsedEl.hide({
37001             duration:.1
37002         });
37003         this.el.slideIn(this.getSlideAnchor(), {
37004             callback : function(){
37005                 this.el.setStyle("z-index", "");
37006                 this.afterSlide();
37007                 if(this.split){
37008                     this.split.el.show();
37009                 }
37010                 this.fireEvent("invalidated", this);
37011                 this.fireEvent("expanded", this);
37012             },
37013             scope: this,
37014             block: true
37015         });
37016     },
37017
37018     anchors : {
37019         "west" : "left",
37020         "east" : "right",
37021         "north" : "top",
37022         "south" : "bottom"
37023     },
37024
37025     sanchors : {
37026         "west" : "l",
37027         "east" : "r",
37028         "north" : "t",
37029         "south" : "b"
37030     },
37031
37032     canchors : {
37033         "west" : "tl-tr",
37034         "east" : "tr-tl",
37035         "north" : "tl-bl",
37036         "south" : "bl-tl"
37037     },
37038
37039     getAnchor : function(){
37040         return this.anchors[this.position];
37041     },
37042
37043     getCollapseAnchor : function(){
37044         return this.canchors[this.position];
37045     },
37046
37047     getSlideAnchor : function(){
37048         return this.sanchors[this.position];
37049     },
37050
37051     getAlignAdj : function(){
37052         var cm = this.cmargins;
37053         switch(this.position){
37054             case "west":
37055                 return [0, 0];
37056             break;
37057             case "east":
37058                 return [0, 0];
37059             break;
37060             case "north":
37061                 return [0, 0];
37062             break;
37063             case "south":
37064                 return [0, 0];
37065             break;
37066         }
37067     },
37068
37069     getExpandAdj : function(){
37070         var c = this.collapsedEl, cm = this.cmargins;
37071         switch(this.position){
37072             case "west":
37073                 return [-(cm.right+c.getWidth()+cm.left), 0];
37074             break;
37075             case "east":
37076                 return [cm.right+c.getWidth()+cm.left, 0];
37077             break;
37078             case "north":
37079                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37080             break;
37081             case "south":
37082                 return [0, cm.top+cm.bottom+c.getHeight()];
37083             break;
37084         }
37085     }
37086 });/*
37087  * Based on:
37088  * Ext JS Library 1.1.1
37089  * Copyright(c) 2006-2007, Ext JS, LLC.
37090  *
37091  * Originally Released Under LGPL - original licence link has changed is not relivant.
37092  *
37093  * Fork - LGPL
37094  * <script type="text/javascript">
37095  */
37096 /*
37097  * These classes are private internal classes
37098  */
37099 Roo.bootstrap.layout.Center = function(config){
37100     config.region = "center";
37101     Roo.bootstrap.layout.Region.call(this, config);
37102     this.visible = true;
37103     this.minWidth = config.minWidth || 20;
37104     this.minHeight = config.minHeight || 20;
37105 };
37106
37107 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37108     hide : function(){
37109         // center panel can't be hidden
37110     },
37111     
37112     show : function(){
37113         // center panel can't be hidden
37114     },
37115     
37116     getMinWidth: function(){
37117         return this.minWidth;
37118     },
37119     
37120     getMinHeight: function(){
37121         return this.minHeight;
37122     }
37123 });
37124
37125
37126
37127
37128  
37129
37130
37131
37132
37133
37134
37135 Roo.bootstrap.layout.North = function(config)
37136 {
37137     config.region = 'north';
37138     config.cursor = 'n-resize';
37139     
37140     Roo.bootstrap.layout.Split.call(this, config);
37141     
37142     
37143     if(this.split){
37144         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37145         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37146         this.split.el.addClass("roo-layout-split-v");
37147     }
37148     var size = config.initialSize || config.height;
37149     if(typeof size != "undefined"){
37150         this.el.setHeight(size);
37151     }
37152 };
37153 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37154 {
37155     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37156     
37157     
37158     
37159     getBox : function(){
37160         if(this.collapsed){
37161             return this.collapsedEl.getBox();
37162         }
37163         var box = this.el.getBox();
37164         if(this.split){
37165             box.height += this.split.el.getHeight();
37166         }
37167         return box;
37168     },
37169     
37170     updateBox : function(box){
37171         if(this.split && !this.collapsed){
37172             box.height -= this.split.el.getHeight();
37173             this.split.el.setLeft(box.x);
37174             this.split.el.setTop(box.y+box.height);
37175             this.split.el.setWidth(box.width);
37176         }
37177         if(this.collapsed){
37178             this.updateBody(box.width, null);
37179         }
37180         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37181     }
37182 });
37183
37184
37185
37186
37187
37188 Roo.bootstrap.layout.South = function(config){
37189     config.region = 'south';
37190     config.cursor = 's-resize';
37191     Roo.bootstrap.layout.Split.call(this, config);
37192     if(this.split){
37193         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37194         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37195         this.split.el.addClass("roo-layout-split-v");
37196     }
37197     var size = config.initialSize || config.height;
37198     if(typeof size != "undefined"){
37199         this.el.setHeight(size);
37200     }
37201 };
37202
37203 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37204     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37205     getBox : function(){
37206         if(this.collapsed){
37207             return this.collapsedEl.getBox();
37208         }
37209         var box = this.el.getBox();
37210         if(this.split){
37211             var sh = this.split.el.getHeight();
37212             box.height += sh;
37213             box.y -= sh;
37214         }
37215         return box;
37216     },
37217     
37218     updateBox : function(box){
37219         if(this.split && !this.collapsed){
37220             var sh = this.split.el.getHeight();
37221             box.height -= sh;
37222             box.y += sh;
37223             this.split.el.setLeft(box.x);
37224             this.split.el.setTop(box.y-sh);
37225             this.split.el.setWidth(box.width);
37226         }
37227         if(this.collapsed){
37228             this.updateBody(box.width, null);
37229         }
37230         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37231     }
37232 });
37233
37234 Roo.bootstrap.layout.East = function(config){
37235     config.region = "east";
37236     config.cursor = "e-resize";
37237     Roo.bootstrap.layout.Split.call(this, config);
37238     if(this.split){
37239         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37240         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37241         this.split.el.addClass("roo-layout-split-h");
37242     }
37243     var size = config.initialSize || config.width;
37244     if(typeof size != "undefined"){
37245         this.el.setWidth(size);
37246     }
37247 };
37248 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37249     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37250     getBox : function(){
37251         if(this.collapsed){
37252             return this.collapsedEl.getBox();
37253         }
37254         var box = this.el.getBox();
37255         if(this.split){
37256             var sw = this.split.el.getWidth();
37257             box.width += sw;
37258             box.x -= sw;
37259         }
37260         return box;
37261     },
37262
37263     updateBox : function(box){
37264         if(this.split && !this.collapsed){
37265             var sw = this.split.el.getWidth();
37266             box.width -= sw;
37267             this.split.el.setLeft(box.x);
37268             this.split.el.setTop(box.y);
37269             this.split.el.setHeight(box.height);
37270             box.x += sw;
37271         }
37272         if(this.collapsed){
37273             this.updateBody(null, box.height);
37274         }
37275         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37276     }
37277 });
37278
37279 Roo.bootstrap.layout.West = function(config){
37280     config.region = "west";
37281     config.cursor = "w-resize";
37282     
37283     Roo.bootstrap.layout.Split.call(this, config);
37284     if(this.split){
37285         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37286         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37287         this.split.el.addClass("roo-layout-split-h");
37288     }
37289     
37290 };
37291 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37292     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37293     
37294     onRender: function(ctr, pos)
37295     {
37296         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37297         var size = this.config.initialSize || this.config.width;
37298         if(typeof size != "undefined"){
37299             this.el.setWidth(size);
37300         }
37301     },
37302     
37303     getBox : function(){
37304         if(this.collapsed){
37305             return this.collapsedEl.getBox();
37306         }
37307         var box = this.el.getBox();
37308         if(this.split){
37309             box.width += this.split.el.getWidth();
37310         }
37311         return box;
37312     },
37313     
37314     updateBox : function(box){
37315         if(this.split && !this.collapsed){
37316             var sw = this.split.el.getWidth();
37317             box.width -= sw;
37318             this.split.el.setLeft(box.x+box.width);
37319             this.split.el.setTop(box.y);
37320             this.split.el.setHeight(box.height);
37321         }
37322         if(this.collapsed){
37323             this.updateBody(null, box.height);
37324         }
37325         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37326     }
37327 });Roo.namespace("Roo.bootstrap.panel");/*
37328  * Based on:
37329  * Ext JS Library 1.1.1
37330  * Copyright(c) 2006-2007, Ext JS, LLC.
37331  *
37332  * Originally Released Under LGPL - original licence link has changed is not relivant.
37333  *
37334  * Fork - LGPL
37335  * <script type="text/javascript">
37336  */
37337 /**
37338  * @class Roo.ContentPanel
37339  * @extends Roo.util.Observable
37340  * A basic ContentPanel element.
37341  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37342  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37343  * @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
37344  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37345  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37346  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37347  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37348  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37349  * @cfg {String} title          The title for this panel
37350  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37351  * @cfg {String} url            Calls {@link #setUrl} with this value
37352  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37353  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37354  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37355  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37356  * @cfg {Boolean} badges render the badges
37357
37358  * @constructor
37359  * Create a new ContentPanel.
37360  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37361  * @param {String/Object} config A string to set only the title or a config object
37362  * @param {String} content (optional) Set the HTML content for this panel
37363  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37364  */
37365 Roo.bootstrap.panel.Content = function( config){
37366     
37367     this.tpl = config.tpl || false;
37368     
37369     var el = config.el;
37370     var content = config.content;
37371
37372     if(config.autoCreate){ // xtype is available if this is called from factory
37373         el = Roo.id();
37374     }
37375     this.el = Roo.get(el);
37376     if(!this.el && config && config.autoCreate){
37377         if(typeof config.autoCreate == "object"){
37378             if(!config.autoCreate.id){
37379                 config.autoCreate.id = config.id||el;
37380             }
37381             this.el = Roo.DomHelper.append(document.body,
37382                         config.autoCreate, true);
37383         }else{
37384             var elcfg =  {   tag: "div",
37385                             cls: "roo-layout-inactive-content",
37386                             id: config.id||el
37387                             };
37388             if (config.html) {
37389                 elcfg.html = config.html;
37390                 
37391             }
37392                         
37393             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37394         }
37395     } 
37396     this.closable = false;
37397     this.loaded = false;
37398     this.active = false;
37399    
37400       
37401     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37402         
37403         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37404         
37405         this.wrapEl = this.el; //this.el.wrap();
37406         var ti = [];
37407         if (config.toolbar.items) {
37408             ti = config.toolbar.items ;
37409             delete config.toolbar.items ;
37410         }
37411         
37412         var nitems = [];
37413         this.toolbar.render(this.wrapEl, 'before');
37414         for(var i =0;i < ti.length;i++) {
37415           //  Roo.log(['add child', items[i]]);
37416             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37417         }
37418         this.toolbar.items = nitems;
37419         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37420         delete config.toolbar;
37421         
37422     }
37423     /*
37424     // xtype created footer. - not sure if will work as we normally have to render first..
37425     if (this.footer && !this.footer.el && this.footer.xtype) {
37426         if (!this.wrapEl) {
37427             this.wrapEl = this.el.wrap();
37428         }
37429     
37430         this.footer.container = this.wrapEl.createChild();
37431          
37432         this.footer = Roo.factory(this.footer, Roo);
37433         
37434     }
37435     */
37436     
37437      if(typeof config == "string"){
37438         this.title = config;
37439     }else{
37440         Roo.apply(this, config);
37441     }
37442     
37443     if(this.resizeEl){
37444         this.resizeEl = Roo.get(this.resizeEl, true);
37445     }else{
37446         this.resizeEl = this.el;
37447     }
37448     // handle view.xtype
37449     
37450  
37451     
37452     
37453     this.addEvents({
37454         /**
37455          * @event activate
37456          * Fires when this panel is activated. 
37457          * @param {Roo.ContentPanel} this
37458          */
37459         "activate" : true,
37460         /**
37461          * @event deactivate
37462          * Fires when this panel is activated. 
37463          * @param {Roo.ContentPanel} this
37464          */
37465         "deactivate" : true,
37466
37467         /**
37468          * @event resize
37469          * Fires when this panel is resized if fitToFrame is true.
37470          * @param {Roo.ContentPanel} this
37471          * @param {Number} width The width after any component adjustments
37472          * @param {Number} height The height after any component adjustments
37473          */
37474         "resize" : true,
37475         
37476          /**
37477          * @event render
37478          * Fires when this tab is created
37479          * @param {Roo.ContentPanel} this
37480          */
37481         "render" : true
37482         
37483         
37484         
37485     });
37486     
37487
37488     
37489     
37490     if(this.autoScroll){
37491         this.resizeEl.setStyle("overflow", "auto");
37492     } else {
37493         // fix randome scrolling
37494         //this.el.on('scroll', function() {
37495         //    Roo.log('fix random scolling');
37496         //    this.scrollTo('top',0); 
37497         //});
37498     }
37499     content = content || this.content;
37500     if(content){
37501         this.setContent(content);
37502     }
37503     if(config && config.url){
37504         this.setUrl(this.url, this.params, this.loadOnce);
37505     }
37506     
37507     
37508     
37509     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37510     
37511     if (this.view && typeof(this.view.xtype) != 'undefined') {
37512         this.view.el = this.el.appendChild(document.createElement("div"));
37513         this.view = Roo.factory(this.view); 
37514         this.view.render  &&  this.view.render(false, '');  
37515     }
37516     
37517     
37518     this.fireEvent('render', this);
37519 };
37520
37521 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37522     
37523     tabTip : '',
37524     
37525     setRegion : function(region){
37526         this.region = region;
37527         this.setActiveClass(region && !this.background);
37528     },
37529     
37530     
37531     setActiveClass: function(state)
37532     {
37533         if(state){
37534            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37535            this.el.setStyle('position','relative');
37536         }else{
37537            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37538            this.el.setStyle('position', 'absolute');
37539         } 
37540     },
37541     
37542     /**
37543      * Returns the toolbar for this Panel if one was configured. 
37544      * @return {Roo.Toolbar} 
37545      */
37546     getToolbar : function(){
37547         return this.toolbar;
37548     },
37549     
37550     setActiveState : function(active)
37551     {
37552         this.active = active;
37553         this.setActiveClass(active);
37554         if(!active){
37555             if(this.fireEvent("deactivate", this) === false){
37556                 return false;
37557             }
37558             return true;
37559         }
37560         this.fireEvent("activate", this);
37561         return true;
37562     },
37563     /**
37564      * Updates this panel's element
37565      * @param {String} content The new content
37566      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37567     */
37568     setContent : function(content, loadScripts){
37569         this.el.update(content, loadScripts);
37570     },
37571
37572     ignoreResize : function(w, h){
37573         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37574             return true;
37575         }else{
37576             this.lastSize = {width: w, height: h};
37577             return false;
37578         }
37579     },
37580     /**
37581      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37582      * @return {Roo.UpdateManager} The UpdateManager
37583      */
37584     getUpdateManager : function(){
37585         return this.el.getUpdateManager();
37586     },
37587      /**
37588      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37589      * @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:
37590 <pre><code>
37591 panel.load({
37592     url: "your-url.php",
37593     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37594     callback: yourFunction,
37595     scope: yourObject, //(optional scope)
37596     discardUrl: false,
37597     nocache: false,
37598     text: "Loading...",
37599     timeout: 30,
37600     scripts: false
37601 });
37602 </code></pre>
37603      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37604      * 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.
37605      * @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}
37606      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37607      * @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.
37608      * @return {Roo.ContentPanel} this
37609      */
37610     load : function(){
37611         var um = this.el.getUpdateManager();
37612         um.update.apply(um, arguments);
37613         return this;
37614     },
37615
37616
37617     /**
37618      * 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.
37619      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37620      * @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)
37621      * @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)
37622      * @return {Roo.UpdateManager} The UpdateManager
37623      */
37624     setUrl : function(url, params, loadOnce){
37625         if(this.refreshDelegate){
37626             this.removeListener("activate", this.refreshDelegate);
37627         }
37628         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37629         this.on("activate", this.refreshDelegate);
37630         return this.el.getUpdateManager();
37631     },
37632     
37633     _handleRefresh : function(url, params, loadOnce){
37634         if(!loadOnce || !this.loaded){
37635             var updater = this.el.getUpdateManager();
37636             updater.update(url, params, this._setLoaded.createDelegate(this));
37637         }
37638     },
37639     
37640     _setLoaded : function(){
37641         this.loaded = true;
37642     }, 
37643     
37644     /**
37645      * Returns this panel's id
37646      * @return {String} 
37647      */
37648     getId : function(){
37649         return this.el.id;
37650     },
37651     
37652     /** 
37653      * Returns this panel's element - used by regiosn to add.
37654      * @return {Roo.Element} 
37655      */
37656     getEl : function(){
37657         return this.wrapEl || this.el;
37658     },
37659     
37660    
37661     
37662     adjustForComponents : function(width, height)
37663     {
37664         //Roo.log('adjustForComponents ');
37665         if(this.resizeEl != this.el){
37666             width -= this.el.getFrameWidth('lr');
37667             height -= this.el.getFrameWidth('tb');
37668         }
37669         if(this.toolbar){
37670             var te = this.toolbar.getEl();
37671             te.setWidth(width);
37672             height -= te.getHeight();
37673         }
37674         if(this.footer){
37675             var te = this.footer.getEl();
37676             te.setWidth(width);
37677             height -= te.getHeight();
37678         }
37679         
37680         
37681         if(this.adjustments){
37682             width += this.adjustments[0];
37683             height += this.adjustments[1];
37684         }
37685         return {"width": width, "height": height};
37686     },
37687     
37688     setSize : function(width, height){
37689         if(this.fitToFrame && !this.ignoreResize(width, height)){
37690             if(this.fitContainer && this.resizeEl != this.el){
37691                 this.el.setSize(width, height);
37692             }
37693             var size = this.adjustForComponents(width, height);
37694             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37695             this.fireEvent('resize', this, size.width, size.height);
37696         }
37697     },
37698     
37699     /**
37700      * Returns this panel's title
37701      * @return {String} 
37702      */
37703     getTitle : function(){
37704         
37705         if (typeof(this.title) != 'object') {
37706             return this.title;
37707         }
37708         
37709         var t = '';
37710         for (var k in this.title) {
37711             if (!this.title.hasOwnProperty(k)) {
37712                 continue;
37713             }
37714             
37715             if (k.indexOf('-') >= 0) {
37716                 var s = k.split('-');
37717                 for (var i = 0; i<s.length; i++) {
37718                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37719                 }
37720             } else {
37721                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37722             }
37723         }
37724         return t;
37725     },
37726     
37727     /**
37728      * Set this panel's title
37729      * @param {String} title
37730      */
37731     setTitle : function(title){
37732         this.title = title;
37733         if(this.region){
37734             this.region.updatePanelTitle(this, title);
37735         }
37736     },
37737     
37738     /**
37739      * Returns true is this panel was configured to be closable
37740      * @return {Boolean} 
37741      */
37742     isClosable : function(){
37743         return this.closable;
37744     },
37745     
37746     beforeSlide : function(){
37747         this.el.clip();
37748         this.resizeEl.clip();
37749     },
37750     
37751     afterSlide : function(){
37752         this.el.unclip();
37753         this.resizeEl.unclip();
37754     },
37755     
37756     /**
37757      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37758      *   Will fail silently if the {@link #setUrl} method has not been called.
37759      *   This does not activate the panel, just updates its content.
37760      */
37761     refresh : function(){
37762         if(this.refreshDelegate){
37763            this.loaded = false;
37764            this.refreshDelegate();
37765         }
37766     },
37767     
37768     /**
37769      * Destroys this panel
37770      */
37771     destroy : function(){
37772         this.el.removeAllListeners();
37773         var tempEl = document.createElement("span");
37774         tempEl.appendChild(this.el.dom);
37775         tempEl.innerHTML = "";
37776         this.el.remove();
37777         this.el = null;
37778     },
37779     
37780     /**
37781      * form - if the content panel contains a form - this is a reference to it.
37782      * @type {Roo.form.Form}
37783      */
37784     form : false,
37785     /**
37786      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37787      *    This contains a reference to it.
37788      * @type {Roo.View}
37789      */
37790     view : false,
37791     
37792       /**
37793      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37794      * <pre><code>
37795
37796 layout.addxtype({
37797        xtype : 'Form',
37798        items: [ .... ]
37799    }
37800 );
37801
37802 </code></pre>
37803      * @param {Object} cfg Xtype definition of item to add.
37804      */
37805     
37806     
37807     getChildContainer: function () {
37808         return this.getEl();
37809     }
37810     
37811     
37812     /*
37813         var  ret = new Roo.factory(cfg);
37814         return ret;
37815         
37816         
37817         // add form..
37818         if (cfg.xtype.match(/^Form$/)) {
37819             
37820             var el;
37821             //if (this.footer) {
37822             //    el = this.footer.container.insertSibling(false, 'before');
37823             //} else {
37824                 el = this.el.createChild();
37825             //}
37826
37827             this.form = new  Roo.form.Form(cfg);
37828             
37829             
37830             if ( this.form.allItems.length) {
37831                 this.form.render(el.dom);
37832             }
37833             return this.form;
37834         }
37835         // should only have one of theses..
37836         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37837             // views.. should not be just added - used named prop 'view''
37838             
37839             cfg.el = this.el.appendChild(document.createElement("div"));
37840             // factory?
37841             
37842             var ret = new Roo.factory(cfg);
37843              
37844              ret.render && ret.render(false, ''); // render blank..
37845             this.view = ret;
37846             return ret;
37847         }
37848         return false;
37849     }
37850     \*/
37851 });
37852  
37853 /**
37854  * @class Roo.bootstrap.panel.Grid
37855  * @extends Roo.bootstrap.panel.Content
37856  * @constructor
37857  * Create a new GridPanel.
37858  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37859  * @param {Object} config A the config object
37860   
37861  */
37862
37863
37864
37865 Roo.bootstrap.panel.Grid = function(config)
37866 {
37867     
37868       
37869     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37870         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37871
37872     config.el = this.wrapper;
37873     //this.el = this.wrapper;
37874     
37875       if (config.container) {
37876         // ctor'ed from a Border/panel.grid
37877         
37878         
37879         this.wrapper.setStyle("overflow", "hidden");
37880         this.wrapper.addClass('roo-grid-container');
37881
37882     }
37883     
37884     
37885     if(config.toolbar){
37886         var tool_el = this.wrapper.createChild();    
37887         this.toolbar = Roo.factory(config.toolbar);
37888         var ti = [];
37889         if (config.toolbar.items) {
37890             ti = config.toolbar.items ;
37891             delete config.toolbar.items ;
37892         }
37893         
37894         var nitems = [];
37895         this.toolbar.render(tool_el);
37896         for(var i =0;i < ti.length;i++) {
37897           //  Roo.log(['add child', items[i]]);
37898             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37899         }
37900         this.toolbar.items = nitems;
37901         
37902         delete config.toolbar;
37903     }
37904     
37905     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37906     config.grid.scrollBody = true;;
37907     config.grid.monitorWindowResize = false; // turn off autosizing
37908     config.grid.autoHeight = false;
37909     config.grid.autoWidth = false;
37910     
37911     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37912     
37913     if (config.background) {
37914         // render grid on panel activation (if panel background)
37915         this.on('activate', function(gp) {
37916             if (!gp.grid.rendered) {
37917                 gp.grid.render(this.wrapper);
37918                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37919             }
37920         });
37921             
37922     } else {
37923         this.grid.render(this.wrapper);
37924         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37925
37926     }
37927     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37928     // ??? needed ??? config.el = this.wrapper;
37929     
37930     
37931     
37932   
37933     // xtype created footer. - not sure if will work as we normally have to render first..
37934     if (this.footer && !this.footer.el && this.footer.xtype) {
37935         
37936         var ctr = this.grid.getView().getFooterPanel(true);
37937         this.footer.dataSource = this.grid.dataSource;
37938         this.footer = Roo.factory(this.footer, Roo);
37939         this.footer.render(ctr);
37940         
37941     }
37942     
37943     
37944     
37945     
37946      
37947 };
37948
37949 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37950     getId : function(){
37951         return this.grid.id;
37952     },
37953     
37954     /**
37955      * Returns the grid for this panel
37956      * @return {Roo.bootstrap.Table} 
37957      */
37958     getGrid : function(){
37959         return this.grid;    
37960     },
37961     
37962     setSize : function(width, height){
37963         if(!this.ignoreResize(width, height)){
37964             var grid = this.grid;
37965             var size = this.adjustForComponents(width, height);
37966             var gridel = grid.getGridEl();
37967             gridel.setSize(size.width, size.height);
37968             /*
37969             var thd = grid.getGridEl().select('thead',true).first();
37970             var tbd = grid.getGridEl().select('tbody', true).first();
37971             if (tbd) {
37972                 tbd.setSize(width, height - thd.getHeight());
37973             }
37974             */
37975             grid.autoSize();
37976         }
37977     },
37978      
37979     
37980     
37981     beforeSlide : function(){
37982         this.grid.getView().scroller.clip();
37983     },
37984     
37985     afterSlide : function(){
37986         this.grid.getView().scroller.unclip();
37987     },
37988     
37989     destroy : function(){
37990         this.grid.destroy();
37991         delete this.grid;
37992         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37993     }
37994 });
37995
37996 /**
37997  * @class Roo.bootstrap.panel.Nest
37998  * @extends Roo.bootstrap.panel.Content
37999  * @constructor
38000  * Create a new Panel, that can contain a layout.Border.
38001  * 
38002  * 
38003  * @param {Roo.BorderLayout} layout The layout for this panel
38004  * @param {String/Object} config A string to set only the title or a config object
38005  */
38006 Roo.bootstrap.panel.Nest = function(config)
38007 {
38008     // construct with only one argument..
38009     /* FIXME - implement nicer consturctors
38010     if (layout.layout) {
38011         config = layout;
38012         layout = config.layout;
38013         delete config.layout;
38014     }
38015     if (layout.xtype && !layout.getEl) {
38016         // then layout needs constructing..
38017         layout = Roo.factory(layout, Roo);
38018     }
38019     */
38020     
38021     config.el =  config.layout.getEl();
38022     
38023     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38024     
38025     config.layout.monitorWindowResize = false; // turn off autosizing
38026     this.layout = config.layout;
38027     this.layout.getEl().addClass("roo-layout-nested-layout");
38028     this.layout.parent = this;
38029     
38030     
38031     
38032     
38033 };
38034
38035 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38036
38037     setSize : function(width, height){
38038         if(!this.ignoreResize(width, height)){
38039             var size = this.adjustForComponents(width, height);
38040             var el = this.layout.getEl();
38041             if (size.height < 1) {
38042                 el.setWidth(size.width);   
38043             } else {
38044                 el.setSize(size.width, size.height);
38045             }
38046             var touch = el.dom.offsetWidth;
38047             this.layout.layout();
38048             // ie requires a double layout on the first pass
38049             if(Roo.isIE && !this.initialized){
38050                 this.initialized = true;
38051                 this.layout.layout();
38052             }
38053         }
38054     },
38055     
38056     // activate all subpanels if not currently active..
38057     
38058     setActiveState : function(active){
38059         this.active = active;
38060         this.setActiveClass(active);
38061         
38062         if(!active){
38063             this.fireEvent("deactivate", this);
38064             return;
38065         }
38066         
38067         this.fireEvent("activate", this);
38068         // not sure if this should happen before or after..
38069         if (!this.layout) {
38070             return; // should not happen..
38071         }
38072         var reg = false;
38073         for (var r in this.layout.regions) {
38074             reg = this.layout.getRegion(r);
38075             if (reg.getActivePanel()) {
38076                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38077                 reg.setActivePanel(reg.getActivePanel());
38078                 continue;
38079             }
38080             if (!reg.panels.length) {
38081                 continue;
38082             }
38083             reg.showPanel(reg.getPanel(0));
38084         }
38085         
38086         
38087         
38088         
38089     },
38090     
38091     /**
38092      * Returns the nested BorderLayout for this panel
38093      * @return {Roo.BorderLayout} 
38094      */
38095     getLayout : function(){
38096         return this.layout;
38097     },
38098     
38099      /**
38100      * Adds a xtype elements to the layout of the nested panel
38101      * <pre><code>
38102
38103 panel.addxtype({
38104        xtype : 'ContentPanel',
38105        region: 'west',
38106        items: [ .... ]
38107    }
38108 );
38109
38110 panel.addxtype({
38111         xtype : 'NestedLayoutPanel',
38112         region: 'west',
38113         layout: {
38114            center: { },
38115            west: { }   
38116         },
38117         items : [ ... list of content panels or nested layout panels.. ]
38118    }
38119 );
38120 </code></pre>
38121      * @param {Object} cfg Xtype definition of item to add.
38122      */
38123     addxtype : function(cfg) {
38124         return this.layout.addxtype(cfg);
38125     
38126     }
38127 });/*
38128  * Based on:
38129  * Ext JS Library 1.1.1
38130  * Copyright(c) 2006-2007, Ext JS, LLC.
38131  *
38132  * Originally Released Under LGPL - original licence link has changed is not relivant.
38133  *
38134  * Fork - LGPL
38135  * <script type="text/javascript">
38136  */
38137 /**
38138  * @class Roo.TabPanel
38139  * @extends Roo.util.Observable
38140  * A lightweight tab container.
38141  * <br><br>
38142  * Usage:
38143  * <pre><code>
38144 // basic tabs 1, built from existing content
38145 var tabs = new Roo.TabPanel("tabs1");
38146 tabs.addTab("script", "View Script");
38147 tabs.addTab("markup", "View Markup");
38148 tabs.activate("script");
38149
38150 // more advanced tabs, built from javascript
38151 var jtabs = new Roo.TabPanel("jtabs");
38152 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38153
38154 // set up the UpdateManager
38155 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38156 var updater = tab2.getUpdateManager();
38157 updater.setDefaultUrl("ajax1.htm");
38158 tab2.on('activate', updater.refresh, updater, true);
38159
38160 // Use setUrl for Ajax loading
38161 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38162 tab3.setUrl("ajax2.htm", null, true);
38163
38164 // Disabled tab
38165 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38166 tab4.disable();
38167
38168 jtabs.activate("jtabs-1");
38169  * </code></pre>
38170  * @constructor
38171  * Create a new TabPanel.
38172  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38173  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38174  */
38175 Roo.bootstrap.panel.Tabs = function(config){
38176     /**
38177     * The container element for this TabPanel.
38178     * @type Roo.Element
38179     */
38180     this.el = Roo.get(config.el);
38181     delete config.el;
38182     if(config){
38183         if(typeof config == "boolean"){
38184             this.tabPosition = config ? "bottom" : "top";
38185         }else{
38186             Roo.apply(this, config);
38187         }
38188     }
38189     
38190     if(this.tabPosition == "bottom"){
38191         // if tabs are at the bottom = create the body first.
38192         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38193         this.el.addClass("roo-tabs-bottom");
38194     }
38195     // next create the tabs holders
38196     
38197     if (this.tabPosition == "west"){
38198         
38199         var reg = this.region; // fake it..
38200         while (reg) {
38201             if (!reg.mgr.parent) {
38202                 break;
38203             }
38204             reg = reg.mgr.parent.region;
38205         }
38206         Roo.log("got nest?");
38207         Roo.log(reg);
38208         if (reg.mgr.getRegion('west')) {
38209             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38210             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38211             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38212             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38213             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38214         
38215             
38216         }
38217         
38218         
38219     } else {
38220      
38221         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38222         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38223         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38224         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38225     }
38226     
38227     
38228     if(Roo.isIE){
38229         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38230     }
38231     
38232     // finally - if tabs are at the top, then create the body last..
38233     if(this.tabPosition != "bottom"){
38234         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38235          * @type Roo.Element
38236          */
38237         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38238         this.el.addClass("roo-tabs-top");
38239     }
38240     this.items = [];
38241
38242     this.bodyEl.setStyle("position", "relative");
38243
38244     this.active = null;
38245     this.activateDelegate = this.activate.createDelegate(this);
38246
38247     this.addEvents({
38248         /**
38249          * @event tabchange
38250          * Fires when the active tab changes
38251          * @param {Roo.TabPanel} this
38252          * @param {Roo.TabPanelItem} activePanel The new active tab
38253          */
38254         "tabchange": true,
38255         /**
38256          * @event beforetabchange
38257          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38258          * @param {Roo.TabPanel} this
38259          * @param {Object} e Set cancel to true on this object to cancel the tab change
38260          * @param {Roo.TabPanelItem} tab The tab being changed to
38261          */
38262         "beforetabchange" : true
38263     });
38264
38265     Roo.EventManager.onWindowResize(this.onResize, this);
38266     this.cpad = this.el.getPadding("lr");
38267     this.hiddenCount = 0;
38268
38269
38270     // toolbar on the tabbar support...
38271     if (this.toolbar) {
38272         alert("no toolbar support yet");
38273         this.toolbar  = false;
38274         /*
38275         var tcfg = this.toolbar;
38276         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38277         this.toolbar = new Roo.Toolbar(tcfg);
38278         if (Roo.isSafari) {
38279             var tbl = tcfg.container.child('table', true);
38280             tbl.setAttribute('width', '100%');
38281         }
38282         */
38283         
38284     }
38285    
38286
38287
38288     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38289 };
38290
38291 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38292     /*
38293      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38294      */
38295     tabPosition : "top",
38296     /*
38297      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38298      */
38299     currentTabWidth : 0,
38300     /*
38301      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38302      */
38303     minTabWidth : 40,
38304     /*
38305      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38306      */
38307     maxTabWidth : 250,
38308     /*
38309      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38310      */
38311     preferredTabWidth : 175,
38312     /*
38313      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38314      */
38315     resizeTabs : false,
38316     /*
38317      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38318      */
38319     monitorResize : true,
38320     /*
38321      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38322      */
38323     toolbar : false,  // set by caller..
38324     
38325     region : false, /// set by caller
38326     
38327     disableTooltips : true, // not used yet...
38328
38329     /**
38330      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38331      * @param {String} id The id of the div to use <b>or create</b>
38332      * @param {String} text The text for the tab
38333      * @param {String} content (optional) Content to put in the TabPanelItem body
38334      * @param {Boolean} closable (optional) True to create a close icon on the tab
38335      * @return {Roo.TabPanelItem} The created TabPanelItem
38336      */
38337     addTab : function(id, text, content, closable, tpl)
38338     {
38339         var item = new Roo.bootstrap.panel.TabItem({
38340             panel: this,
38341             id : id,
38342             text : text,
38343             closable : closable,
38344             tpl : tpl
38345         });
38346         this.addTabItem(item);
38347         if(content){
38348             item.setContent(content);
38349         }
38350         return item;
38351     },
38352
38353     /**
38354      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38355      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38356      * @return {Roo.TabPanelItem}
38357      */
38358     getTab : function(id){
38359         return this.items[id];
38360     },
38361
38362     /**
38363      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38364      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38365      */
38366     hideTab : function(id){
38367         var t = this.items[id];
38368         if(!t.isHidden()){
38369            t.setHidden(true);
38370            this.hiddenCount++;
38371            this.autoSizeTabs();
38372         }
38373     },
38374
38375     /**
38376      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38377      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38378      */
38379     unhideTab : function(id){
38380         var t = this.items[id];
38381         if(t.isHidden()){
38382            t.setHidden(false);
38383            this.hiddenCount--;
38384            this.autoSizeTabs();
38385         }
38386     },
38387
38388     /**
38389      * Adds an existing {@link Roo.TabPanelItem}.
38390      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38391      */
38392     addTabItem : function(item)
38393     {
38394         this.items[item.id] = item;
38395         this.items.push(item);
38396         this.autoSizeTabs();
38397       //  if(this.resizeTabs){
38398     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38399   //         this.autoSizeTabs();
38400 //        }else{
38401 //            item.autoSize();
38402        // }
38403     },
38404
38405     /**
38406      * Removes a {@link Roo.TabPanelItem}.
38407      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38408      */
38409     removeTab : function(id){
38410         var items = this.items;
38411         var tab = items[id];
38412         if(!tab) { return; }
38413         var index = items.indexOf(tab);
38414         if(this.active == tab && items.length > 1){
38415             var newTab = this.getNextAvailable(index);
38416             if(newTab) {
38417                 newTab.activate();
38418             }
38419         }
38420         this.stripEl.dom.removeChild(tab.pnode.dom);
38421         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38422             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38423         }
38424         items.splice(index, 1);
38425         delete this.items[tab.id];
38426         tab.fireEvent("close", tab);
38427         tab.purgeListeners();
38428         this.autoSizeTabs();
38429     },
38430
38431     getNextAvailable : function(start){
38432         var items = this.items;
38433         var index = start;
38434         // look for a next tab that will slide over to
38435         // replace the one being removed
38436         while(index < items.length){
38437             var item = items[++index];
38438             if(item && !item.isHidden()){
38439                 return item;
38440             }
38441         }
38442         // if one isn't found select the previous tab (on the left)
38443         index = start;
38444         while(index >= 0){
38445             var item = items[--index];
38446             if(item && !item.isHidden()){
38447                 return item;
38448             }
38449         }
38450         return null;
38451     },
38452
38453     /**
38454      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38455      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38456      */
38457     disableTab : function(id){
38458         var tab = this.items[id];
38459         if(tab && this.active != tab){
38460             tab.disable();
38461         }
38462     },
38463
38464     /**
38465      * Enables a {@link Roo.TabPanelItem} that is disabled.
38466      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38467      */
38468     enableTab : function(id){
38469         var tab = this.items[id];
38470         tab.enable();
38471     },
38472
38473     /**
38474      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38475      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38476      * @return {Roo.TabPanelItem} The TabPanelItem.
38477      */
38478     activate : function(id)
38479     {
38480         //Roo.log('activite:'  + id);
38481         
38482         var tab = this.items[id];
38483         if(!tab){
38484             return null;
38485         }
38486         if(tab == this.active || tab.disabled){
38487             return tab;
38488         }
38489         var e = {};
38490         this.fireEvent("beforetabchange", this, e, tab);
38491         if(e.cancel !== true && !tab.disabled){
38492             if(this.active){
38493                 this.active.hide();
38494             }
38495             this.active = this.items[id];
38496             this.active.show();
38497             this.fireEvent("tabchange", this, this.active);
38498         }
38499         return tab;
38500     },
38501
38502     /**
38503      * Gets the active {@link Roo.TabPanelItem}.
38504      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38505      */
38506     getActiveTab : function(){
38507         return this.active;
38508     },
38509
38510     /**
38511      * Updates the tab body element to fit the height of the container element
38512      * for overflow scrolling
38513      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38514      */
38515     syncHeight : function(targetHeight){
38516         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38517         var bm = this.bodyEl.getMargins();
38518         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38519         this.bodyEl.setHeight(newHeight);
38520         return newHeight;
38521     },
38522
38523     onResize : function(){
38524         if(this.monitorResize){
38525             this.autoSizeTabs();
38526         }
38527     },
38528
38529     /**
38530      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38531      */
38532     beginUpdate : function(){
38533         this.updating = true;
38534     },
38535
38536     /**
38537      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38538      */
38539     endUpdate : function(){
38540         this.updating = false;
38541         this.autoSizeTabs();
38542     },
38543
38544     /**
38545      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38546      */
38547     autoSizeTabs : function()
38548     {
38549         var count = this.items.length;
38550         var vcount = count - this.hiddenCount;
38551         
38552         if (vcount < 2) {
38553             this.stripEl.hide();
38554         } else {
38555             this.stripEl.show();
38556         }
38557         
38558         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38559             return;
38560         }
38561         
38562         
38563         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38564         var availWidth = Math.floor(w / vcount);
38565         var b = this.stripBody;
38566         if(b.getWidth() > w){
38567             var tabs = this.items;
38568             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38569             if(availWidth < this.minTabWidth){
38570                 /*if(!this.sleft){    // incomplete scrolling code
38571                     this.createScrollButtons();
38572                 }
38573                 this.showScroll();
38574                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38575             }
38576         }else{
38577             if(this.currentTabWidth < this.preferredTabWidth){
38578                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38579             }
38580         }
38581     },
38582
38583     /**
38584      * Returns the number of tabs in this TabPanel.
38585      * @return {Number}
38586      */
38587      getCount : function(){
38588          return this.items.length;
38589      },
38590
38591     /**
38592      * Resizes all the tabs to the passed width
38593      * @param {Number} The new width
38594      */
38595     setTabWidth : function(width){
38596         this.currentTabWidth = width;
38597         for(var i = 0, len = this.items.length; i < len; i++) {
38598                 if(!this.items[i].isHidden()) {
38599                 this.items[i].setWidth(width);
38600             }
38601         }
38602     },
38603
38604     /**
38605      * Destroys this TabPanel
38606      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38607      */
38608     destroy : function(removeEl){
38609         Roo.EventManager.removeResizeListener(this.onResize, this);
38610         for(var i = 0, len = this.items.length; i < len; i++){
38611             this.items[i].purgeListeners();
38612         }
38613         if(removeEl === true){
38614             this.el.update("");
38615             this.el.remove();
38616         }
38617     },
38618     
38619     createStrip : function(container)
38620     {
38621         var strip = document.createElement("nav");
38622         strip.className = Roo.bootstrap.version == 4 ?
38623             "navbar-light bg-light" : 
38624             "navbar navbar-default"; //"x-tabs-wrap";
38625         container.appendChild(strip);
38626         return strip;
38627     },
38628     
38629     createStripList : function(strip)
38630     {
38631         // div wrapper for retard IE
38632         // returns the "tr" element.
38633         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38634         //'<div class="x-tabs-strip-wrap">'+
38635           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38636           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38637         return strip.firstChild; //.firstChild.firstChild.firstChild;
38638     },
38639     createBody : function(container)
38640     {
38641         var body = document.createElement("div");
38642         Roo.id(body, "tab-body");
38643         //Roo.fly(body).addClass("x-tabs-body");
38644         Roo.fly(body).addClass("tab-content");
38645         container.appendChild(body);
38646         return body;
38647     },
38648     createItemBody :function(bodyEl, id){
38649         var body = Roo.getDom(id);
38650         if(!body){
38651             body = document.createElement("div");
38652             body.id = id;
38653         }
38654         //Roo.fly(body).addClass("x-tabs-item-body");
38655         Roo.fly(body).addClass("tab-pane");
38656          bodyEl.insertBefore(body, bodyEl.firstChild);
38657         return body;
38658     },
38659     /** @private */
38660     createStripElements :  function(stripEl, text, closable, tpl)
38661     {
38662         var td = document.createElement("li"); // was td..
38663         td.className = 'nav-item';
38664         
38665         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38666         
38667         
38668         stripEl.appendChild(td);
38669         /*if(closable){
38670             td.className = "x-tabs-closable";
38671             if(!this.closeTpl){
38672                 this.closeTpl = new Roo.Template(
38673                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38674                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38675                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38676                 );
38677             }
38678             var el = this.closeTpl.overwrite(td, {"text": text});
38679             var close = el.getElementsByTagName("div")[0];
38680             var inner = el.getElementsByTagName("em")[0];
38681             return {"el": el, "close": close, "inner": inner};
38682         } else {
38683         */
38684         // not sure what this is..
38685 //            if(!this.tabTpl){
38686                 //this.tabTpl = new Roo.Template(
38687                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38688                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38689                 //);
38690 //                this.tabTpl = new Roo.Template(
38691 //                   '<a href="#">' +
38692 //                   '<span unselectable="on"' +
38693 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38694 //                            ' >{text}</span></a>'
38695 //                );
38696 //                
38697 //            }
38698
38699
38700             var template = tpl || this.tabTpl || false;
38701             
38702             if(!template){
38703                 template =  new Roo.Template(
38704                         Roo.bootstrap.version == 4 ? 
38705                             (
38706                                 '<a class="nav-link" href="#" unselectable="on"' +
38707                                      (this.disableTooltips ? '' : ' title="{text}"') +
38708                                      ' >{text}</a>'
38709                             ) : (
38710                                 '<a class="nav-link" href="#">' +
38711                                 '<span unselectable="on"' +
38712                                          (this.disableTooltips ? '' : ' title="{text}"') +
38713                                     ' >{text}</span></a>'
38714                             )
38715                 );
38716             }
38717             
38718             switch (typeof(template)) {
38719                 case 'object' :
38720                     break;
38721                 case 'string' :
38722                     template = new Roo.Template(template);
38723                     break;
38724                 default :
38725                     break;
38726             }
38727             
38728             var el = template.overwrite(td, {"text": text});
38729             
38730             var inner = el.getElementsByTagName("span")[0];
38731             
38732             return {"el": el, "inner": inner};
38733             
38734     }
38735         
38736     
38737 });
38738
38739 /**
38740  * @class Roo.TabPanelItem
38741  * @extends Roo.util.Observable
38742  * Represents an individual item (tab plus body) in a TabPanel.
38743  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38744  * @param {String} id The id of this TabPanelItem
38745  * @param {String} text The text for the tab of this TabPanelItem
38746  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38747  */
38748 Roo.bootstrap.panel.TabItem = function(config){
38749     /**
38750      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38751      * @type Roo.TabPanel
38752      */
38753     this.tabPanel = config.panel;
38754     /**
38755      * The id for this TabPanelItem
38756      * @type String
38757      */
38758     this.id = config.id;
38759     /** @private */
38760     this.disabled = false;
38761     /** @private */
38762     this.text = config.text;
38763     /** @private */
38764     this.loaded = false;
38765     this.closable = config.closable;
38766
38767     /**
38768      * The body element for this TabPanelItem.
38769      * @type Roo.Element
38770      */
38771     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38772     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38773     this.bodyEl.setStyle("display", "block");
38774     this.bodyEl.setStyle("zoom", "1");
38775     //this.hideAction();
38776
38777     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38778     /** @private */
38779     this.el = Roo.get(els.el);
38780     this.inner = Roo.get(els.inner, true);
38781      this.textEl = Roo.bootstrap.version == 4 ?
38782         this.el : Roo.get(this.el.dom.firstChild, true);
38783
38784     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38785     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38786
38787     
38788 //    this.el.on("mousedown", this.onTabMouseDown, this);
38789     this.el.on("click", this.onTabClick, this);
38790     /** @private */
38791     if(config.closable){
38792         var c = Roo.get(els.close, true);
38793         c.dom.title = this.closeText;
38794         c.addClassOnOver("close-over");
38795         c.on("click", this.closeClick, this);
38796      }
38797
38798     this.addEvents({
38799          /**
38800          * @event activate
38801          * Fires when this tab becomes the active tab.
38802          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38803          * @param {Roo.TabPanelItem} this
38804          */
38805         "activate": true,
38806         /**
38807          * @event beforeclose
38808          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38809          * @param {Roo.TabPanelItem} this
38810          * @param {Object} e Set cancel to true on this object to cancel the close.
38811          */
38812         "beforeclose": true,
38813         /**
38814          * @event close
38815          * Fires when this tab is closed.
38816          * @param {Roo.TabPanelItem} this
38817          */
38818          "close": true,
38819         /**
38820          * @event deactivate
38821          * Fires when this tab is no longer the active tab.
38822          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38823          * @param {Roo.TabPanelItem} this
38824          */
38825          "deactivate" : true
38826     });
38827     this.hidden = false;
38828
38829     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38830 };
38831
38832 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38833            {
38834     purgeListeners : function(){
38835        Roo.util.Observable.prototype.purgeListeners.call(this);
38836        this.el.removeAllListeners();
38837     },
38838     /**
38839      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38840      */
38841     show : function(){
38842         this.status_node.addClass("active");
38843         this.showAction();
38844         if(Roo.isOpera){
38845             this.tabPanel.stripWrap.repaint();
38846         }
38847         this.fireEvent("activate", this.tabPanel, this);
38848     },
38849
38850     /**
38851      * Returns true if this tab is the active tab.
38852      * @return {Boolean}
38853      */
38854     isActive : function(){
38855         return this.tabPanel.getActiveTab() == this;
38856     },
38857
38858     /**
38859      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38860      */
38861     hide : function(){
38862         this.status_node.removeClass("active");
38863         this.hideAction();
38864         this.fireEvent("deactivate", this.tabPanel, this);
38865     },
38866
38867     hideAction : function(){
38868         this.bodyEl.hide();
38869         this.bodyEl.setStyle("position", "absolute");
38870         this.bodyEl.setLeft("-20000px");
38871         this.bodyEl.setTop("-20000px");
38872     },
38873
38874     showAction : function(){
38875         this.bodyEl.setStyle("position", "relative");
38876         this.bodyEl.setTop("");
38877         this.bodyEl.setLeft("");
38878         this.bodyEl.show();
38879     },
38880
38881     /**
38882      * Set the tooltip for the tab.
38883      * @param {String} tooltip The tab's tooltip
38884      */
38885     setTooltip : function(text){
38886         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38887             this.textEl.dom.qtip = text;
38888             this.textEl.dom.removeAttribute('title');
38889         }else{
38890             this.textEl.dom.title = text;
38891         }
38892     },
38893
38894     onTabClick : function(e){
38895         e.preventDefault();
38896         this.tabPanel.activate(this.id);
38897     },
38898
38899     onTabMouseDown : function(e){
38900         e.preventDefault();
38901         this.tabPanel.activate(this.id);
38902     },
38903 /*
38904     getWidth : function(){
38905         return this.inner.getWidth();
38906     },
38907
38908     setWidth : function(width){
38909         var iwidth = width - this.linode.getPadding("lr");
38910         this.inner.setWidth(iwidth);
38911         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38912         this.linode.setWidth(width);
38913     },
38914 */
38915     /**
38916      * Show or hide the tab
38917      * @param {Boolean} hidden True to hide or false to show.
38918      */
38919     setHidden : function(hidden){
38920         this.hidden = hidden;
38921         this.linode.setStyle("display", hidden ? "none" : "");
38922     },
38923
38924     /**
38925      * Returns true if this tab is "hidden"
38926      * @return {Boolean}
38927      */
38928     isHidden : function(){
38929         return this.hidden;
38930     },
38931
38932     /**
38933      * Returns the text for this tab
38934      * @return {String}
38935      */
38936     getText : function(){
38937         return this.text;
38938     },
38939     /*
38940     autoSize : function(){
38941         //this.el.beginMeasure();
38942         this.textEl.setWidth(1);
38943         /*
38944          *  #2804 [new] Tabs in Roojs
38945          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38946          */
38947         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38948         //this.el.endMeasure();
38949     //},
38950
38951     /**
38952      * Sets the text for the tab (Note: this also sets the tooltip text)
38953      * @param {String} text The tab's text and tooltip
38954      */
38955     setText : function(text){
38956         this.text = text;
38957         this.textEl.update(text);
38958         this.setTooltip(text);
38959         //if(!this.tabPanel.resizeTabs){
38960         //    this.autoSize();
38961         //}
38962     },
38963     /**
38964      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38965      */
38966     activate : function(){
38967         this.tabPanel.activate(this.id);
38968     },
38969
38970     /**
38971      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38972      */
38973     disable : function(){
38974         if(this.tabPanel.active != this){
38975             this.disabled = true;
38976             this.status_node.addClass("disabled");
38977         }
38978     },
38979
38980     /**
38981      * Enables this TabPanelItem if it was previously disabled.
38982      */
38983     enable : function(){
38984         this.disabled = false;
38985         this.status_node.removeClass("disabled");
38986     },
38987
38988     /**
38989      * Sets the content for this TabPanelItem.
38990      * @param {String} content The content
38991      * @param {Boolean} loadScripts true to look for and load scripts
38992      */
38993     setContent : function(content, loadScripts){
38994         this.bodyEl.update(content, loadScripts);
38995     },
38996
38997     /**
38998      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38999      * @return {Roo.UpdateManager} The UpdateManager
39000      */
39001     getUpdateManager : function(){
39002         return this.bodyEl.getUpdateManager();
39003     },
39004
39005     /**
39006      * Set a URL to be used to load the content for this TabPanelItem.
39007      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39008      * @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)
39009      * @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)
39010      * @return {Roo.UpdateManager} The UpdateManager
39011      */
39012     setUrl : function(url, params, loadOnce){
39013         if(this.refreshDelegate){
39014             this.un('activate', this.refreshDelegate);
39015         }
39016         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39017         this.on("activate", this.refreshDelegate);
39018         return this.bodyEl.getUpdateManager();
39019     },
39020
39021     /** @private */
39022     _handleRefresh : function(url, params, loadOnce){
39023         if(!loadOnce || !this.loaded){
39024             var updater = this.bodyEl.getUpdateManager();
39025             updater.update(url, params, this._setLoaded.createDelegate(this));
39026         }
39027     },
39028
39029     /**
39030      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
39031      *   Will fail silently if the setUrl method has not been called.
39032      *   This does not activate the panel, just updates its content.
39033      */
39034     refresh : function(){
39035         if(this.refreshDelegate){
39036            this.loaded = false;
39037            this.refreshDelegate();
39038         }
39039     },
39040
39041     /** @private */
39042     _setLoaded : function(){
39043         this.loaded = true;
39044     },
39045
39046     /** @private */
39047     closeClick : function(e){
39048         var o = {};
39049         e.stopEvent();
39050         this.fireEvent("beforeclose", this, o);
39051         if(o.cancel !== true){
39052             this.tabPanel.removeTab(this.id);
39053         }
39054     },
39055     /**
39056      * The text displayed in the tooltip for the close icon.
39057      * @type String
39058      */
39059     closeText : "Close this tab"
39060 });
39061 /**
39062 *    This script refer to:
39063 *    Title: International Telephone Input
39064 *    Author: Jack O'Connor
39065 *    Code version:  v12.1.12
39066 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39067 **/
39068
39069 Roo.bootstrap.PhoneInputData = function() {
39070     var d = [
39071       [
39072         "Afghanistan (‫افغانستان‬‎)",
39073         "af",
39074         "93"
39075       ],
39076       [
39077         "Albania (Shqipëri)",
39078         "al",
39079         "355"
39080       ],
39081       [
39082         "Algeria (‫الجزائر‬‎)",
39083         "dz",
39084         "213"
39085       ],
39086       [
39087         "American Samoa",
39088         "as",
39089         "1684"
39090       ],
39091       [
39092         "Andorra",
39093         "ad",
39094         "376"
39095       ],
39096       [
39097         "Angola",
39098         "ao",
39099         "244"
39100       ],
39101       [
39102         "Anguilla",
39103         "ai",
39104         "1264"
39105       ],
39106       [
39107         "Antigua and Barbuda",
39108         "ag",
39109         "1268"
39110       ],
39111       [
39112         "Argentina",
39113         "ar",
39114         "54"
39115       ],
39116       [
39117         "Armenia (Հայաստան)",
39118         "am",
39119         "374"
39120       ],
39121       [
39122         "Aruba",
39123         "aw",
39124         "297"
39125       ],
39126       [
39127         "Australia",
39128         "au",
39129         "61",
39130         0
39131       ],
39132       [
39133         "Austria (Österreich)",
39134         "at",
39135         "43"
39136       ],
39137       [
39138         "Azerbaijan (Azərbaycan)",
39139         "az",
39140         "994"
39141       ],
39142       [
39143         "Bahamas",
39144         "bs",
39145         "1242"
39146       ],
39147       [
39148         "Bahrain (‫البحرين‬‎)",
39149         "bh",
39150         "973"
39151       ],
39152       [
39153         "Bangladesh (বাংলাদেশ)",
39154         "bd",
39155         "880"
39156       ],
39157       [
39158         "Barbados",
39159         "bb",
39160         "1246"
39161       ],
39162       [
39163         "Belarus (Беларусь)",
39164         "by",
39165         "375"
39166       ],
39167       [
39168         "Belgium (België)",
39169         "be",
39170         "32"
39171       ],
39172       [
39173         "Belize",
39174         "bz",
39175         "501"
39176       ],
39177       [
39178         "Benin (Bénin)",
39179         "bj",
39180         "229"
39181       ],
39182       [
39183         "Bermuda",
39184         "bm",
39185         "1441"
39186       ],
39187       [
39188         "Bhutan (འབྲུག)",
39189         "bt",
39190         "975"
39191       ],
39192       [
39193         "Bolivia",
39194         "bo",
39195         "591"
39196       ],
39197       [
39198         "Bosnia and Herzegovina (Босна и Херцеговина)",
39199         "ba",
39200         "387"
39201       ],
39202       [
39203         "Botswana",
39204         "bw",
39205         "267"
39206       ],
39207       [
39208         "Brazil (Brasil)",
39209         "br",
39210         "55"
39211       ],
39212       [
39213         "British Indian Ocean Territory",
39214         "io",
39215         "246"
39216       ],
39217       [
39218         "British Virgin Islands",
39219         "vg",
39220         "1284"
39221       ],
39222       [
39223         "Brunei",
39224         "bn",
39225         "673"
39226       ],
39227       [
39228         "Bulgaria (България)",
39229         "bg",
39230         "359"
39231       ],
39232       [
39233         "Burkina Faso",
39234         "bf",
39235         "226"
39236       ],
39237       [
39238         "Burundi (Uburundi)",
39239         "bi",
39240         "257"
39241       ],
39242       [
39243         "Cambodia (កម្ពុជា)",
39244         "kh",
39245         "855"
39246       ],
39247       [
39248         "Cameroon (Cameroun)",
39249         "cm",
39250         "237"
39251       ],
39252       [
39253         "Canada",
39254         "ca",
39255         "1",
39256         1,
39257         ["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"]
39258       ],
39259       [
39260         "Cape Verde (Kabu Verdi)",
39261         "cv",
39262         "238"
39263       ],
39264       [
39265         "Caribbean Netherlands",
39266         "bq",
39267         "599",
39268         1
39269       ],
39270       [
39271         "Cayman Islands",
39272         "ky",
39273         "1345"
39274       ],
39275       [
39276         "Central African Republic (République centrafricaine)",
39277         "cf",
39278         "236"
39279       ],
39280       [
39281         "Chad (Tchad)",
39282         "td",
39283         "235"
39284       ],
39285       [
39286         "Chile",
39287         "cl",
39288         "56"
39289       ],
39290       [
39291         "China (中国)",
39292         "cn",
39293         "86"
39294       ],
39295       [
39296         "Christmas Island",
39297         "cx",
39298         "61",
39299         2
39300       ],
39301       [
39302         "Cocos (Keeling) Islands",
39303         "cc",
39304         "61",
39305         1
39306       ],
39307       [
39308         "Colombia",
39309         "co",
39310         "57"
39311       ],
39312       [
39313         "Comoros (‫جزر القمر‬‎)",
39314         "km",
39315         "269"
39316       ],
39317       [
39318         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39319         "cd",
39320         "243"
39321       ],
39322       [
39323         "Congo (Republic) (Congo-Brazzaville)",
39324         "cg",
39325         "242"
39326       ],
39327       [
39328         "Cook Islands",
39329         "ck",
39330         "682"
39331       ],
39332       [
39333         "Costa Rica",
39334         "cr",
39335         "506"
39336       ],
39337       [
39338         "Côte d’Ivoire",
39339         "ci",
39340         "225"
39341       ],
39342       [
39343         "Croatia (Hrvatska)",
39344         "hr",
39345         "385"
39346       ],
39347       [
39348         "Cuba",
39349         "cu",
39350         "53"
39351       ],
39352       [
39353         "Curaçao",
39354         "cw",
39355         "599",
39356         0
39357       ],
39358       [
39359         "Cyprus (Κύπρος)",
39360         "cy",
39361         "357"
39362       ],
39363       [
39364         "Czech Republic (Česká republika)",
39365         "cz",
39366         "420"
39367       ],
39368       [
39369         "Denmark (Danmark)",
39370         "dk",
39371         "45"
39372       ],
39373       [
39374         "Djibouti",
39375         "dj",
39376         "253"
39377       ],
39378       [
39379         "Dominica",
39380         "dm",
39381         "1767"
39382       ],
39383       [
39384         "Dominican Republic (República Dominicana)",
39385         "do",
39386         "1",
39387         2,
39388         ["809", "829", "849"]
39389       ],
39390       [
39391         "Ecuador",
39392         "ec",
39393         "593"
39394       ],
39395       [
39396         "Egypt (‫مصر‬‎)",
39397         "eg",
39398         "20"
39399       ],
39400       [
39401         "El Salvador",
39402         "sv",
39403         "503"
39404       ],
39405       [
39406         "Equatorial Guinea (Guinea Ecuatorial)",
39407         "gq",
39408         "240"
39409       ],
39410       [
39411         "Eritrea",
39412         "er",
39413         "291"
39414       ],
39415       [
39416         "Estonia (Eesti)",
39417         "ee",
39418         "372"
39419       ],
39420       [
39421         "Ethiopia",
39422         "et",
39423         "251"
39424       ],
39425       [
39426         "Falkland Islands (Islas Malvinas)",
39427         "fk",
39428         "500"
39429       ],
39430       [
39431         "Faroe Islands (Føroyar)",
39432         "fo",
39433         "298"
39434       ],
39435       [
39436         "Fiji",
39437         "fj",
39438         "679"
39439       ],
39440       [
39441         "Finland (Suomi)",
39442         "fi",
39443         "358",
39444         0
39445       ],
39446       [
39447         "France",
39448         "fr",
39449         "33"
39450       ],
39451       [
39452         "French Guiana (Guyane française)",
39453         "gf",
39454         "594"
39455       ],
39456       [
39457         "French Polynesia (Polynésie française)",
39458         "pf",
39459         "689"
39460       ],
39461       [
39462         "Gabon",
39463         "ga",
39464         "241"
39465       ],
39466       [
39467         "Gambia",
39468         "gm",
39469         "220"
39470       ],
39471       [
39472         "Georgia (საქართველო)",
39473         "ge",
39474         "995"
39475       ],
39476       [
39477         "Germany (Deutschland)",
39478         "de",
39479         "49"
39480       ],
39481       [
39482         "Ghana (Gaana)",
39483         "gh",
39484         "233"
39485       ],
39486       [
39487         "Gibraltar",
39488         "gi",
39489         "350"
39490       ],
39491       [
39492         "Greece (Ελλάδα)",
39493         "gr",
39494         "30"
39495       ],
39496       [
39497         "Greenland (Kalaallit Nunaat)",
39498         "gl",
39499         "299"
39500       ],
39501       [
39502         "Grenada",
39503         "gd",
39504         "1473"
39505       ],
39506       [
39507         "Guadeloupe",
39508         "gp",
39509         "590",
39510         0
39511       ],
39512       [
39513         "Guam",
39514         "gu",
39515         "1671"
39516       ],
39517       [
39518         "Guatemala",
39519         "gt",
39520         "502"
39521       ],
39522       [
39523         "Guernsey",
39524         "gg",
39525         "44",
39526         1
39527       ],
39528       [
39529         "Guinea (Guinée)",
39530         "gn",
39531         "224"
39532       ],
39533       [
39534         "Guinea-Bissau (Guiné Bissau)",
39535         "gw",
39536         "245"
39537       ],
39538       [
39539         "Guyana",
39540         "gy",
39541         "592"
39542       ],
39543       [
39544         "Haiti",
39545         "ht",
39546         "509"
39547       ],
39548       [
39549         "Honduras",
39550         "hn",
39551         "504"
39552       ],
39553       [
39554         "Hong Kong (香港)",
39555         "hk",
39556         "852"
39557       ],
39558       [
39559         "Hungary (Magyarország)",
39560         "hu",
39561         "36"
39562       ],
39563       [
39564         "Iceland (Ísland)",
39565         "is",
39566         "354"
39567       ],
39568       [
39569         "India (भारत)",
39570         "in",
39571         "91"
39572       ],
39573       [
39574         "Indonesia",
39575         "id",
39576         "62"
39577       ],
39578       [
39579         "Iran (‫ایران‬‎)",
39580         "ir",
39581         "98"
39582       ],
39583       [
39584         "Iraq (‫العراق‬‎)",
39585         "iq",
39586         "964"
39587       ],
39588       [
39589         "Ireland",
39590         "ie",
39591         "353"
39592       ],
39593       [
39594         "Isle of Man",
39595         "im",
39596         "44",
39597         2
39598       ],
39599       [
39600         "Israel (‫ישראל‬‎)",
39601         "il",
39602         "972"
39603       ],
39604       [
39605         "Italy (Italia)",
39606         "it",
39607         "39",
39608         0
39609       ],
39610       [
39611         "Jamaica",
39612         "jm",
39613         "1876"
39614       ],
39615       [
39616         "Japan (日本)",
39617         "jp",
39618         "81"
39619       ],
39620       [
39621         "Jersey",
39622         "je",
39623         "44",
39624         3
39625       ],
39626       [
39627         "Jordan (‫الأردن‬‎)",
39628         "jo",
39629         "962"
39630       ],
39631       [
39632         "Kazakhstan (Казахстан)",
39633         "kz",
39634         "7",
39635         1
39636       ],
39637       [
39638         "Kenya",
39639         "ke",
39640         "254"
39641       ],
39642       [
39643         "Kiribati",
39644         "ki",
39645         "686"
39646       ],
39647       [
39648         "Kosovo",
39649         "xk",
39650         "383"
39651       ],
39652       [
39653         "Kuwait (‫الكويت‬‎)",
39654         "kw",
39655         "965"
39656       ],
39657       [
39658         "Kyrgyzstan (Кыргызстан)",
39659         "kg",
39660         "996"
39661       ],
39662       [
39663         "Laos (ລາວ)",
39664         "la",
39665         "856"
39666       ],
39667       [
39668         "Latvia (Latvija)",
39669         "lv",
39670         "371"
39671       ],
39672       [
39673         "Lebanon (‫لبنان‬‎)",
39674         "lb",
39675         "961"
39676       ],
39677       [
39678         "Lesotho",
39679         "ls",
39680         "266"
39681       ],
39682       [
39683         "Liberia",
39684         "lr",
39685         "231"
39686       ],
39687       [
39688         "Libya (‫ليبيا‬‎)",
39689         "ly",
39690         "218"
39691       ],
39692       [
39693         "Liechtenstein",
39694         "li",
39695         "423"
39696       ],
39697       [
39698         "Lithuania (Lietuva)",
39699         "lt",
39700         "370"
39701       ],
39702       [
39703         "Luxembourg",
39704         "lu",
39705         "352"
39706       ],
39707       [
39708         "Macau (澳門)",
39709         "mo",
39710         "853"
39711       ],
39712       [
39713         "Macedonia (FYROM) (Македонија)",
39714         "mk",
39715         "389"
39716       ],
39717       [
39718         "Madagascar (Madagasikara)",
39719         "mg",
39720         "261"
39721       ],
39722       [
39723         "Malawi",
39724         "mw",
39725         "265"
39726       ],
39727       [
39728         "Malaysia",
39729         "my",
39730         "60"
39731       ],
39732       [
39733         "Maldives",
39734         "mv",
39735         "960"
39736       ],
39737       [
39738         "Mali",
39739         "ml",
39740         "223"
39741       ],
39742       [
39743         "Malta",
39744         "mt",
39745         "356"
39746       ],
39747       [
39748         "Marshall Islands",
39749         "mh",
39750         "692"
39751       ],
39752       [
39753         "Martinique",
39754         "mq",
39755         "596"
39756       ],
39757       [
39758         "Mauritania (‫موريتانيا‬‎)",
39759         "mr",
39760         "222"
39761       ],
39762       [
39763         "Mauritius (Moris)",
39764         "mu",
39765         "230"
39766       ],
39767       [
39768         "Mayotte",
39769         "yt",
39770         "262",
39771         1
39772       ],
39773       [
39774         "Mexico (México)",
39775         "mx",
39776         "52"
39777       ],
39778       [
39779         "Micronesia",
39780         "fm",
39781         "691"
39782       ],
39783       [
39784         "Moldova (Republica Moldova)",
39785         "md",
39786         "373"
39787       ],
39788       [
39789         "Monaco",
39790         "mc",
39791         "377"
39792       ],
39793       [
39794         "Mongolia (Монгол)",
39795         "mn",
39796         "976"
39797       ],
39798       [
39799         "Montenegro (Crna Gora)",
39800         "me",
39801         "382"
39802       ],
39803       [
39804         "Montserrat",
39805         "ms",
39806         "1664"
39807       ],
39808       [
39809         "Morocco (‫المغرب‬‎)",
39810         "ma",
39811         "212",
39812         0
39813       ],
39814       [
39815         "Mozambique (Moçambique)",
39816         "mz",
39817         "258"
39818       ],
39819       [
39820         "Myanmar (Burma) (မြန်မာ)",
39821         "mm",
39822         "95"
39823       ],
39824       [
39825         "Namibia (Namibië)",
39826         "na",
39827         "264"
39828       ],
39829       [
39830         "Nauru",
39831         "nr",
39832         "674"
39833       ],
39834       [
39835         "Nepal (नेपाल)",
39836         "np",
39837         "977"
39838       ],
39839       [
39840         "Netherlands (Nederland)",
39841         "nl",
39842         "31"
39843       ],
39844       [
39845         "New Caledonia (Nouvelle-Calédonie)",
39846         "nc",
39847         "687"
39848       ],
39849       [
39850         "New Zealand",
39851         "nz",
39852         "64"
39853       ],
39854       [
39855         "Nicaragua",
39856         "ni",
39857         "505"
39858       ],
39859       [
39860         "Niger (Nijar)",
39861         "ne",
39862         "227"
39863       ],
39864       [
39865         "Nigeria",
39866         "ng",
39867         "234"
39868       ],
39869       [
39870         "Niue",
39871         "nu",
39872         "683"
39873       ],
39874       [
39875         "Norfolk Island",
39876         "nf",
39877         "672"
39878       ],
39879       [
39880         "North Korea (조선 민주주의 인민 공화국)",
39881         "kp",
39882         "850"
39883       ],
39884       [
39885         "Northern Mariana Islands",
39886         "mp",
39887         "1670"
39888       ],
39889       [
39890         "Norway (Norge)",
39891         "no",
39892         "47",
39893         0
39894       ],
39895       [
39896         "Oman (‫عُمان‬‎)",
39897         "om",
39898         "968"
39899       ],
39900       [
39901         "Pakistan (‫پاکستان‬‎)",
39902         "pk",
39903         "92"
39904       ],
39905       [
39906         "Palau",
39907         "pw",
39908         "680"
39909       ],
39910       [
39911         "Palestine (‫فلسطين‬‎)",
39912         "ps",
39913         "970"
39914       ],
39915       [
39916         "Panama (Panamá)",
39917         "pa",
39918         "507"
39919       ],
39920       [
39921         "Papua New Guinea",
39922         "pg",
39923         "675"
39924       ],
39925       [
39926         "Paraguay",
39927         "py",
39928         "595"
39929       ],
39930       [
39931         "Peru (Perú)",
39932         "pe",
39933         "51"
39934       ],
39935       [
39936         "Philippines",
39937         "ph",
39938         "63"
39939       ],
39940       [
39941         "Poland (Polska)",
39942         "pl",
39943         "48"
39944       ],
39945       [
39946         "Portugal",
39947         "pt",
39948         "351"
39949       ],
39950       [
39951         "Puerto Rico",
39952         "pr",
39953         "1",
39954         3,
39955         ["787", "939"]
39956       ],
39957       [
39958         "Qatar (‫قطر‬‎)",
39959         "qa",
39960         "974"
39961       ],
39962       [
39963         "Réunion (La Réunion)",
39964         "re",
39965         "262",
39966         0
39967       ],
39968       [
39969         "Romania (România)",
39970         "ro",
39971         "40"
39972       ],
39973       [
39974         "Russia (Россия)",
39975         "ru",
39976         "7",
39977         0
39978       ],
39979       [
39980         "Rwanda",
39981         "rw",
39982         "250"
39983       ],
39984       [
39985         "Saint Barthélemy",
39986         "bl",
39987         "590",
39988         1
39989       ],
39990       [
39991         "Saint Helena",
39992         "sh",
39993         "290"
39994       ],
39995       [
39996         "Saint Kitts and Nevis",
39997         "kn",
39998         "1869"
39999       ],
40000       [
40001         "Saint Lucia",
40002         "lc",
40003         "1758"
40004       ],
40005       [
40006         "Saint Martin (Saint-Martin (partie française))",
40007         "mf",
40008         "590",
40009         2
40010       ],
40011       [
40012         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40013         "pm",
40014         "508"
40015       ],
40016       [
40017         "Saint Vincent and the Grenadines",
40018         "vc",
40019         "1784"
40020       ],
40021       [
40022         "Samoa",
40023         "ws",
40024         "685"
40025       ],
40026       [
40027         "San Marino",
40028         "sm",
40029         "378"
40030       ],
40031       [
40032         "São Tomé and Príncipe (São Tomé e Príncipe)",
40033         "st",
40034         "239"
40035       ],
40036       [
40037         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
40038         "sa",
40039         "966"
40040       ],
40041       [
40042         "Senegal (Sénégal)",
40043         "sn",
40044         "221"
40045       ],
40046       [
40047         "Serbia (Србија)",
40048         "rs",
40049         "381"
40050       ],
40051       [
40052         "Seychelles",
40053         "sc",
40054         "248"
40055       ],
40056       [
40057         "Sierra Leone",
40058         "sl",
40059         "232"
40060       ],
40061       [
40062         "Singapore",
40063         "sg",
40064         "65"
40065       ],
40066       [
40067         "Sint Maarten",
40068         "sx",
40069         "1721"
40070       ],
40071       [
40072         "Slovakia (Slovensko)",
40073         "sk",
40074         "421"
40075       ],
40076       [
40077         "Slovenia (Slovenija)",
40078         "si",
40079         "386"
40080       ],
40081       [
40082         "Solomon Islands",
40083         "sb",
40084         "677"
40085       ],
40086       [
40087         "Somalia (Soomaaliya)",
40088         "so",
40089         "252"
40090       ],
40091       [
40092         "South Africa",
40093         "za",
40094         "27"
40095       ],
40096       [
40097         "South Korea (대한민국)",
40098         "kr",
40099         "82"
40100       ],
40101       [
40102         "South Sudan (‫جنوب السودان‬‎)",
40103         "ss",
40104         "211"
40105       ],
40106       [
40107         "Spain (España)",
40108         "es",
40109         "34"
40110       ],
40111       [
40112         "Sri Lanka (ශ්‍රී ලංකාව)",
40113         "lk",
40114         "94"
40115       ],
40116       [
40117         "Sudan (‫السودان‬‎)",
40118         "sd",
40119         "249"
40120       ],
40121       [
40122         "Suriname",
40123         "sr",
40124         "597"
40125       ],
40126       [
40127         "Svalbard and Jan Mayen",
40128         "sj",
40129         "47",
40130         1
40131       ],
40132       [
40133         "Swaziland",
40134         "sz",
40135         "268"
40136       ],
40137       [
40138         "Sweden (Sverige)",
40139         "se",
40140         "46"
40141       ],
40142       [
40143         "Switzerland (Schweiz)",
40144         "ch",
40145         "41"
40146       ],
40147       [
40148         "Syria (‫سوريا‬‎)",
40149         "sy",
40150         "963"
40151       ],
40152       [
40153         "Taiwan (台灣)",
40154         "tw",
40155         "886"
40156       ],
40157       [
40158         "Tajikistan",
40159         "tj",
40160         "992"
40161       ],
40162       [
40163         "Tanzania",
40164         "tz",
40165         "255"
40166       ],
40167       [
40168         "Thailand (ไทย)",
40169         "th",
40170         "66"
40171       ],
40172       [
40173         "Timor-Leste",
40174         "tl",
40175         "670"
40176       ],
40177       [
40178         "Togo",
40179         "tg",
40180         "228"
40181       ],
40182       [
40183         "Tokelau",
40184         "tk",
40185         "690"
40186       ],
40187       [
40188         "Tonga",
40189         "to",
40190         "676"
40191       ],
40192       [
40193         "Trinidad and Tobago",
40194         "tt",
40195         "1868"
40196       ],
40197       [
40198         "Tunisia (‫تونس‬‎)",
40199         "tn",
40200         "216"
40201       ],
40202       [
40203         "Turkey (Türkiye)",
40204         "tr",
40205         "90"
40206       ],
40207       [
40208         "Turkmenistan",
40209         "tm",
40210         "993"
40211       ],
40212       [
40213         "Turks and Caicos Islands",
40214         "tc",
40215         "1649"
40216       ],
40217       [
40218         "Tuvalu",
40219         "tv",
40220         "688"
40221       ],
40222       [
40223         "U.S. Virgin Islands",
40224         "vi",
40225         "1340"
40226       ],
40227       [
40228         "Uganda",
40229         "ug",
40230         "256"
40231       ],
40232       [
40233         "Ukraine (Україна)",
40234         "ua",
40235         "380"
40236       ],
40237       [
40238         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40239         "ae",
40240         "971"
40241       ],
40242       [
40243         "United Kingdom",
40244         "gb",
40245         "44",
40246         0
40247       ],
40248       [
40249         "United States",
40250         "us",
40251         "1",
40252         0
40253       ],
40254       [
40255         "Uruguay",
40256         "uy",
40257         "598"
40258       ],
40259       [
40260         "Uzbekistan (Oʻzbekiston)",
40261         "uz",
40262         "998"
40263       ],
40264       [
40265         "Vanuatu",
40266         "vu",
40267         "678"
40268       ],
40269       [
40270         "Vatican City (Città del Vaticano)",
40271         "va",
40272         "39",
40273         1
40274       ],
40275       [
40276         "Venezuela",
40277         "ve",
40278         "58"
40279       ],
40280       [
40281         "Vietnam (Việt Nam)",
40282         "vn",
40283         "84"
40284       ],
40285       [
40286         "Wallis and Futuna (Wallis-et-Futuna)",
40287         "wf",
40288         "681"
40289       ],
40290       [
40291         "Western Sahara (‫الصحراء الغربية‬‎)",
40292         "eh",
40293         "212",
40294         1
40295       ],
40296       [
40297         "Yemen (‫اليمن‬‎)",
40298         "ye",
40299         "967"
40300       ],
40301       [
40302         "Zambia",
40303         "zm",
40304         "260"
40305       ],
40306       [
40307         "Zimbabwe",
40308         "zw",
40309         "263"
40310       ],
40311       [
40312         "Åland Islands",
40313         "ax",
40314         "358",
40315         1
40316       ]
40317   ];
40318   
40319   return d;
40320 }/**
40321 *    This script refer to:
40322 *    Title: International Telephone Input
40323 *    Author: Jack O'Connor
40324 *    Code version:  v12.1.12
40325 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40326 **/
40327
40328 /**
40329  * @class Roo.bootstrap.PhoneInput
40330  * @extends Roo.bootstrap.TriggerField
40331  * An input with International dial-code selection
40332  
40333  * @cfg {String} defaultDialCode default '+852'
40334  * @cfg {Array} preferedCountries default []
40335   
40336  * @constructor
40337  * Create a new PhoneInput.
40338  * @param {Object} config Configuration options
40339  */
40340
40341 Roo.bootstrap.PhoneInput = function(config) {
40342     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40343 };
40344
40345 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40346         
40347         listWidth: undefined,
40348         
40349         selectedClass: 'active',
40350         
40351         invalidClass : "has-warning",
40352         
40353         validClass: 'has-success',
40354         
40355         allowed: '0123456789',
40356         
40357         max_length: 15,
40358         
40359         /**
40360          * @cfg {String} defaultDialCode The default dial code when initializing the input
40361          */
40362         defaultDialCode: '+852',
40363         
40364         /**
40365          * @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
40366          */
40367         preferedCountries: false,
40368         
40369         getAutoCreate : function()
40370         {
40371             var data = Roo.bootstrap.PhoneInputData();
40372             var align = this.labelAlign || this.parentLabelAlign();
40373             var id = Roo.id();
40374             
40375             this.allCountries = [];
40376             this.dialCodeMapping = [];
40377             
40378             for (var i = 0; i < data.length; i++) {
40379               var c = data[i];
40380               this.allCountries[i] = {
40381                 name: c[0],
40382                 iso2: c[1],
40383                 dialCode: c[2],
40384                 priority: c[3] || 0,
40385                 areaCodes: c[4] || null
40386               };
40387               this.dialCodeMapping[c[2]] = {
40388                   name: c[0],
40389                   iso2: c[1],
40390                   priority: c[3] || 0,
40391                   areaCodes: c[4] || null
40392               };
40393             }
40394             
40395             var cfg = {
40396                 cls: 'form-group',
40397                 cn: []
40398             };
40399             
40400             var input =  {
40401                 tag: 'input',
40402                 id : id,
40403                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40404                 maxlength: this.max_length,
40405                 cls : 'form-control tel-input',
40406                 autocomplete: 'new-password'
40407             };
40408             
40409             var hiddenInput = {
40410                 tag: 'input',
40411                 type: 'hidden',
40412                 cls: 'hidden-tel-input'
40413             };
40414             
40415             if (this.name) {
40416                 hiddenInput.name = this.name;
40417             }
40418             
40419             if (this.disabled) {
40420                 input.disabled = true;
40421             }
40422             
40423             var flag_container = {
40424                 tag: 'div',
40425                 cls: 'flag-box',
40426                 cn: [
40427                     {
40428                         tag: 'div',
40429                         cls: 'flag'
40430                     },
40431                     {
40432                         tag: 'div',
40433                         cls: 'caret'
40434                     }
40435                 ]
40436             };
40437             
40438             var box = {
40439                 tag: 'div',
40440                 cls: this.hasFeedback ? 'has-feedback' : '',
40441                 cn: [
40442                     hiddenInput,
40443                     input,
40444                     {
40445                         tag: 'input',
40446                         cls: 'dial-code-holder',
40447                         disabled: true
40448                     }
40449                 ]
40450             };
40451             
40452             var container = {
40453                 cls: 'roo-select2-container input-group',
40454                 cn: [
40455                     flag_container,
40456                     box
40457                 ]
40458             };
40459             
40460             if (this.fieldLabel.length) {
40461                 var indicator = {
40462                     tag: 'i',
40463                     tooltip: 'This field is required'
40464                 };
40465                 
40466                 var label = {
40467                     tag: 'label',
40468                     'for':  id,
40469                     cls: 'control-label',
40470                     cn: []
40471                 };
40472                 
40473                 var label_text = {
40474                     tag: 'span',
40475                     html: this.fieldLabel
40476                 };
40477                 
40478                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40479                 label.cn = [
40480                     indicator,
40481                     label_text
40482                 ];
40483                 
40484                 if(this.indicatorpos == 'right') {
40485                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40486                     label.cn = [
40487                         label_text,
40488                         indicator
40489                     ];
40490                 }
40491                 
40492                 if(align == 'left') {
40493                     container = {
40494                         tag: 'div',
40495                         cn: [
40496                             container
40497                         ]
40498                     };
40499                     
40500                     if(this.labelWidth > 12){
40501                         label.style = "width: " + this.labelWidth + 'px';
40502                     }
40503                     if(this.labelWidth < 13 && this.labelmd == 0){
40504                         this.labelmd = this.labelWidth;
40505                     }
40506                     if(this.labellg > 0){
40507                         label.cls += ' col-lg-' + this.labellg;
40508                         input.cls += ' col-lg-' + (12 - this.labellg);
40509                     }
40510                     if(this.labelmd > 0){
40511                         label.cls += ' col-md-' + this.labelmd;
40512                         container.cls += ' col-md-' + (12 - this.labelmd);
40513                     }
40514                     if(this.labelsm > 0){
40515                         label.cls += ' col-sm-' + this.labelsm;
40516                         container.cls += ' col-sm-' + (12 - this.labelsm);
40517                     }
40518                     if(this.labelxs > 0){
40519                         label.cls += ' col-xs-' + this.labelxs;
40520                         container.cls += ' col-xs-' + (12 - this.labelxs);
40521                     }
40522                 }
40523             }
40524             
40525             cfg.cn = [
40526                 label,
40527                 container
40528             ];
40529             
40530             var settings = this;
40531             
40532             ['xs','sm','md','lg'].map(function(size){
40533                 if (settings[size]) {
40534                     cfg.cls += ' col-' + size + '-' + settings[size];
40535                 }
40536             });
40537             
40538             this.store = new Roo.data.Store({
40539                 proxy : new Roo.data.MemoryProxy({}),
40540                 reader : new Roo.data.JsonReader({
40541                     fields : [
40542                         {
40543                             'name' : 'name',
40544                             'type' : 'string'
40545                         },
40546                         {
40547                             'name' : 'iso2',
40548                             'type' : 'string'
40549                         },
40550                         {
40551                             'name' : 'dialCode',
40552                             'type' : 'string'
40553                         },
40554                         {
40555                             'name' : 'priority',
40556                             'type' : 'string'
40557                         },
40558                         {
40559                             'name' : 'areaCodes',
40560                             'type' : 'string'
40561                         }
40562                     ]
40563                 })
40564             });
40565             
40566             if(!this.preferedCountries) {
40567                 this.preferedCountries = [
40568                     'hk',
40569                     'gb',
40570                     'us'
40571                 ];
40572             }
40573             
40574             var p = this.preferedCountries.reverse();
40575             
40576             if(p) {
40577                 for (var i = 0; i < p.length; i++) {
40578                     for (var j = 0; j < this.allCountries.length; j++) {
40579                         if(this.allCountries[j].iso2 == p[i]) {
40580                             var t = this.allCountries[j];
40581                             this.allCountries.splice(j,1);
40582                             this.allCountries.unshift(t);
40583                         }
40584                     } 
40585                 }
40586             }
40587             
40588             this.store.proxy.data = {
40589                 success: true,
40590                 data: this.allCountries
40591             };
40592             
40593             return cfg;
40594         },
40595         
40596         initEvents : function()
40597         {
40598             this.createList();
40599             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40600             
40601             this.indicator = this.indicatorEl();
40602             this.flag = this.flagEl();
40603             this.dialCodeHolder = this.dialCodeHolderEl();
40604             
40605             this.trigger = this.el.select('div.flag-box',true).first();
40606             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40607             
40608             var _this = this;
40609             
40610             (function(){
40611                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40612                 _this.list.setWidth(lw);
40613             }).defer(100);
40614             
40615             this.list.on('mouseover', this.onViewOver, this);
40616             this.list.on('mousemove', this.onViewMove, this);
40617             this.inputEl().on("keyup", this.onKeyUp, this);
40618             this.inputEl().on("keypress", this.onKeyPress, this);
40619             
40620             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40621
40622             this.view = new Roo.View(this.list, this.tpl, {
40623                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40624             });
40625             
40626             this.view.on('click', this.onViewClick, this);
40627             this.setValue(this.defaultDialCode);
40628         },
40629         
40630         onTriggerClick : function(e)
40631         {
40632             Roo.log('trigger click');
40633             if(this.disabled){
40634                 return;
40635             }
40636             
40637             if(this.isExpanded()){
40638                 this.collapse();
40639                 this.hasFocus = false;
40640             }else {
40641                 this.store.load({});
40642                 this.hasFocus = true;
40643                 this.expand();
40644             }
40645         },
40646         
40647         isExpanded : function()
40648         {
40649             return this.list.isVisible();
40650         },
40651         
40652         collapse : function()
40653         {
40654             if(!this.isExpanded()){
40655                 return;
40656             }
40657             this.list.hide();
40658             Roo.get(document).un('mousedown', this.collapseIf, this);
40659             Roo.get(document).un('mousewheel', this.collapseIf, this);
40660             this.fireEvent('collapse', this);
40661             this.validate();
40662         },
40663         
40664         expand : function()
40665         {
40666             Roo.log('expand');
40667
40668             if(this.isExpanded() || !this.hasFocus){
40669                 return;
40670             }
40671             
40672             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40673             this.list.setWidth(lw);
40674             
40675             this.list.show();
40676             this.restrictHeight();
40677             
40678             Roo.get(document).on('mousedown', this.collapseIf, this);
40679             Roo.get(document).on('mousewheel', this.collapseIf, this);
40680             
40681             this.fireEvent('expand', this);
40682         },
40683         
40684         restrictHeight : function()
40685         {
40686             this.list.alignTo(this.inputEl(), this.listAlign);
40687             this.list.alignTo(this.inputEl(), this.listAlign);
40688         },
40689         
40690         onViewOver : function(e, t)
40691         {
40692             if(this.inKeyMode){
40693                 return;
40694             }
40695             var item = this.view.findItemFromChild(t);
40696             
40697             if(item){
40698                 var index = this.view.indexOf(item);
40699                 this.select(index, false);
40700             }
40701         },
40702
40703         // private
40704         onViewClick : function(view, doFocus, el, e)
40705         {
40706             var index = this.view.getSelectedIndexes()[0];
40707             
40708             var r = this.store.getAt(index);
40709             
40710             if(r){
40711                 this.onSelect(r, index);
40712             }
40713             if(doFocus !== false && !this.blockFocus){
40714                 this.inputEl().focus();
40715             }
40716         },
40717         
40718         onViewMove : function(e, t)
40719         {
40720             this.inKeyMode = false;
40721         },
40722         
40723         select : function(index, scrollIntoView)
40724         {
40725             this.selectedIndex = index;
40726             this.view.select(index);
40727             if(scrollIntoView !== false){
40728                 var el = this.view.getNode(index);
40729                 if(el){
40730                     this.list.scrollChildIntoView(el, false);
40731                 }
40732             }
40733         },
40734         
40735         createList : function()
40736         {
40737             this.list = Roo.get(document.body).createChild({
40738                 tag: 'ul',
40739                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40740                 style: 'display:none'
40741             });
40742             
40743             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40744         },
40745         
40746         collapseIf : function(e)
40747         {
40748             var in_combo  = e.within(this.el);
40749             var in_list =  e.within(this.list);
40750             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40751             
40752             if (in_combo || in_list || is_list) {
40753                 return;
40754             }
40755             this.collapse();
40756         },
40757         
40758         onSelect : function(record, index)
40759         {
40760             if(this.fireEvent('beforeselect', this, record, index) !== false){
40761                 
40762                 this.setFlagClass(record.data.iso2);
40763                 this.setDialCode(record.data.dialCode);
40764                 this.hasFocus = false;
40765                 this.collapse();
40766                 this.fireEvent('select', this, record, index);
40767             }
40768         },
40769         
40770         flagEl : function()
40771         {
40772             var flag = this.el.select('div.flag',true).first();
40773             if(!flag){
40774                 return false;
40775             }
40776             return flag;
40777         },
40778         
40779         dialCodeHolderEl : function()
40780         {
40781             var d = this.el.select('input.dial-code-holder',true).first();
40782             if(!d){
40783                 return false;
40784             }
40785             return d;
40786         },
40787         
40788         setDialCode : function(v)
40789         {
40790             this.dialCodeHolder.dom.value = '+'+v;
40791         },
40792         
40793         setFlagClass : function(n)
40794         {
40795             this.flag.dom.className = 'flag '+n;
40796         },
40797         
40798         getValue : function()
40799         {
40800             var v = this.inputEl().getValue();
40801             if(this.dialCodeHolder) {
40802                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40803             }
40804             return v;
40805         },
40806         
40807         setValue : function(v)
40808         {
40809             var d = this.getDialCode(v);
40810             
40811             //invalid dial code
40812             if(v.length == 0 || !d || d.length == 0) {
40813                 if(this.rendered){
40814                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40815                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40816                 }
40817                 return;
40818             }
40819             
40820             //valid dial code
40821             this.setFlagClass(this.dialCodeMapping[d].iso2);
40822             this.setDialCode(d);
40823             this.inputEl().dom.value = v.replace('+'+d,'');
40824             this.hiddenEl().dom.value = this.getValue();
40825             
40826             this.validate();
40827         },
40828         
40829         getDialCode : function(v)
40830         {
40831             v = v ||  '';
40832             
40833             if (v.length == 0) {
40834                 return this.dialCodeHolder.dom.value;
40835             }
40836             
40837             var dialCode = "";
40838             if (v.charAt(0) != "+") {
40839                 return false;
40840             }
40841             var numericChars = "";
40842             for (var i = 1; i < v.length; i++) {
40843               var c = v.charAt(i);
40844               if (!isNaN(c)) {
40845                 numericChars += c;
40846                 if (this.dialCodeMapping[numericChars]) {
40847                   dialCode = v.substr(1, i);
40848                 }
40849                 if (numericChars.length == 4) {
40850                   break;
40851                 }
40852               }
40853             }
40854             return dialCode;
40855         },
40856         
40857         reset : function()
40858         {
40859             this.setValue(this.defaultDialCode);
40860             this.validate();
40861         },
40862         
40863         hiddenEl : function()
40864         {
40865             return this.el.select('input.hidden-tel-input',true).first();
40866         },
40867         
40868         // after setting val
40869         onKeyUp : function(e){
40870             this.setValue(this.getValue());
40871         },
40872         
40873         onKeyPress : function(e){
40874             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40875                 e.stopEvent();
40876             }
40877         }
40878         
40879 });
40880 /**
40881  * @class Roo.bootstrap.MoneyField
40882  * @extends Roo.bootstrap.ComboBox
40883  * Bootstrap MoneyField class
40884  * 
40885  * @constructor
40886  * Create a new MoneyField.
40887  * @param {Object} config Configuration options
40888  */
40889
40890 Roo.bootstrap.MoneyField = function(config) {
40891     
40892     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40893     
40894 };
40895
40896 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40897     
40898     /**
40899      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40900      */
40901     allowDecimals : true,
40902     /**
40903      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40904      */
40905     decimalSeparator : ".",
40906     /**
40907      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40908      */
40909     decimalPrecision : 0,
40910     /**
40911      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40912      */
40913     allowNegative : true,
40914     /**
40915      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40916      */
40917     allowZero: true,
40918     /**
40919      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40920      */
40921     minValue : Number.NEGATIVE_INFINITY,
40922     /**
40923      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40924      */
40925     maxValue : Number.MAX_VALUE,
40926     /**
40927      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40928      */
40929     minText : "The minimum value for this field is {0}",
40930     /**
40931      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40932      */
40933     maxText : "The maximum value for this field is {0}",
40934     /**
40935      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40936      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40937      */
40938     nanText : "{0} is not a valid number",
40939     /**
40940      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40941      */
40942     castInt : true,
40943     /**
40944      * @cfg {String} defaults currency of the MoneyField
40945      * value should be in lkey
40946      */
40947     defaultCurrency : false,
40948     /**
40949      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40950      */
40951     thousandsDelimiter : false,
40952     /**
40953      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40954      */
40955     max_length: false,
40956     
40957     inputlg : 9,
40958     inputmd : 9,
40959     inputsm : 9,
40960     inputxs : 6,
40961     
40962     store : false,
40963     
40964     getAutoCreate : function()
40965     {
40966         var align = this.labelAlign || this.parentLabelAlign();
40967         
40968         var id = Roo.id();
40969
40970         var cfg = {
40971             cls: 'form-group',
40972             cn: []
40973         };
40974
40975         var input =  {
40976             tag: 'input',
40977             id : id,
40978             cls : 'form-control roo-money-amount-input',
40979             autocomplete: 'new-password'
40980         };
40981         
40982         var hiddenInput = {
40983             tag: 'input',
40984             type: 'hidden',
40985             id: Roo.id(),
40986             cls: 'hidden-number-input'
40987         };
40988         
40989         if(this.max_length) {
40990             input.maxlength = this.max_length; 
40991         }
40992         
40993         if (this.name) {
40994             hiddenInput.name = this.name;
40995         }
40996
40997         if (this.disabled) {
40998             input.disabled = true;
40999         }
41000
41001         var clg = 12 - this.inputlg;
41002         var cmd = 12 - this.inputmd;
41003         var csm = 12 - this.inputsm;
41004         var cxs = 12 - this.inputxs;
41005         
41006         var container = {
41007             tag : 'div',
41008             cls : 'row roo-money-field',
41009             cn : [
41010                 {
41011                     tag : 'div',
41012                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41013                     cn : [
41014                         {
41015                             tag : 'div',
41016                             cls: 'roo-select2-container input-group',
41017                             cn: [
41018                                 {
41019                                     tag : 'input',
41020                                     cls : 'form-control roo-money-currency-input',
41021                                     autocomplete: 'new-password',
41022                                     readOnly : 1,
41023                                     name : this.currencyName
41024                                 },
41025                                 {
41026                                     tag :'span',
41027                                     cls : 'input-group-addon',
41028                                     cn : [
41029                                         {
41030                                             tag: 'span',
41031                                             cls: 'caret'
41032                                         }
41033                                     ]
41034                                 }
41035                             ]
41036                         }
41037                     ]
41038                 },
41039                 {
41040                     tag : 'div',
41041                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41042                     cn : [
41043                         {
41044                             tag: 'div',
41045                             cls: this.hasFeedback ? 'has-feedback' : '',
41046                             cn: [
41047                                 input
41048                             ]
41049                         }
41050                     ]
41051                 }
41052             ]
41053             
41054         };
41055         
41056         if (this.fieldLabel.length) {
41057             var indicator = {
41058                 tag: 'i',
41059                 tooltip: 'This field is required'
41060             };
41061
41062             var label = {
41063                 tag: 'label',
41064                 'for':  id,
41065                 cls: 'control-label',
41066                 cn: []
41067             };
41068
41069             var label_text = {
41070                 tag: 'span',
41071                 html: this.fieldLabel
41072             };
41073
41074             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41075             label.cn = [
41076                 indicator,
41077                 label_text
41078             ];
41079
41080             if(this.indicatorpos == 'right') {
41081                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41082                 label.cn = [
41083                     label_text,
41084                     indicator
41085                 ];
41086             }
41087
41088             if(align == 'left') {
41089                 container = {
41090                     tag: 'div',
41091                     cn: [
41092                         container
41093                     ]
41094                 };
41095
41096                 if(this.labelWidth > 12){
41097                     label.style = "width: " + this.labelWidth + 'px';
41098                 }
41099                 if(this.labelWidth < 13 && this.labelmd == 0){
41100                     this.labelmd = this.labelWidth;
41101                 }
41102                 if(this.labellg > 0){
41103                     label.cls += ' col-lg-' + this.labellg;
41104                     input.cls += ' col-lg-' + (12 - this.labellg);
41105                 }
41106                 if(this.labelmd > 0){
41107                     label.cls += ' col-md-' + this.labelmd;
41108                     container.cls += ' col-md-' + (12 - this.labelmd);
41109                 }
41110                 if(this.labelsm > 0){
41111                     label.cls += ' col-sm-' + this.labelsm;
41112                     container.cls += ' col-sm-' + (12 - this.labelsm);
41113                 }
41114                 if(this.labelxs > 0){
41115                     label.cls += ' col-xs-' + this.labelxs;
41116                     container.cls += ' col-xs-' + (12 - this.labelxs);
41117                 }
41118             }
41119         }
41120
41121         cfg.cn = [
41122             label,
41123             container,
41124             hiddenInput
41125         ];
41126         
41127         var settings = this;
41128
41129         ['xs','sm','md','lg'].map(function(size){
41130             if (settings[size]) {
41131                 cfg.cls += ' col-' + size + '-' + settings[size];
41132             }
41133         });
41134         
41135         return cfg;
41136     },
41137     
41138     initEvents : function()
41139     {
41140         this.indicator = this.indicatorEl();
41141         
41142         this.initCurrencyEvent();
41143         
41144         this.initNumberEvent();
41145     },
41146     
41147     initCurrencyEvent : function()
41148     {
41149         if (!this.store) {
41150             throw "can not find store for combo";
41151         }
41152         
41153         this.store = Roo.factory(this.store, Roo.data);
41154         this.store.parent = this;
41155         
41156         this.createList();
41157         
41158         this.triggerEl = this.el.select('.input-group-addon', true).first();
41159         
41160         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41161         
41162         var _this = this;
41163         
41164         (function(){
41165             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41166             _this.list.setWidth(lw);
41167         }).defer(100);
41168         
41169         this.list.on('mouseover', this.onViewOver, this);
41170         this.list.on('mousemove', this.onViewMove, this);
41171         this.list.on('scroll', this.onViewScroll, this);
41172         
41173         if(!this.tpl){
41174             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41175         }
41176         
41177         this.view = new Roo.View(this.list, this.tpl, {
41178             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41179         });
41180         
41181         this.view.on('click', this.onViewClick, this);
41182         
41183         this.store.on('beforeload', this.onBeforeLoad, this);
41184         this.store.on('load', this.onLoad, this);
41185         this.store.on('loadexception', this.onLoadException, this);
41186         
41187         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41188             "up" : function(e){
41189                 this.inKeyMode = true;
41190                 this.selectPrev();
41191             },
41192
41193             "down" : function(e){
41194                 if(!this.isExpanded()){
41195                     this.onTriggerClick();
41196                 }else{
41197                     this.inKeyMode = true;
41198                     this.selectNext();
41199                 }
41200             },
41201
41202             "enter" : function(e){
41203                 this.collapse();
41204                 
41205                 if(this.fireEvent("specialkey", this, e)){
41206                     this.onViewClick(false);
41207                 }
41208                 
41209                 return true;
41210             },
41211
41212             "esc" : function(e){
41213                 this.collapse();
41214             },
41215
41216             "tab" : function(e){
41217                 this.collapse();
41218                 
41219                 if(this.fireEvent("specialkey", this, e)){
41220                     this.onViewClick(false);
41221                 }
41222                 
41223                 return true;
41224             },
41225
41226             scope : this,
41227
41228             doRelay : function(foo, bar, hname){
41229                 if(hname == 'down' || this.scope.isExpanded()){
41230                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41231                 }
41232                 return true;
41233             },
41234
41235             forceKeyDown: true
41236         });
41237         
41238         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41239         
41240     },
41241     
41242     initNumberEvent : function(e)
41243     {
41244         this.inputEl().on("keydown" , this.fireKey,  this);
41245         this.inputEl().on("focus", this.onFocus,  this);
41246         this.inputEl().on("blur", this.onBlur,  this);
41247         
41248         this.inputEl().relayEvent('keyup', this);
41249         
41250         if(this.indicator){
41251             this.indicator.addClass('invisible');
41252         }
41253  
41254         this.originalValue = this.getValue();
41255         
41256         if(this.validationEvent == 'keyup'){
41257             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41258             this.inputEl().on('keyup', this.filterValidation, this);
41259         }
41260         else if(this.validationEvent !== false){
41261             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41262         }
41263         
41264         if(this.selectOnFocus){
41265             this.on("focus", this.preFocus, this);
41266             
41267         }
41268         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41269             this.inputEl().on("keypress", this.filterKeys, this);
41270         } else {
41271             this.inputEl().relayEvent('keypress', this);
41272         }
41273         
41274         var allowed = "0123456789";
41275         
41276         if(this.allowDecimals){
41277             allowed += this.decimalSeparator;
41278         }
41279         
41280         if(this.allowNegative){
41281             allowed += "-";
41282         }
41283         
41284         if(this.thousandsDelimiter) {
41285             allowed += ",";
41286         }
41287         
41288         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41289         
41290         var keyPress = function(e){
41291             
41292             var k = e.getKey();
41293             
41294             var c = e.getCharCode();
41295             
41296             if(
41297                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41298                     allowed.indexOf(String.fromCharCode(c)) === -1
41299             ){
41300                 e.stopEvent();
41301                 return;
41302             }
41303             
41304             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41305                 return;
41306             }
41307             
41308             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41309                 e.stopEvent();
41310             }
41311         };
41312         
41313         this.inputEl().on("keypress", keyPress, this);
41314         
41315     },
41316     
41317     onTriggerClick : function(e)
41318     {   
41319         if(this.disabled){
41320             return;
41321         }
41322         
41323         this.page = 0;
41324         this.loadNext = false;
41325         
41326         if(this.isExpanded()){
41327             this.collapse();
41328             return;
41329         }
41330         
41331         this.hasFocus = true;
41332         
41333         if(this.triggerAction == 'all') {
41334             this.doQuery(this.allQuery, true);
41335             return;
41336         }
41337         
41338         this.doQuery(this.getRawValue());
41339     },
41340     
41341     getCurrency : function()
41342     {   
41343         var v = this.currencyEl().getValue();
41344         
41345         return v;
41346     },
41347     
41348     restrictHeight : function()
41349     {
41350         this.list.alignTo(this.currencyEl(), this.listAlign);
41351         this.list.alignTo(this.currencyEl(), this.listAlign);
41352     },
41353     
41354     onViewClick : function(view, doFocus, el, e)
41355     {
41356         var index = this.view.getSelectedIndexes()[0];
41357         
41358         var r = this.store.getAt(index);
41359         
41360         if(r){
41361             this.onSelect(r, index);
41362         }
41363     },
41364     
41365     onSelect : function(record, index){
41366         
41367         if(this.fireEvent('beforeselect', this, record, index) !== false){
41368         
41369             this.setFromCurrencyData(index > -1 ? record.data : false);
41370             
41371             this.collapse();
41372             
41373             this.fireEvent('select', this, record, index);
41374         }
41375     },
41376     
41377     setFromCurrencyData : function(o)
41378     {
41379         var currency = '';
41380         
41381         this.lastCurrency = o;
41382         
41383         if (this.currencyField) {
41384             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41385         } else {
41386             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41387         }
41388         
41389         this.lastSelectionText = currency;
41390         
41391         //setting default currency
41392         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41393             this.setCurrency(this.defaultCurrency);
41394             return;
41395         }
41396         
41397         this.setCurrency(currency);
41398     },
41399     
41400     setFromData : function(o)
41401     {
41402         var c = {};
41403         
41404         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41405         
41406         this.setFromCurrencyData(c);
41407         
41408         var value = '';
41409         
41410         if (this.name) {
41411             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41412         } else {
41413             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41414         }
41415         
41416         this.setValue(value);
41417         
41418     },
41419     
41420     setCurrency : function(v)
41421     {   
41422         this.currencyValue = v;
41423         
41424         if(this.rendered){
41425             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41426             this.validate();
41427         }
41428     },
41429     
41430     setValue : function(v)
41431     {
41432         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41433         
41434         this.value = v;
41435         
41436         if(this.rendered){
41437             
41438             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41439             
41440             this.inputEl().dom.value = (v == '') ? '' :
41441                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41442             
41443             if(!this.allowZero && v === '0') {
41444                 this.hiddenEl().dom.value = '';
41445                 this.inputEl().dom.value = '';
41446             }
41447             
41448             this.validate();
41449         }
41450     },
41451     
41452     getRawValue : function()
41453     {
41454         var v = this.inputEl().getValue();
41455         
41456         return v;
41457     },
41458     
41459     getValue : function()
41460     {
41461         return this.fixPrecision(this.parseValue(this.getRawValue()));
41462     },
41463     
41464     parseValue : function(value)
41465     {
41466         if(this.thousandsDelimiter) {
41467             value += "";
41468             r = new RegExp(",", "g");
41469             value = value.replace(r, "");
41470         }
41471         
41472         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41473         return isNaN(value) ? '' : value;
41474         
41475     },
41476     
41477     fixPrecision : function(value)
41478     {
41479         if(this.thousandsDelimiter) {
41480             value += "";
41481             r = new RegExp(",", "g");
41482             value = value.replace(r, "");
41483         }
41484         
41485         var nan = isNaN(value);
41486         
41487         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41488             return nan ? '' : value;
41489         }
41490         return parseFloat(value).toFixed(this.decimalPrecision);
41491     },
41492     
41493     decimalPrecisionFcn : function(v)
41494     {
41495         return Math.floor(v);
41496     },
41497     
41498     validateValue : function(value)
41499     {
41500         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41501             return false;
41502         }
41503         
41504         var num = this.parseValue(value);
41505         
41506         if(isNaN(num)){
41507             this.markInvalid(String.format(this.nanText, value));
41508             return false;
41509         }
41510         
41511         if(num < this.minValue){
41512             this.markInvalid(String.format(this.minText, this.minValue));
41513             return false;
41514         }
41515         
41516         if(num > this.maxValue){
41517             this.markInvalid(String.format(this.maxText, this.maxValue));
41518             return false;
41519         }
41520         
41521         return true;
41522     },
41523     
41524     validate : function()
41525     {
41526         if(this.disabled || this.allowBlank){
41527             this.markValid();
41528             return true;
41529         }
41530         
41531         var currency = this.getCurrency();
41532         
41533         if(this.validateValue(this.getRawValue()) && currency.length){
41534             this.markValid();
41535             return true;
41536         }
41537         
41538         this.markInvalid();
41539         return false;
41540     },
41541     
41542     getName: function()
41543     {
41544         return this.name;
41545     },
41546     
41547     beforeBlur : function()
41548     {
41549         if(!this.castInt){
41550             return;
41551         }
41552         
41553         var v = this.parseValue(this.getRawValue());
41554         
41555         if(v || v == 0){
41556             this.setValue(v);
41557         }
41558     },
41559     
41560     onBlur : function()
41561     {
41562         this.beforeBlur();
41563         
41564         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41565             //this.el.removeClass(this.focusClass);
41566         }
41567         
41568         this.hasFocus = false;
41569         
41570         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41571             this.validate();
41572         }
41573         
41574         var v = this.getValue();
41575         
41576         if(String(v) !== String(this.startValue)){
41577             this.fireEvent('change', this, v, this.startValue);
41578         }
41579         
41580         this.fireEvent("blur", this);
41581     },
41582     
41583     inputEl : function()
41584     {
41585         return this.el.select('.roo-money-amount-input', true).first();
41586     },
41587     
41588     currencyEl : function()
41589     {
41590         return this.el.select('.roo-money-currency-input', true).first();
41591     },
41592     
41593     hiddenEl : function()
41594     {
41595         return this.el.select('input.hidden-number-input',true).first();
41596     }
41597     
41598 });/**
41599  * @class Roo.bootstrap.BezierSignature
41600  * @extends Roo.bootstrap.Component
41601  * Bootstrap BezierSignature class
41602  * This script refer to:
41603  *    Title: Signature Pad
41604  *    Author: szimek
41605  *    Availability: https://github.com/szimek/signature_pad
41606  *
41607  * @constructor
41608  * Create a new BezierSignature
41609  * @param {Object} config The config object
41610  */
41611
41612 Roo.bootstrap.BezierSignature = function(config){
41613     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41614     this.addEvents({
41615         "resize" : true
41616     });
41617 };
41618
41619 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41620 {
41621      
41622     curve_data: [],
41623     
41624     is_empty: true,
41625     
41626     mouse_btn_down: true,
41627     
41628     /**
41629      * @cfg {int} canvas height
41630      */
41631     canvas_height: '200px',
41632     
41633     /**
41634      * @cfg {float|function} Radius of a single dot.
41635      */ 
41636     dot_size: false,
41637     
41638     /**
41639      * @cfg {float} Minimum width of a line. Defaults to 0.5.
41640      */
41641     min_width: 0.5,
41642     
41643     /**
41644      * @cfg {float} Maximum width of a line. Defaults to 2.5.
41645      */
41646     max_width: 2.5,
41647     
41648     /**
41649      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41650      */
41651     throttle: 16,
41652     
41653     /**
41654      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41655      */
41656     min_distance: 5,
41657     
41658     /**
41659      * @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.
41660      */
41661     bg_color: 'rgba(0, 0, 0, 0)',
41662     
41663     /**
41664      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41665      */
41666     dot_color: 'black',
41667     
41668     /**
41669      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41670      */ 
41671     velocity_filter_weight: 0.7,
41672     
41673     /**
41674      * @cfg {function} Callback when stroke begin. 
41675      */
41676     onBegin: false,
41677     
41678     /**
41679      * @cfg {function} Callback when stroke end.
41680      */
41681     onEnd: false,
41682     
41683     getAutoCreate : function()
41684     {
41685         var cls = 'roo-signature column';
41686         
41687         if(this.cls){
41688             cls += ' ' + this.cls;
41689         }
41690         
41691         var col_sizes = [
41692             'lg',
41693             'md',
41694             'sm',
41695             'xs'
41696         ];
41697         
41698         for(var i = 0; i < col_sizes.length; i++) {
41699             if(this[col_sizes[i]]) {
41700                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41701             }
41702         }
41703         
41704         var cfg = {
41705             tag: 'div',
41706             cls: cls,
41707             cn: [
41708                 {
41709                     tag: 'div',
41710                     cls: 'roo-signature-body',
41711                     cn: [
41712                         {
41713                             tag: 'canvas',
41714                             cls: 'roo-signature-body-canvas',
41715                             height: this.canvas_height,
41716                             width: this.canvas_width
41717                         }
41718                     ]
41719                 },
41720                 {
41721                     tag: 'input',
41722                     type: 'file',
41723                     style: 'display: none'
41724                 }
41725             ]
41726         };
41727         
41728         return cfg;
41729     },
41730     
41731     initEvents: function() 
41732     {
41733         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41734         
41735         var canvas = this.canvasEl();
41736         
41737         // mouse && touch event swapping...
41738         canvas.dom.style.touchAction = 'none';
41739         canvas.dom.style.msTouchAction = 'none';
41740         
41741         this.mouse_btn_down = false;
41742         canvas.on('mousedown', this._handleMouseDown, this);
41743         canvas.on('mousemove', this._handleMouseMove, this);
41744         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41745         
41746         if (window.PointerEvent) {
41747             canvas.on('pointerdown', this._handleMouseDown, this);
41748             canvas.on('pointermove', this._handleMouseMove, this);
41749             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41750         }
41751         
41752         if ('ontouchstart' in window) {
41753             canvas.on('touchstart', this._handleTouchStart, this);
41754             canvas.on('touchmove', this._handleTouchMove, this);
41755             canvas.on('touchend', this._handleTouchEnd, this);
41756         }
41757         
41758         Roo.EventManager.onWindowResize(this.resize, this, true);
41759         
41760         // file input event
41761         this.fileEl().on('change', this.uploadImage, this);
41762         
41763         this.clear();
41764         
41765         this.resize();
41766     },
41767     
41768     resize: function(){
41769         
41770         var canvas = this.canvasEl().dom;
41771         var ctx = this.canvasElCtx();
41772         var img_data = false;
41773         
41774         if(canvas.width > 0) {
41775             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41776         }
41777         // setting canvas width will clean img data
41778         canvas.width = 0;
41779         
41780         var style = window.getComputedStyle ? 
41781             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41782             
41783         var padding_left = parseInt(style.paddingLeft) || 0;
41784         var padding_right = parseInt(style.paddingRight) || 0;
41785         
41786         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41787         
41788         if(img_data) {
41789             ctx.putImageData(img_data, 0, 0);
41790         }
41791     },
41792     
41793     _handleMouseDown: function(e)
41794     {
41795         if (e.browserEvent.which === 1) {
41796             this.mouse_btn_down = true;
41797             this.strokeBegin(e);
41798         }
41799     },
41800     
41801     _handleMouseMove: function (e)
41802     {
41803         if (this.mouse_btn_down) {
41804             this.strokeMoveUpdate(e);
41805         }
41806     },
41807     
41808     _handleMouseUp: function (e)
41809     {
41810         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41811             this.mouse_btn_down = false;
41812             this.strokeEnd(e);
41813         }
41814     },
41815     
41816     _handleTouchStart: function (e) {
41817         
41818         e.preventDefault();
41819         if (e.browserEvent.targetTouches.length === 1) {
41820             // var touch = e.browserEvent.changedTouches[0];
41821             // this.strokeBegin(touch);
41822             
41823              this.strokeBegin(e); // assume e catching the correct xy...
41824         }
41825     },
41826     
41827     _handleTouchMove: function (e) {
41828         e.preventDefault();
41829         // var touch = event.targetTouches[0];
41830         // _this._strokeMoveUpdate(touch);
41831         this.strokeMoveUpdate(e);
41832     },
41833     
41834     _handleTouchEnd: function (e) {
41835         var wasCanvasTouched = e.target === this.canvasEl().dom;
41836         if (wasCanvasTouched) {
41837             e.preventDefault();
41838             // var touch = event.changedTouches[0];
41839             // _this._strokeEnd(touch);
41840             this.strokeEnd(e);
41841         }
41842     },
41843     
41844     reset: function () {
41845         this._lastPoints = [];
41846         this._lastVelocity = 0;
41847         this._lastWidth = (this.min_width + this.max_width) / 2;
41848         this.canvasElCtx().fillStyle = this.dot_color;
41849     },
41850     
41851     strokeMoveUpdate: function(e)
41852     {
41853         this.strokeUpdate(e);
41854         
41855         if (this.throttle) {
41856             this.throttleStroke(this.strokeUpdate, this.throttle);
41857         }
41858         else {
41859             this.strokeUpdate(e);
41860         }
41861     },
41862     
41863     strokeBegin: function(e)
41864     {
41865         var newPointGroup = {
41866             color: this.dot_color,
41867             points: []
41868         };
41869         
41870         if (typeof this.onBegin === 'function') {
41871             this.onBegin(e);
41872         }
41873         
41874         this.curve_data.push(newPointGroup);
41875         this.reset();
41876         this.strokeUpdate(e);
41877     },
41878     
41879     strokeUpdate: function(e)
41880     {
41881         var rect = this.canvasEl().dom.getBoundingClientRect();
41882         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41883         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41884         var lastPoints = lastPointGroup.points;
41885         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41886         var isLastPointTooClose = lastPoint
41887             ? point.distanceTo(lastPoint) <= this.min_distance
41888             : false;
41889         var color = lastPointGroup.color;
41890         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41891             var curve = this.addPoint(point);
41892             if (!lastPoint) {
41893                 this.drawDot({color: color, point: point});
41894             }
41895             else if (curve) {
41896                 this.drawCurve({color: color, curve: curve});
41897             }
41898             lastPoints.push({
41899                 time: point.time,
41900                 x: point.x,
41901                 y: point.y
41902             });
41903         }
41904     },
41905     
41906     strokeEnd: function(e)
41907     {
41908         this.strokeUpdate(e);
41909         if (typeof this.onEnd === 'function') {
41910             this.onEnd(e);
41911         }
41912     },
41913     
41914     addPoint:  function (point) {
41915         var _lastPoints = this._lastPoints;
41916         _lastPoints.push(point);
41917         if (_lastPoints.length > 2) {
41918             if (_lastPoints.length === 3) {
41919                 _lastPoints.unshift(_lastPoints[0]);
41920             }
41921             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41922             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41923             _lastPoints.shift();
41924             return curve;
41925         }
41926         return null;
41927     },
41928     
41929     calculateCurveWidths: function (startPoint, endPoint) {
41930         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
41931             (1 - this.velocity_filter_weight) * this._lastVelocity;
41932
41933         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
41934         var widths = {
41935             end: newWidth,
41936             start: this._lastWidth
41937         };
41938         
41939         this._lastVelocity = velocity;
41940         this._lastWidth = newWidth;
41941         return widths;
41942     },
41943     
41944     drawDot: function (_a) {
41945         var color = _a.color, point = _a.point;
41946         var ctx = this.canvasElCtx();
41947         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
41948         ctx.beginPath();
41949         this.drawCurveSegment(point.x, point.y, width);
41950         ctx.closePath();
41951         ctx.fillStyle = color;
41952         ctx.fill();
41953     },
41954     
41955     drawCurve: function (_a) {
41956         var color = _a.color, curve = _a.curve;
41957         var ctx = this.canvasElCtx();
41958         var widthDelta = curve.endWidth - curve.startWidth;
41959         var drawSteps = Math.floor(curve.length()) * 2;
41960         ctx.beginPath();
41961         ctx.fillStyle = color;
41962         for (var i = 0; i < drawSteps; i += 1) {
41963         var t = i / drawSteps;
41964         var tt = t * t;
41965         var ttt = tt * t;
41966         var u = 1 - t;
41967         var uu = u * u;
41968         var uuu = uu * u;
41969         var x = uuu * curve.startPoint.x;
41970         x += 3 * uu * t * curve.control1.x;
41971         x += 3 * u * tt * curve.control2.x;
41972         x += ttt * curve.endPoint.x;
41973         var y = uuu * curve.startPoint.y;
41974         y += 3 * uu * t * curve.control1.y;
41975         y += 3 * u * tt * curve.control2.y;
41976         y += ttt * curve.endPoint.y;
41977         var width = curve.startWidth + ttt * widthDelta;
41978         this.drawCurveSegment(x, y, width);
41979         }
41980         ctx.closePath();
41981         ctx.fill();
41982     },
41983     
41984     drawCurveSegment: function (x, y, width) {
41985         var ctx = this.canvasElCtx();
41986         ctx.moveTo(x, y);
41987         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
41988         this.is_empty = false;
41989     },
41990     
41991     clear: function()
41992     {
41993         var ctx = this.canvasElCtx();
41994         var canvas = this.canvasEl().dom;
41995         ctx.fillStyle = this.bg_color;
41996         ctx.clearRect(0, 0, canvas.width, canvas.height);
41997         ctx.fillRect(0, 0, canvas.width, canvas.height);
41998         this.curve_data = [];
41999         this.reset();
42000         this.is_empty = true;
42001     },
42002     
42003     fileEl: function()
42004     {
42005         return  this.el.select('input',true).first();
42006     },
42007     
42008     canvasEl: function()
42009     {
42010         return this.el.select('canvas',true).first();
42011     },
42012     
42013     canvasElCtx: function()
42014     {
42015         return this.el.select('canvas',true).first().dom.getContext('2d');
42016     },
42017     
42018     getImage: function(type)
42019     {
42020         if(this.is_empty) {
42021             return false;
42022         }
42023         
42024         // encryption ?
42025         return this.canvasEl().dom.toDataURL('image/'+type, 1);
42026     },
42027     
42028     drawFromImage: function(img_src)
42029     {
42030         var img = new Image();
42031         
42032         img.onload = function(){
42033             this.canvasElCtx().drawImage(img, 0, 0);
42034         }.bind(this);
42035         
42036         img.src = img_src;
42037         
42038         this.is_empty = false;
42039     },
42040     
42041     selectImage: function()
42042     {
42043         this.fileEl().dom.click();
42044     },
42045     
42046     uploadImage: function(e)
42047     {
42048         var reader = new FileReader();
42049         
42050         reader.onload = function(e){
42051             var img = new Image();
42052             img.onload = function(){
42053                 this.reset();
42054                 this.canvasElCtx().drawImage(img, 0, 0);
42055             }.bind(this);
42056             img.src = e.target.result;
42057         }.bind(this);
42058         
42059         reader.readAsDataURL(e.target.files[0]);
42060     },
42061     
42062     // Bezier Point Constructor
42063     Point: (function () {
42064         function Point(x, y, time) {
42065             this.x = x;
42066             this.y = y;
42067             this.time = time || Date.now();
42068         }
42069         Point.prototype.distanceTo = function (start) {
42070             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42071         };
42072         Point.prototype.equals = function (other) {
42073             return this.x === other.x && this.y === other.y && this.time === other.time;
42074         };
42075         Point.prototype.velocityFrom = function (start) {
42076             return this.time !== start.time
42077             ? this.distanceTo(start) / (this.time - start.time)
42078             : 0;
42079         };
42080         return Point;
42081     }()),
42082     
42083     
42084     // Bezier Constructor
42085     Bezier: (function () {
42086         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42087             this.startPoint = startPoint;
42088             this.control2 = control2;
42089             this.control1 = control1;
42090             this.endPoint = endPoint;
42091             this.startWidth = startWidth;
42092             this.endWidth = endWidth;
42093         }
42094         Bezier.fromPoints = function (points, widths, scope) {
42095             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42096             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42097             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42098         };
42099         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42100             var dx1 = s1.x - s2.x;
42101             var dy1 = s1.y - s2.y;
42102             var dx2 = s2.x - s3.x;
42103             var dy2 = s2.y - s3.y;
42104             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42105             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42106             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42107             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42108             var dxm = m1.x - m2.x;
42109             var dym = m1.y - m2.y;
42110             var k = l2 / (l1 + l2);
42111             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42112             var tx = s2.x - cm.x;
42113             var ty = s2.y - cm.y;
42114             return {
42115                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42116                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42117             };
42118         };
42119         Bezier.prototype.length = function () {
42120             var steps = 10;
42121             var length = 0;
42122             var px;
42123             var py;
42124             for (var i = 0; i <= steps; i += 1) {
42125                 var t = i / steps;
42126                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42127                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42128                 if (i > 0) {
42129                     var xdiff = cx - px;
42130                     var ydiff = cy - py;
42131                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42132                 }
42133                 px = cx;
42134                 py = cy;
42135             }
42136             return length;
42137         };
42138         Bezier.prototype.point = function (t, start, c1, c2, end) {
42139             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42140             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42141             + (3.0 * c2 * (1.0 - t) * t * t)
42142             + (end * t * t * t);
42143         };
42144         return Bezier;
42145     }()),
42146     
42147     throttleStroke: function(fn, wait) {
42148       if (wait === void 0) { wait = 250; }
42149       var previous = 0;
42150       var timeout = null;
42151       var result;
42152       var storedContext;
42153       var storedArgs;
42154       var later = function () {
42155           previous = Date.now();
42156           timeout = null;
42157           result = fn.apply(storedContext, storedArgs);
42158           if (!timeout) {
42159               storedContext = null;
42160               storedArgs = [];
42161           }
42162       };
42163       return function wrapper() {
42164           var args = [];
42165           for (var _i = 0; _i < arguments.length; _i++) {
42166               args[_i] = arguments[_i];
42167           }
42168           var now = Date.now();
42169           var remaining = wait - (now - previous);
42170           storedContext = this;
42171           storedArgs = args;
42172           if (remaining <= 0 || remaining > wait) {
42173               if (timeout) {
42174                   clearTimeout(timeout);
42175                   timeout = null;
42176               }
42177               previous = now;
42178               result = fn.apply(storedContext, storedArgs);
42179               if (!timeout) {
42180                   storedContext = null;
42181                   storedArgs = [];
42182               }
42183           }
42184           else if (!timeout) {
42185               timeout = window.setTimeout(later, remaining);
42186           }
42187           return result;
42188       };
42189   }
42190   
42191 });
42192
42193  
42194
42195