Roo/bootstrap/Button.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[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass('hidden');
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass('hidden');
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587  * @cfg {String} size ( lg | sm | xs)
588  * @cfg {String} tag ( a | input | submit)
589  * @cfg {String} href empty or href
590  * @cfg {Boolean} disabled default false;
591  * @cfg {Boolean} isClose default false;
592  * @cfg {String} glyphicon depricated - use fs
593  * @cfg {String} badge text for badge
594  * @cfg {String} theme (default|glow)  
595  * @cfg {Boolean} inverse dark themed version
596  * @cfg {Boolean} toggle is it a slidy toggle button
597  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
598  * @cfg {String} ontext text for on slidy toggle state
599  * @cfg {String} offtext text for off slidy toggle state
600  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
601  * @cfg {Boolean} removeClass remove the standard class..
602  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
603  * 
604  * @constructor
605  * Create a new button
606  * @param {Object} config The config object
607  */
608
609
610 Roo.bootstrap.Button = function(config){
611     Roo.bootstrap.Button.superclass.constructor.call(this, config);
612     this.weightClass = ["btn-default btn-outline-secondary", 
613                        "btn-primary", 
614                        "btn-success", 
615                        "btn-info", 
616                        "btn-warning",
617                        "btn-danger",
618                        "btn-link"
619                       ],  
620     this.addEvents({
621         // raw events
622         /**
623          * @event click
624          * When a butotn is pressed
625          * @param {Roo.bootstrap.Button} btn
626          * @param {Roo.EventObject} e
627          */
628         "click" : true,
629          /**
630          * @event toggle
631          * After the button has been toggles
632          * @param {Roo.bootstrap.Button} btn
633          * @param {Roo.EventObject} e
634          * @param {boolean} pressed (also available as button.pressed)
635          */
636         "toggle" : true
637     });
638 };
639
640 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
641     html: false,
642     active: false,
643     weight: '',
644     badge_weight: '',
645     outline : false,
646     size: '',
647     tag: 'button',
648     href: '',
649     disabled: false,
650     isClose: false,
651     glyphicon: '',
652     fa: '',
653     badge: '',
654     theme: 'default',
655     inverse: false,
656     
657     toggle: false,
658     ontext: 'ON',
659     offtext: 'OFF',
660     defaulton: true,
661     preventDefault: true,
662     removeClass: false,
663     name: false,
664     target: false,
665      
666     pressed : null,
667      
668     
669     getAutoCreate : function(){
670         
671         var cfg = {
672             tag : 'button',
673             cls : 'roo-button',
674             html: ''
675         };
676         
677         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
678             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
679             this.tag = 'button';
680         } else {
681             cfg.tag = this.tag;
682         }
683         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
684         
685         if (this.toggle == true) {
686             cfg={
687                 tag: 'div',
688                 cls: 'slider-frame roo-button',
689                 cn: [
690                     {
691                         tag: 'span',
692                         'data-on-text':'ON',
693                         'data-off-text':'OFF',
694                         cls: 'slider-button',
695                         html: this.offtext
696                     }
697                 ]
698             };
699             
700             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
701                 cfg.cls += ' '+this.weight;
702             }
703             
704             return cfg;
705         }
706         
707         if (this.isClose) {
708             cfg.cls += ' close';
709             
710             cfg["aria-hidden"] = true;
711             
712             cfg.html = "&times;";
713             
714             return cfg;
715         }
716         
717          
718         if (this.theme==='default') {
719             cfg.cls = 'btn roo-button';
720             
721             //if (this.parentType != 'Navbar') {
722             this.weight = this.weight.length ?  this.weight : 'default';
723             //}
724             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
725                 
726                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
727                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
728                 cfg.cls += ' btn-' + outline + weight;
729                 if (this.weight == 'default') {
730                     // BC
731                     cfg.cls += ' btn-' + this.weight;
732                 }
733             }
734         } else if (this.theme==='glow') {
735             
736             cfg.tag = 'a';
737             cfg.cls = 'btn-glow roo-button';
738             
739             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
740                 
741                 cfg.cls += ' ' + this.weight;
742             }
743         }
744    
745         
746         if (this.inverse) {
747             this.cls += ' inverse';
748         }
749         
750         
751         if (this.active || this.pressed === true) {
752             cfg.cls += ' active';
753         }
754         
755         if (this.disabled) {
756             cfg.disabled = 'disabled';
757         }
758         
759         if (this.items) {
760             Roo.log('changing to ul' );
761             cfg.tag = 'ul';
762             this.glyphicon = 'caret';
763             if (Roo.bootstrap.version == 4) {
764                 this.fa = 'caret-down';
765             }
766             
767         }
768         
769         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
770          
771         //gsRoo.log(this.parentType);
772         if (this.parentType === 'Navbar' && !this.parent().bar) {
773             Roo.log('changing to li?');
774             
775             cfg.tag = 'li';
776             
777             cfg.cls = '';
778             cfg.cn =  [{
779                 tag : 'a',
780                 cls : 'roo-button',
781                 html : this.html,
782                 href : this.href || '#'
783             }];
784             if (this.menu) {
785                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
786                 cfg.cls += ' dropdown';
787             }   
788             
789             delete cfg.html;
790             
791         }
792         
793        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
794         
795         if (this.glyphicon) {
796             cfg.html = ' ' + cfg.html;
797             
798             cfg.cn = [
799                 {
800                     tag: 'span',
801                     cls: 'glyphicon glyphicon-' + this.glyphicon
802                 }
803             ];
804         }
805         if (this.fa) {
806             cfg.html = ' ' + cfg.html;
807             
808             cfg.cn = [
809                 {
810                     tag: 'i',
811                     cls: 'fa fas fa-' + this.fa
812                 }
813             ];
814         }
815         
816         if (this.badge) {
817             cfg.html += ' ';
818             
819             cfg.tag = 'a';
820             
821 //            cfg.cls='btn roo-button';
822             
823             cfg.href=this.href;
824             
825             var value = cfg.html;
826             
827             if(this.glyphicon){
828                 value = {
829                     tag: 'span',
830                     cls: 'glyphicon glyphicon-' + this.glyphicon,
831                     html: this.html
832                 };
833             }
834             if(this.fa){
835                 value = {
836                     tag: 'i',
837                     cls: 'fa fas fa-' + this.fa,
838                     html: this.html
839                 };
840             }
841             
842             var bw = this.badge_weight.length ? this.badge_weight :
843                 (this.weight.length ? this.weight : 'secondary');
844             bw = bw == 'default' ? 'secondary' : bw;
845             
846             cfg.cn = [
847                 value,
848                 {
849                     tag: 'span',
850                     cls: 'badge badge-' + bw,
851                     html: this.badge
852                 }
853             ];
854             
855             cfg.html='';
856         }
857         
858         if (this.menu) {
859             cfg.cls += ' dropdown';
860             cfg.html = typeof(cfg.html) != 'undefined' ?
861                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
862         }
863         
864         if (cfg.tag !== 'a' && this.href !== '') {
865             throw "Tag must be a to set href.";
866         } else if (this.href.length > 0) {
867             cfg.href = this.href;
868         }
869         
870         if(this.removeClass){
871             cfg.cls = '';
872         }
873         
874         if(this.target){
875             cfg.target = this.target;
876         }
877         
878         return cfg;
879     },
880     initEvents: function() {
881        // Roo.log('init events?');
882 //        Roo.log(this.el.dom);
883         // add the menu...
884         
885         if (typeof (this.menu) != 'undefined') {
886             this.menu.parentType = this.xtype;
887             this.menu.triggerEl = this.el;
888             this.addxtype(Roo.apply({}, this.menu));
889         }
890
891
892        if (this.el.hasClass('roo-button')) {
893             this.el.on('click', this.onClick, this);
894        } else {
895             this.el.select('.roo-button').on('click', this.onClick, this);
896        }
897        
898        if(this.removeClass){
899            this.el.on('click', this.onClick, this);
900        }
901        
902        this.el.enableDisplayMode();
903         
904     },
905     onClick : function(e)
906     {
907         if (this.disabled) {
908             return;
909         }
910         
911         Roo.log('button on click ');
912         if(this.preventDefault){
913             e.preventDefault();
914         }
915         
916         if (this.pressed === true || this.pressed === false) {
917             this.toggleActive(e);
918         }
919         
920         
921         this.fireEvent('click', this, e);
922     },
923     
924     /**
925      * Enables this button
926      */
927     enable : function()
928     {
929         this.disabled = false;
930         this.el.removeClass('disabled');
931     },
932     
933     /**
934      * Disable this button
935      */
936     disable : function()
937     {
938         this.disabled = true;
939         this.el.addClass('disabled');
940     },
941      /**
942      * sets the active state on/off, 
943      * @param {Boolean} state (optional) Force a particular state
944      */
945     setActive : function(v) {
946         
947         this.el[v ? 'addClass' : 'removeClass']('active');
948         this.pressed = v;
949     },
950      /**
951      * toggles the current active state 
952      */
953     toggleActive : function(e)
954     {
955         this.setActive(!this.pressed);
956         this.fireEvent('toggle', this, e, !this.pressed);
957     },
958      /**
959      * get the current active state
960      * @return {boolean} true if it's active
961      */
962     isActive : function()
963     {
964         return this.el.hasClass('active');
965     },
966     /**
967      * set the text of the first selected button
968      */
969     setText : function(str)
970     {
971         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
972     },
973     /**
974      * get the text of the first selected button
975      */
976     getText : function()
977     {
978         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
979     },
980     
981     setWeight : function(str)
982     {
983         this.el.removeClass(this.weightClass);
984         this.weight = str;
985         var outline = this.outline ? 'outline-' : '';
986         if (str == 'default') {
987             this.el.addClass('btn-default btn-outline-secondary');        
988             return;
989         }
990         this.el.addClass('btn-' + outline + str);        
991     }
992     
993     
994 });
995
996  /*
997  * - LGPL
998  *
999  * column
1000  * 
1001  */
1002
1003 /**
1004  * @class Roo.bootstrap.Column
1005  * @extends Roo.bootstrap.Component
1006  * Bootstrap Column class
1007  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1008  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1009  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1010  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1011  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1012  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1013  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1014  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1015  *
1016  * 
1017  * @cfg {Boolean} hidden (true|false) hide the element
1018  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1019  * @cfg {String} fa (ban|check|...) font awesome icon
1020  * @cfg {Number} fasize (1|2|....) font awsome size
1021
1022  * @cfg {String} icon (info-sign|check|...) glyphicon name
1023
1024  * @cfg {String} html content of column.
1025  * 
1026  * @constructor
1027  * Create a new Column
1028  * @param {Object} config The config object
1029  */
1030
1031 Roo.bootstrap.Column = function(config){
1032     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1033 };
1034
1035 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1036     
1037     xs: false,
1038     sm: false,
1039     md: false,
1040     lg: false,
1041     xsoff: false,
1042     smoff: false,
1043     mdoff: false,
1044     lgoff: false,
1045     html: '',
1046     offset: 0,
1047     alert: false,
1048     fa: false,
1049     icon : false,
1050     hidden : false,
1051     fasize : 1,
1052     
1053     getAutoCreate : function(){
1054         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1055         
1056         cfg = {
1057             tag: 'div',
1058             cls: 'column'
1059         };
1060         
1061         var settings=this;
1062         ['xs','sm','md','lg'].map(function(size){
1063             //Roo.log( size + ':' + settings[size]);
1064             
1065             if (settings[size+'off'] !== false) {
1066                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1067             }
1068             
1069             if (settings[size] === false) {
1070                 return;
1071             }
1072             
1073             if (!settings[size]) { // 0 = hidden
1074                 cfg.cls += ' hidden-' + size;
1075                 return;
1076             }
1077             cfg.cls += ' col-' + size + '-' + settings[size];
1078             
1079         });
1080         
1081         if (this.hidden) {
1082             cfg.cls += ' hidden';
1083         }
1084         
1085         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1086             cfg.cls +=' alert alert-' + this.alert;
1087         }
1088         
1089         
1090         if (this.html.length) {
1091             cfg.html = this.html;
1092         }
1093         if (this.fa) {
1094             var fasize = '';
1095             if (this.fasize > 1) {
1096                 fasize = ' fa-' + this.fasize + 'x';
1097             }
1098             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1099             
1100             
1101         }
1102         if (this.icon) {
1103             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1104         }
1105         
1106         return cfg;
1107     }
1108    
1109 });
1110
1111  
1112
1113  /*
1114  * - LGPL
1115  *
1116  * page container.
1117  * 
1118  */
1119
1120
1121 /**
1122  * @class Roo.bootstrap.Container
1123  * @extends Roo.bootstrap.Component
1124  * Bootstrap Container class
1125  * @cfg {Boolean} jumbotron is it a jumbotron element
1126  * @cfg {String} html content of element
1127  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1128  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1129  * @cfg {String} header content of header (for panel)
1130  * @cfg {String} footer content of footer (for panel)
1131  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1132  * @cfg {String} tag (header|aside|section) type of HTML tag.
1133  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1134  * @cfg {String} fa font awesome icon
1135  * @cfg {String} icon (info-sign|check|...) glyphicon name
1136  * @cfg {Boolean} hidden (true|false) hide the element
1137  * @cfg {Boolean} expandable (true|false) default false
1138  * @cfg {Boolean} expanded (true|false) default true
1139  * @cfg {String} rheader contet on the right of header
1140  * @cfg {Boolean} clickable (true|false) default false
1141
1142  *     
1143  * @constructor
1144  * Create a new Container
1145  * @param {Object} config The config object
1146  */
1147
1148 Roo.bootstrap.Container = function(config){
1149     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1150     
1151     this.addEvents({
1152         // raw events
1153          /**
1154          * @event expand
1155          * After the panel has been expand
1156          * 
1157          * @param {Roo.bootstrap.Container} this
1158          */
1159         "expand" : true,
1160         /**
1161          * @event collapse
1162          * After the panel has been collapsed
1163          * 
1164          * @param {Roo.bootstrap.Container} this
1165          */
1166         "collapse" : true,
1167         /**
1168          * @event click
1169          * When a element is chick
1170          * @param {Roo.bootstrap.Container} this
1171          * @param {Roo.EventObject} e
1172          */
1173         "click" : true
1174     });
1175 };
1176
1177 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1178     
1179     jumbotron : false,
1180     well: '',
1181     panel : '',
1182     header: '',
1183     footer : '',
1184     sticky: '',
1185     tag : false,
1186     alert : false,
1187     fa: false,
1188     icon : false,
1189     expandable : false,
1190     rheader : '',
1191     expanded : true,
1192     clickable: false,
1193   
1194      
1195     getChildContainer : function() {
1196         
1197         if(!this.el){
1198             return false;
1199         }
1200         
1201         if (this.panel.length) {
1202             return this.el.select('.panel-body',true).first();
1203         }
1204         
1205         return this.el;
1206     },
1207     
1208     
1209     getAutoCreate : function(){
1210         
1211         var cfg = {
1212             tag : this.tag || 'div',
1213             html : '',
1214             cls : ''
1215         };
1216         if (this.jumbotron) {
1217             cfg.cls = 'jumbotron';
1218         }
1219         
1220         
1221         
1222         // - this is applied by the parent..
1223         //if (this.cls) {
1224         //    cfg.cls = this.cls + '';
1225         //}
1226         
1227         if (this.sticky.length) {
1228             
1229             var bd = Roo.get(document.body);
1230             if (!bd.hasClass('bootstrap-sticky')) {
1231                 bd.addClass('bootstrap-sticky');
1232                 Roo.select('html',true).setStyle('height', '100%');
1233             }
1234              
1235             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1236         }
1237         
1238         
1239         if (this.well.length) {
1240             switch (this.well) {
1241                 case 'lg':
1242                 case 'sm':
1243                     cfg.cls +=' well well-' +this.well;
1244                     break;
1245                 default:
1246                     cfg.cls +=' well';
1247                     break;
1248             }
1249         }
1250         
1251         if (this.hidden) {
1252             cfg.cls += ' hidden';
1253         }
1254         
1255         
1256         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1257             cfg.cls +=' alert alert-' + this.alert;
1258         }
1259         
1260         var body = cfg;
1261         
1262         if (this.panel.length) {
1263             cfg.cls += ' panel panel-' + this.panel;
1264             cfg.cn = [];
1265             if (this.header.length) {
1266                 
1267                 var h = [];
1268                 
1269                 if(this.expandable){
1270                     
1271                     cfg.cls = cfg.cls + ' expandable';
1272                     
1273                     h.push({
1274                         tag: 'i',
1275                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1276                     });
1277                     
1278                 }
1279                 
1280                 h.push(
1281                     {
1282                         tag: 'span',
1283                         cls : 'panel-title',
1284                         html : (this.expandable ? '&nbsp;' : '') + this.header
1285                     },
1286                     {
1287                         tag: 'span',
1288                         cls: 'panel-header-right',
1289                         html: this.rheader
1290                     }
1291                 );
1292                 
1293                 cfg.cn.push({
1294                     cls : 'panel-heading',
1295                     style : this.expandable ? 'cursor: pointer' : '',
1296                     cn : h
1297                 });
1298                 
1299             }
1300             
1301             body = false;
1302             cfg.cn.push({
1303                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1304                 html : this.html
1305             });
1306             
1307             
1308             if (this.footer.length) {
1309                 cfg.cn.push({
1310                     cls : 'panel-footer',
1311                     html : this.footer
1312                     
1313                 });
1314             }
1315             
1316         }
1317         
1318         if (body) {
1319             body.html = this.html || cfg.html;
1320             // prefix with the icons..
1321             if (this.fa) {
1322                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1323             }
1324             if (this.icon) {
1325                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1326             }
1327             
1328             
1329         }
1330         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1331             cfg.cls =  'container';
1332         }
1333         
1334         return cfg;
1335     },
1336     
1337     initEvents: function() 
1338     {
1339         if(this.expandable){
1340             var headerEl = this.headerEl();
1341         
1342             if(headerEl){
1343                 headerEl.on('click', this.onToggleClick, this);
1344             }
1345         }
1346         
1347         if(this.clickable){
1348             this.el.on('click', this.onClick, this);
1349         }
1350         
1351     },
1352     
1353     onToggleClick : function()
1354     {
1355         var headerEl = this.headerEl();
1356         
1357         if(!headerEl){
1358             return;
1359         }
1360         
1361         if(this.expanded){
1362             this.collapse();
1363             return;
1364         }
1365         
1366         this.expand();
1367     },
1368     
1369     expand : function()
1370     {
1371         if(this.fireEvent('expand', this)) {
1372             
1373             this.expanded = true;
1374             
1375             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1376             
1377             this.el.select('.panel-body',true).first().removeClass('hide');
1378             
1379             var toggleEl = this.toggleEl();
1380
1381             if(!toggleEl){
1382                 return;
1383             }
1384
1385             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1386         }
1387         
1388     },
1389     
1390     collapse : function()
1391     {
1392         if(this.fireEvent('collapse', this)) {
1393             
1394             this.expanded = false;
1395             
1396             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1397             this.el.select('.panel-body',true).first().addClass('hide');
1398         
1399             var toggleEl = this.toggleEl();
1400
1401             if(!toggleEl){
1402                 return;
1403             }
1404
1405             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1406         }
1407     },
1408     
1409     toggleEl : function()
1410     {
1411         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1412             return;
1413         }
1414         
1415         return this.el.select('.panel-heading .fa',true).first();
1416     },
1417     
1418     headerEl : function()
1419     {
1420         if(!this.el || !this.panel.length || !this.header.length){
1421             return;
1422         }
1423         
1424         return this.el.select('.panel-heading',true).first()
1425     },
1426     
1427     bodyEl : function()
1428     {
1429         if(!this.el || !this.panel.length){
1430             return;
1431         }
1432         
1433         return this.el.select('.panel-body',true).first()
1434     },
1435     
1436     titleEl : function()
1437     {
1438         if(!this.el || !this.panel.length || !this.header.length){
1439             return;
1440         }
1441         
1442         return this.el.select('.panel-title',true).first();
1443     },
1444     
1445     setTitle : function(v)
1446     {
1447         var titleEl = this.titleEl();
1448         
1449         if(!titleEl){
1450             return;
1451         }
1452         
1453         titleEl.dom.innerHTML = v;
1454     },
1455     
1456     getTitle : function()
1457     {
1458         
1459         var titleEl = this.titleEl();
1460         
1461         if(!titleEl){
1462             return '';
1463         }
1464         
1465         return titleEl.dom.innerHTML;
1466     },
1467     
1468     setRightTitle : function(v)
1469     {
1470         var t = this.el.select('.panel-header-right',true).first();
1471         
1472         if(!t){
1473             return;
1474         }
1475         
1476         t.dom.innerHTML = v;
1477     },
1478     
1479     onClick : function(e)
1480     {
1481         e.preventDefault();
1482         
1483         this.fireEvent('click', this, e);
1484     }
1485 });
1486
1487  /*
1488  * - LGPL
1489  *
1490  * image
1491  * 
1492  */
1493
1494
1495 /**
1496  * @class Roo.bootstrap.Img
1497  * @extends Roo.bootstrap.Component
1498  * Bootstrap Img class
1499  * @cfg {Boolean} imgResponsive false | true
1500  * @cfg {String} border rounded | circle | thumbnail
1501  * @cfg {String} src image source
1502  * @cfg {String} alt image alternative text
1503  * @cfg {String} href a tag href
1504  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1505  * @cfg {String} xsUrl xs image source
1506  * @cfg {String} smUrl sm image source
1507  * @cfg {String} mdUrl md image source
1508  * @cfg {String} lgUrl lg image source
1509  * 
1510  * @constructor
1511  * Create a new Input
1512  * @param {Object} config The config object
1513  */
1514
1515 Roo.bootstrap.Img = function(config){
1516     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1517     
1518     this.addEvents({
1519         // img events
1520         /**
1521          * @event click
1522          * The img click event for the img.
1523          * @param {Roo.EventObject} e
1524          */
1525         "click" : true
1526     });
1527 };
1528
1529 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1530     
1531     imgResponsive: true,
1532     border: '',
1533     src: 'about:blank',
1534     href: false,
1535     target: false,
1536     xsUrl: '',
1537     smUrl: '',
1538     mdUrl: '',
1539     lgUrl: '',
1540
1541     getAutoCreate : function()
1542     {   
1543         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1544             return this.createSingleImg();
1545         }
1546         
1547         var cfg = {
1548             tag: 'div',
1549             cls: 'roo-image-responsive-group',
1550             cn: []
1551         };
1552         var _this = this;
1553         
1554         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1555             
1556             if(!_this[size + 'Url']){
1557                 return;
1558             }
1559             
1560             var img = {
1561                 tag: 'img',
1562                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1563                 html: _this.html || cfg.html,
1564                 src: _this[size + 'Url']
1565             };
1566             
1567             img.cls += ' roo-image-responsive-' + size;
1568             
1569             var s = ['xs', 'sm', 'md', 'lg'];
1570             
1571             s.splice(s.indexOf(size), 1);
1572             
1573             Roo.each(s, function(ss){
1574                 img.cls += ' hidden-' + ss;
1575             });
1576             
1577             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1578                 cfg.cls += ' img-' + _this.border;
1579             }
1580             
1581             if(_this.alt){
1582                 cfg.alt = _this.alt;
1583             }
1584             
1585             if(_this.href){
1586                 var a = {
1587                     tag: 'a',
1588                     href: _this.href,
1589                     cn: [
1590                         img
1591                     ]
1592                 };
1593
1594                 if(this.target){
1595                     a.target = _this.target;
1596                 }
1597             }
1598             
1599             cfg.cn.push((_this.href) ? a : img);
1600             
1601         });
1602         
1603         return cfg;
1604     },
1605     
1606     createSingleImg : function()
1607     {
1608         var cfg = {
1609             tag: 'img',
1610             cls: (this.imgResponsive) ? 'img-responsive' : '',
1611             html : null,
1612             src : 'about:blank'  // just incase src get's set to undefined?!?
1613         };
1614         
1615         cfg.html = this.html || cfg.html;
1616         
1617         cfg.src = this.src || cfg.src;
1618         
1619         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1620             cfg.cls += ' img-' + this.border;
1621         }
1622         
1623         if(this.alt){
1624             cfg.alt = this.alt;
1625         }
1626         
1627         if(this.href){
1628             var a = {
1629                 tag: 'a',
1630                 href: this.href,
1631                 cn: [
1632                     cfg
1633                 ]
1634             };
1635             
1636             if(this.target){
1637                 a.target = this.target;
1638             }
1639             
1640         }
1641         
1642         return (this.href) ? a : cfg;
1643     },
1644     
1645     initEvents: function() 
1646     {
1647         if(!this.href){
1648             this.el.on('click', this.onClick, this);
1649         }
1650         
1651     },
1652     
1653     onClick : function(e)
1654     {
1655         Roo.log('img onclick');
1656         this.fireEvent('click', this, e);
1657     },
1658     /**
1659      * Sets the url of the image - used to update it
1660      * @param {String} url the url of the image
1661      */
1662     
1663     setSrc : function(url)
1664     {
1665         this.src =  url;
1666         
1667         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1668             this.el.dom.src =  url;
1669             return;
1670         }
1671         
1672         this.el.select('img', true).first().dom.src =  url;
1673     }
1674     
1675     
1676    
1677 });
1678
1679  /*
1680  * - LGPL
1681  *
1682  * image
1683  * 
1684  */
1685
1686
1687 /**
1688  * @class Roo.bootstrap.Link
1689  * @extends Roo.bootstrap.Component
1690  * Bootstrap Link Class
1691  * @cfg {String} alt image alternative text
1692  * @cfg {String} href a tag href
1693  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1694  * @cfg {String} html the content of the link.
1695  * @cfg {String} anchor name for the anchor link
1696  * @cfg {String} fa - favicon
1697
1698  * @cfg {Boolean} preventDefault (true | false) default false
1699
1700  * 
1701  * @constructor
1702  * Create a new Input
1703  * @param {Object} config The config object
1704  */
1705
1706 Roo.bootstrap.Link = function(config){
1707     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1708     
1709     this.addEvents({
1710         // img events
1711         /**
1712          * @event click
1713          * The img click event for the img.
1714          * @param {Roo.EventObject} e
1715          */
1716         "click" : true
1717     });
1718 };
1719
1720 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1721     
1722     href: false,
1723     target: false,
1724     preventDefault: false,
1725     anchor : false,
1726     alt : false,
1727     fa: false,
1728
1729
1730     getAutoCreate : function()
1731     {
1732         var html = this.html || '';
1733         
1734         if (this.fa !== false) {
1735             html = '<i class="fa fa-' + this.fa + '"></i>';
1736         }
1737         var cfg = {
1738             tag: 'a'
1739         };
1740         // anchor's do not require html/href...
1741         if (this.anchor === false) {
1742             cfg.html = html;
1743             cfg.href = this.href || '#';
1744         } else {
1745             cfg.name = this.anchor;
1746             if (this.html !== false || this.fa !== false) {
1747                 cfg.html = html;
1748             }
1749             if (this.href !== false) {
1750                 cfg.href = this.href;
1751             }
1752         }
1753         
1754         if(this.alt !== false){
1755             cfg.alt = this.alt;
1756         }
1757         
1758         
1759         if(this.target !== false) {
1760             cfg.target = this.target;
1761         }
1762         
1763         return cfg;
1764     },
1765     
1766     initEvents: function() {
1767         
1768         if(!this.href || this.preventDefault){
1769             this.el.on('click', this.onClick, this);
1770         }
1771     },
1772     
1773     onClick : function(e)
1774     {
1775         if(this.preventDefault){
1776             e.preventDefault();
1777         }
1778         //Roo.log('img onclick');
1779         this.fireEvent('click', this, e);
1780     }
1781    
1782 });
1783
1784  /*
1785  * - LGPL
1786  *
1787  * header
1788  * 
1789  */
1790
1791 /**
1792  * @class Roo.bootstrap.Header
1793  * @extends Roo.bootstrap.Component
1794  * Bootstrap Header class
1795  * @cfg {String} html content of header
1796  * @cfg {Number} level (1|2|3|4|5|6) default 1
1797  * 
1798  * @constructor
1799  * Create a new Header
1800  * @param {Object} config The config object
1801  */
1802
1803
1804 Roo.bootstrap.Header  = function(config){
1805     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1806 };
1807
1808 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1809     
1810     //href : false,
1811     html : false,
1812     level : 1,
1813     
1814     
1815     
1816     getAutoCreate : function(){
1817         
1818         
1819         
1820         var cfg = {
1821             tag: 'h' + (1 *this.level),
1822             html: this.html || ''
1823         } ;
1824         
1825         return cfg;
1826     }
1827    
1828 });
1829
1830  
1831
1832  /*
1833  * Based on:
1834  * Ext JS Library 1.1.1
1835  * Copyright(c) 2006-2007, Ext JS, LLC.
1836  *
1837  * Originally Released Under LGPL - original licence link has changed is not relivant.
1838  *
1839  * Fork - LGPL
1840  * <script type="text/javascript">
1841  */
1842  
1843 /**
1844  * @class Roo.bootstrap.MenuMgr
1845  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1846  * @singleton
1847  */
1848 Roo.bootstrap.MenuMgr = function(){
1849    var menus, active, groups = {}, attached = false, lastShow = new Date();
1850
1851    // private - called when first menu is created
1852    function init(){
1853        menus = {};
1854        active = new Roo.util.MixedCollection();
1855        Roo.get(document).addKeyListener(27, function(){
1856            if(active.length > 0){
1857                hideAll();
1858            }
1859        });
1860    }
1861
1862    // private
1863    function hideAll(){
1864        if(active && active.length > 0){
1865            var c = active.clone();
1866            c.each(function(m){
1867                m.hide();
1868            });
1869        }
1870    }
1871
1872    // private
1873    function onHide(m){
1874        active.remove(m);
1875        if(active.length < 1){
1876            Roo.get(document).un("mouseup", onMouseDown);
1877             
1878            attached = false;
1879        }
1880    }
1881
1882    // private
1883    function onShow(m){
1884        var last = active.last();
1885        lastShow = new Date();
1886        active.add(m);
1887        if(!attached){
1888           Roo.get(document).on("mouseup", onMouseDown);
1889            
1890            attached = true;
1891        }
1892        if(m.parentMenu){
1893           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1894           m.parentMenu.activeChild = m;
1895        }else if(last && last.isVisible()){
1896           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1897        }
1898    }
1899
1900    // private
1901    function onBeforeHide(m){
1902        if(m.activeChild){
1903            m.activeChild.hide();
1904        }
1905        if(m.autoHideTimer){
1906            clearTimeout(m.autoHideTimer);
1907            delete m.autoHideTimer;
1908        }
1909    }
1910
1911    // private
1912    function onBeforeShow(m){
1913        var pm = m.parentMenu;
1914        if(!pm && !m.allowOtherMenus){
1915            hideAll();
1916        }else if(pm && pm.activeChild && active != m){
1917            pm.activeChild.hide();
1918        }
1919    }
1920
1921    // private this should really trigger on mouseup..
1922    function onMouseDown(e){
1923         Roo.log("on Mouse Up");
1924         
1925         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1926             Roo.log("MenuManager hideAll");
1927             hideAll();
1928             e.stopEvent();
1929         }
1930         
1931         
1932    }
1933
1934    // private
1935    function onBeforeCheck(mi, state){
1936        if(state){
1937            var g = groups[mi.group];
1938            for(var i = 0, l = g.length; i < l; i++){
1939                if(g[i] != mi){
1940                    g[i].setChecked(false);
1941                }
1942            }
1943        }
1944    }
1945
1946    return {
1947
1948        /**
1949         * Hides all menus that are currently visible
1950         */
1951        hideAll : function(){
1952             hideAll();  
1953        },
1954
1955        // private
1956        register : function(menu){
1957            if(!menus){
1958                init();
1959            }
1960            menus[menu.id] = menu;
1961            menu.on("beforehide", onBeforeHide);
1962            menu.on("hide", onHide);
1963            menu.on("beforeshow", onBeforeShow);
1964            menu.on("show", onShow);
1965            var g = menu.group;
1966            if(g && menu.events["checkchange"]){
1967                if(!groups[g]){
1968                    groups[g] = [];
1969                }
1970                groups[g].push(menu);
1971                menu.on("checkchange", onCheck);
1972            }
1973        },
1974
1975         /**
1976          * Returns a {@link Roo.menu.Menu} object
1977          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1978          * be used to generate and return a new Menu instance.
1979          */
1980        get : function(menu){
1981            if(typeof menu == "string"){ // menu id
1982                return menus[menu];
1983            }else if(menu.events){  // menu instance
1984                return menu;
1985            }
1986            /*else if(typeof menu.length == 'number'){ // array of menu items?
1987                return new Roo.bootstrap.Menu({items:menu});
1988            }else{ // otherwise, must be a config
1989                return new Roo.bootstrap.Menu(menu);
1990            }
1991            */
1992            return false;
1993        },
1994
1995        // private
1996        unregister : function(menu){
1997            delete menus[menu.id];
1998            menu.un("beforehide", onBeforeHide);
1999            menu.un("hide", onHide);
2000            menu.un("beforeshow", onBeforeShow);
2001            menu.un("show", onShow);
2002            var g = menu.group;
2003            if(g && menu.events["checkchange"]){
2004                groups[g].remove(menu);
2005                menu.un("checkchange", onCheck);
2006            }
2007        },
2008
2009        // private
2010        registerCheckable : function(menuItem){
2011            var g = menuItem.group;
2012            if(g){
2013                if(!groups[g]){
2014                    groups[g] = [];
2015                }
2016                groups[g].push(menuItem);
2017                menuItem.on("beforecheckchange", onBeforeCheck);
2018            }
2019        },
2020
2021        // private
2022        unregisterCheckable : function(menuItem){
2023            var g = menuItem.group;
2024            if(g){
2025                groups[g].remove(menuItem);
2026                menuItem.un("beforecheckchange", onBeforeCheck);
2027            }
2028        }
2029    };
2030 }();/*
2031  * - LGPL
2032  *
2033  * menu
2034  * 
2035  */
2036
2037 /**
2038  * @class Roo.bootstrap.Menu
2039  * @extends Roo.bootstrap.Component
2040  * Bootstrap Menu class - container for MenuItems
2041  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2042  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2043  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2044  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2045  * 
2046  * @constructor
2047  * Create a new Menu
2048  * @param {Object} config The config object
2049  */
2050
2051
2052 Roo.bootstrap.Menu = function(config){
2053     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2054     if (this.registerMenu && this.type != 'treeview')  {
2055         Roo.bootstrap.MenuMgr.register(this);
2056     }
2057     
2058     
2059     this.addEvents({
2060         /**
2061          * @event beforeshow
2062          * Fires before this menu is displayed
2063          * @param {Roo.menu.Menu} this
2064          */
2065         beforeshow : true,
2066         /**
2067          * @event beforehide
2068          * Fires before this menu is hidden
2069          * @param {Roo.menu.Menu} this
2070          */
2071         beforehide : true,
2072         /**
2073          * @event show
2074          * Fires after this menu is displayed
2075          * @param {Roo.menu.Menu} this
2076          */
2077         show : true,
2078         /**
2079          * @event hide
2080          * Fires after this menu is hidden
2081          * @param {Roo.menu.Menu} this
2082          */
2083         hide : true,
2084         /**
2085          * @event click
2086          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2087          * @param {Roo.menu.Menu} this
2088          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2089          * @param {Roo.EventObject} e
2090          */
2091         click : true,
2092         /**
2093          * @event mouseover
2094          * Fires when the mouse is hovering over this menu
2095          * @param {Roo.menu.Menu} this
2096          * @param {Roo.EventObject} e
2097          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2098          */
2099         mouseover : true,
2100         /**
2101          * @event mouseout
2102          * Fires when the mouse exits this menu
2103          * @param {Roo.menu.Menu} this
2104          * @param {Roo.EventObject} e
2105          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2106          */
2107         mouseout : true,
2108         /**
2109          * @event itemclick
2110          * Fires when a menu item contained in this menu is clicked
2111          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2112          * @param {Roo.EventObject} e
2113          */
2114         itemclick: true
2115     });
2116     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2117 };
2118
2119 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2120     
2121    /// html : false,
2122     //align : '',
2123     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2124     type: false,
2125     /**
2126      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2127      */
2128     registerMenu : true,
2129     
2130     menuItems :false, // stores the menu items..
2131     
2132     hidden:true,
2133         
2134     parentMenu : false,
2135     
2136     stopEvent : true,
2137     
2138     isLink : false,
2139     
2140     getChildContainer : function() {
2141         return this.el;  
2142     },
2143     
2144     getAutoCreate : function(){
2145          
2146         //if (['right'].indexOf(this.align)!==-1) {
2147         //    cfg.cn[1].cls += ' pull-right'
2148         //}
2149         
2150         
2151         var cfg = {
2152             tag : 'ul',
2153             cls : 'dropdown-menu' ,
2154             style : 'z-index:1000'
2155             
2156         };
2157         
2158         if (this.type === 'submenu') {
2159             cfg.cls = 'submenu active';
2160         }
2161         if (this.type === 'treeview') {
2162             cfg.cls = 'treeview-menu';
2163         }
2164         
2165         return cfg;
2166     },
2167     initEvents : function() {
2168         
2169        // Roo.log("ADD event");
2170        // Roo.log(this.triggerEl.dom);
2171         
2172         this.triggerEl.on('click', this.onTriggerClick, this);
2173         
2174         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2175         
2176         
2177         if (this.triggerEl.hasClass('nav-item')) {
2178             // dropdown toggle on the 'a' in BS4?
2179             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2180         } else {
2181             this.triggerEl.addClass('dropdown-toggle');
2182         }
2183         if (Roo.isTouch) {
2184             this.el.on('touchstart'  , this.onTouch, this);
2185         }
2186         this.el.on('click' , this.onClick, this);
2187
2188         this.el.on("mouseover", this.onMouseOver, this);
2189         this.el.on("mouseout", this.onMouseOut, this);
2190         
2191     },
2192     
2193     findTargetItem : function(e)
2194     {
2195         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2196         if(!t){
2197             return false;
2198         }
2199         //Roo.log(t);         Roo.log(t.id);
2200         if(t && t.id){
2201             //Roo.log(this.menuitems);
2202             return this.menuitems.get(t.id);
2203             
2204             //return this.items.get(t.menuItemId);
2205         }
2206         
2207         return false;
2208     },
2209     
2210     onTouch : function(e) 
2211     {
2212         Roo.log("menu.onTouch");
2213         //e.stopEvent(); this make the user popdown broken
2214         this.onClick(e);
2215     },
2216     
2217     onClick : function(e)
2218     {
2219         Roo.log("menu.onClick");
2220         
2221         var t = this.findTargetItem(e);
2222         if(!t || t.isContainer){
2223             return;
2224         }
2225         Roo.log(e);
2226         /*
2227         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2228             if(t == this.activeItem && t.shouldDeactivate(e)){
2229                 this.activeItem.deactivate();
2230                 delete this.activeItem;
2231                 return;
2232             }
2233             if(t.canActivate){
2234                 this.setActiveItem(t, true);
2235             }
2236             return;
2237             
2238             
2239         }
2240         */
2241        
2242         Roo.log('pass click event');
2243         
2244         t.onClick(e);
2245         
2246         this.fireEvent("click", this, t, e);
2247         
2248         var _this = this;
2249         
2250         if(!t.href.length || t.href == '#'){
2251             (function() { _this.hide(); }).defer(100);
2252         }
2253         
2254     },
2255     
2256     onMouseOver : function(e){
2257         var t  = this.findTargetItem(e);
2258         //Roo.log(t);
2259         //if(t){
2260         //    if(t.canActivate && !t.disabled){
2261         //        this.setActiveItem(t, true);
2262         //    }
2263         //}
2264         
2265         this.fireEvent("mouseover", this, e, t);
2266     },
2267     isVisible : function(){
2268         return !this.hidden;
2269     },
2270      onMouseOut : function(e){
2271         var t  = this.findTargetItem(e);
2272         
2273         //if(t ){
2274         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2275         //        this.activeItem.deactivate();
2276         //        delete this.activeItem;
2277         //    }
2278         //}
2279         this.fireEvent("mouseout", this, e, t);
2280     },
2281     
2282     
2283     /**
2284      * Displays this menu relative to another element
2285      * @param {String/HTMLElement/Roo.Element} element The element to align to
2286      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2287      * the element (defaults to this.defaultAlign)
2288      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2289      */
2290     show : function(el, pos, parentMenu){
2291         this.parentMenu = parentMenu;
2292         if(!this.el){
2293             this.render();
2294         }
2295         this.fireEvent("beforeshow", this);
2296         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2297     },
2298      /**
2299      * Displays this menu at a specific xy position
2300      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2301      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2302      */
2303     showAt : function(xy, parentMenu, /* private: */_e){
2304         this.parentMenu = parentMenu;
2305         if(!this.el){
2306             this.render();
2307         }
2308         if(_e !== false){
2309             this.fireEvent("beforeshow", this);
2310             //xy = this.el.adjustForConstraints(xy);
2311         }
2312         
2313         //this.el.show();
2314         this.hideMenuItems();
2315         this.hidden = false;
2316         this.triggerEl.addClass('open');
2317         this.el.addClass('show');
2318         
2319         // reassign x when hitting right
2320         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2321             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2322         }
2323         
2324         // reassign y when hitting bottom
2325         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2326             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2327         }
2328         
2329         // but the list may align on trigger left or trigger top... should it be a properity?
2330         
2331         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2332             this.el.setXY(xy);
2333         }
2334         
2335         this.focus();
2336         this.fireEvent("show", this);
2337     },
2338     
2339     focus : function(){
2340         return;
2341         if(!this.hidden){
2342             this.doFocus.defer(50, this);
2343         }
2344     },
2345
2346     doFocus : function(){
2347         if(!this.hidden){
2348             this.focusEl.focus();
2349         }
2350     },
2351
2352     /**
2353      * Hides this menu and optionally all parent menus
2354      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2355      */
2356     hide : function(deep)
2357     {
2358         
2359         this.hideMenuItems();
2360         if(this.el && this.isVisible()){
2361             this.fireEvent("beforehide", this);
2362             if(this.activeItem){
2363                 this.activeItem.deactivate();
2364                 this.activeItem = null;
2365             }
2366             this.triggerEl.removeClass('open');;
2367             this.el.removeClass('show');
2368             this.hidden = true;
2369             this.fireEvent("hide", this);
2370         }
2371         if(deep === true && this.parentMenu){
2372             this.parentMenu.hide(true);
2373         }
2374     },
2375     
2376     onTriggerClick : function(e)
2377     {
2378         Roo.log('trigger click');
2379         
2380         var target = e.getTarget();
2381         
2382         Roo.log(target.nodeName.toLowerCase());
2383         
2384         if(target.nodeName.toLowerCase() === 'i'){
2385             e.preventDefault();
2386         }
2387         
2388     },
2389     
2390     onTriggerPress  : function(e)
2391     {
2392         Roo.log('trigger press');
2393         //Roo.log(e.getTarget());
2394        // Roo.log(this.triggerEl.dom);
2395        
2396         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2397         var pel = Roo.get(e.getTarget());
2398         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2399             Roo.log('is treeview or dropdown?');
2400             return;
2401         }
2402         
2403         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2404             return;
2405         }
2406         
2407         if (this.isVisible()) {
2408             Roo.log('hide');
2409             this.hide();
2410         } else {
2411             Roo.log('show');
2412             this.show(this.triggerEl, false, false);
2413         }
2414         
2415         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2416             e.stopEvent();
2417         }
2418         
2419     },
2420        
2421     
2422     hideMenuItems : function()
2423     {
2424         Roo.log("hide Menu Items");
2425         if (!this.el) { 
2426             return;
2427         }
2428         //$(backdrop).remove()
2429         this.el.select('.open',true).each(function(aa) {
2430             
2431             aa.removeClass('open');
2432           //var parent = getParent($(this))
2433           //var relatedTarget = { relatedTarget: this }
2434           
2435            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2436           //if (e.isDefaultPrevented()) return
2437            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2438         });
2439     },
2440     addxtypeChild : function (tree, cntr) {
2441         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2442           
2443         this.menuitems.add(comp);
2444         return comp;
2445
2446     },
2447     getEl : function()
2448     {
2449         Roo.log(this.el);
2450         return this.el;
2451     },
2452     
2453     clear : function()
2454     {
2455         this.getEl().dom.innerHTML = '';
2456         this.menuitems.clear();
2457     }
2458 });
2459
2460  
2461  /*
2462  * - LGPL
2463  *
2464  * menu item
2465  * 
2466  */
2467
2468
2469 /**
2470  * @class Roo.bootstrap.MenuItem
2471  * @extends Roo.bootstrap.Component
2472  * Bootstrap MenuItem class
2473  * @cfg {String} html the menu label
2474  * @cfg {String} href the link
2475  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2476  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2477  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2478  * @cfg {String} fa favicon to show on left of menu item.
2479  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2480  * 
2481  * 
2482  * @constructor
2483  * Create a new MenuItem
2484  * @param {Object} config The config object
2485  */
2486
2487
2488 Roo.bootstrap.MenuItem = function(config){
2489     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2490     this.addEvents({
2491         // raw events
2492         /**
2493          * @event click
2494          * The raw click event for the entire grid.
2495          * @param {Roo.bootstrap.MenuItem} this
2496          * @param {Roo.EventObject} e
2497          */
2498         "click" : true
2499     });
2500 };
2501
2502 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2503     
2504     href : false,
2505     html : false,
2506     preventDefault: false,
2507     isContainer : false,
2508     active : false,
2509     fa: false,
2510     
2511     getAutoCreate : function(){
2512         
2513         if(this.isContainer){
2514             return {
2515                 tag: 'li',
2516                 cls: 'dropdown-menu-item dropdown-item'
2517             };
2518         }
2519         var ctag = {
2520             tag: 'span',
2521             html: 'Link'
2522         };
2523         
2524         var anc = {
2525             tag : 'a',
2526             href : '#',
2527             cn : [  ]
2528         };
2529         
2530         if (this.fa !== false) {
2531             anc.cn.push({
2532                 tag : 'i',
2533                 cls : 'fa fa-' + this.fa
2534             });
2535         }
2536         
2537         anc.cn.push(ctag);
2538         
2539         
2540         var cfg= {
2541             tag: 'li',
2542             cls: 'dropdown-menu-item dropdown-item',
2543             cn: [ anc ]
2544         };
2545         if (this.parent().type == 'treeview') {
2546             cfg.cls = 'treeview-menu';
2547         }
2548         if (this.active) {
2549             cfg.cls += ' active';
2550         }
2551         
2552         
2553         
2554         anc.href = this.href || cfg.cn[0].href ;
2555         ctag.html = this.html || cfg.cn[0].html ;
2556         return cfg;
2557     },
2558     
2559     initEvents: function()
2560     {
2561         if (this.parent().type == 'treeview') {
2562             this.el.select('a').on('click', this.onClick, this);
2563         }
2564         
2565         if (this.menu) {
2566             this.menu.parentType = this.xtype;
2567             this.menu.triggerEl = this.el;
2568             this.menu = this.addxtype(Roo.apply({}, this.menu));
2569         }
2570         
2571     },
2572     onClick : function(e)
2573     {
2574         Roo.log('item on click ');
2575         
2576         if(this.preventDefault){
2577             e.preventDefault();
2578         }
2579         //this.parent().hideMenuItems();
2580         
2581         this.fireEvent('click', this, e);
2582     },
2583     getEl : function()
2584     {
2585         return this.el;
2586     } 
2587 });
2588
2589  
2590
2591  /*
2592  * - LGPL
2593  *
2594  * menu separator
2595  * 
2596  */
2597
2598
2599 /**
2600  * @class Roo.bootstrap.MenuSeparator
2601  * @extends Roo.bootstrap.Component
2602  * Bootstrap MenuSeparator class
2603  * 
2604  * @constructor
2605  * Create a new MenuItem
2606  * @param {Object} config The config object
2607  */
2608
2609
2610 Roo.bootstrap.MenuSeparator = function(config){
2611     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2612 };
2613
2614 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2615     
2616     getAutoCreate : function(){
2617         var cfg = {
2618             cls: 'divider',
2619             tag : 'li'
2620         };
2621         
2622         return cfg;
2623     }
2624    
2625 });
2626
2627  
2628
2629  
2630 /*
2631 * Licence: LGPL
2632 */
2633
2634 /**
2635  * @class Roo.bootstrap.Modal
2636  * @extends Roo.bootstrap.Component
2637  * Bootstrap Modal class
2638  * @cfg {String} title Title of dialog
2639  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2640  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2641  * @cfg {Boolean} specificTitle default false
2642  * @cfg {Array} buttons Array of buttons or standard button set..
2643  * @cfg {String} buttonPosition (left|right|center) default right
2644  * @cfg {Boolean} animate default true
2645  * @cfg {Boolean} allow_close default true
2646  * @cfg {Boolean} fitwindow default false
2647  * @cfg {String} size (sm|lg) default empty
2648  * @cfg {Number} max_width set the max width of modal
2649  *
2650  *
2651  * @constructor
2652  * Create a new Modal Dialog
2653  * @param {Object} config The config object
2654  */
2655
2656 Roo.bootstrap.Modal = function(config){
2657     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2658     this.addEvents({
2659         // raw events
2660         /**
2661          * @event btnclick
2662          * The raw btnclick event for the button
2663          * @param {Roo.EventObject} e
2664          */
2665         "btnclick" : true,
2666         /**
2667          * @event resize
2668          * Fire when dialog resize
2669          * @param {Roo.bootstrap.Modal} this
2670          * @param {Roo.EventObject} e
2671          */
2672         "resize" : true
2673     });
2674     this.buttons = this.buttons || [];
2675
2676     if (this.tmpl) {
2677         this.tmpl = Roo.factory(this.tmpl);
2678     }
2679
2680 };
2681
2682 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2683
2684     title : 'test dialog',
2685
2686     buttons : false,
2687
2688     // set on load...
2689
2690     html: false,
2691
2692     tmp: false,
2693
2694     specificTitle: false,
2695
2696     buttonPosition: 'right',
2697
2698     allow_close : true,
2699
2700     animate : true,
2701
2702     fitwindow: false,
2703     
2704      // private
2705     dialogEl: false,
2706     bodyEl:  false,
2707     footerEl:  false,
2708     titleEl:  false,
2709     closeEl:  false,
2710
2711     size: '',
2712     
2713     max_width: 0,
2714     
2715     max_height: 0,
2716     
2717     fit_content: false,
2718
2719     onRender : function(ct, position)
2720     {
2721         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2722
2723         if(!this.el){
2724             var cfg = Roo.apply({},  this.getAutoCreate());
2725             cfg.id = Roo.id();
2726             //if(!cfg.name){
2727             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2728             //}
2729             //if (!cfg.name.length) {
2730             //    delete cfg.name;
2731            // }
2732             if (this.cls) {
2733                 cfg.cls += ' ' + this.cls;
2734             }
2735             if (this.style) {
2736                 cfg.style = this.style;
2737             }
2738             this.el = Roo.get(document.body).createChild(cfg, position);
2739         }
2740         //var type = this.el.dom.type;
2741
2742
2743         if(this.tabIndex !== undefined){
2744             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2745         }
2746
2747         this.dialogEl = this.el.select('.modal-dialog',true).first();
2748         this.bodyEl = this.el.select('.modal-body',true).first();
2749         this.closeEl = this.el.select('.modal-header .close', true).first();
2750         this.headerEl = this.el.select('.modal-header',true).first();
2751         this.titleEl = this.el.select('.modal-title',true).first();
2752         this.footerEl = this.el.select('.modal-footer',true).first();
2753
2754         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2755         
2756         //this.el.addClass("x-dlg-modal");
2757
2758         if (this.buttons.length) {
2759             Roo.each(this.buttons, function(bb) {
2760                 var b = Roo.apply({}, bb);
2761                 b.xns = b.xns || Roo.bootstrap;
2762                 b.xtype = b.xtype || 'Button';
2763                 if (typeof(b.listeners) == 'undefined') {
2764                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2765                 }
2766
2767                 var btn = Roo.factory(b);
2768
2769                 btn.render(this.el.select('.modal-footer div').first());
2770
2771             },this);
2772         }
2773         // render the children.
2774         var nitems = [];
2775
2776         if(typeof(this.items) != 'undefined'){
2777             var items = this.items;
2778             delete this.items;
2779
2780             for(var i =0;i < items.length;i++) {
2781                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2782             }
2783         }
2784
2785         this.items = nitems;
2786
2787         // where are these used - they used to be body/close/footer
2788
2789
2790         this.initEvents();
2791         //this.el.addClass([this.fieldClass, this.cls]);
2792
2793     },
2794
2795     getAutoCreate : function()
2796     {
2797         var bdy = {
2798                 cls : 'modal-body',
2799                 html : this.html || ''
2800         };
2801
2802         var title = {
2803             tag: 'h4',
2804             cls : 'modal-title',
2805             html : this.title
2806         };
2807
2808         if(this.specificTitle){
2809             title = this.title;
2810
2811         };
2812
2813         var header = [];
2814         if (this.allow_close && Roo.bootstrap.version == 3) {
2815             header.push({
2816                 tag: 'button',
2817                 cls : 'close',
2818                 html : '&times'
2819             });
2820         }
2821
2822         header.push(title);
2823
2824         if (this.allow_close && Roo.bootstrap.version == 4) {
2825             header.push({
2826                 tag: 'button',
2827                 cls : 'close',
2828                 html : '&times'
2829             });
2830         }
2831         
2832         var size = '';
2833
2834         if(this.size.length){
2835             size = 'modal-' + this.size;
2836         }
2837
2838         var modal = {
2839             cls: "modal",
2840              cn : [
2841                 {
2842                     cls: "modal-dialog " + size,
2843                     cn : [
2844                         {
2845                             cls : "modal-content",
2846                             cn : [
2847                                 {
2848                                     cls : 'modal-header',
2849                                     cn : header
2850                                 },
2851                                 bdy,
2852                                 {
2853                                     cls : 'modal-footer',
2854                                     cn : [
2855                                         {
2856                                             tag: 'div',
2857                                             cls: 'btn-' + this.buttonPosition
2858                                         }
2859                                     ]
2860
2861                                 }
2862
2863
2864                             ]
2865
2866                         }
2867                     ]
2868
2869                 }
2870             ]
2871         };
2872
2873         if(this.animate){
2874             modal.cls += ' fade';
2875         }
2876
2877         return modal;
2878
2879     },
2880     getChildContainer : function() {
2881
2882          return this.bodyEl;
2883
2884     },
2885     getButtonContainer : function() {
2886          return this.el.select('.modal-footer div',true).first();
2887
2888     },
2889     initEvents : function()
2890     {
2891         if (this.allow_close) {
2892             this.closeEl.on('click', this.hide, this);
2893         }
2894         Roo.EventManager.onWindowResize(this.resize, this, true);
2895
2896
2897     },
2898
2899     resize : function()
2900     {
2901         this.maskEl.setSize(
2902             Roo.lib.Dom.getViewWidth(true),
2903             Roo.lib.Dom.getViewHeight(true)
2904         );
2905         
2906         if (this.fitwindow) {
2907             this.setSize(
2908                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2909                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2910             );
2911             return;
2912         }
2913         
2914         if(this.max_width !== 0) {
2915             
2916             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2917             
2918             if(this.height) {
2919                 this.setSize(w, this.height);
2920                 return;
2921             }
2922             
2923             if(this.max_height) {
2924                 this.setSize(w,Math.min(
2925                     this.max_height,
2926                     Roo.lib.Dom.getViewportHeight(true) - 60
2927                 ));
2928                 
2929                 return;
2930             }
2931             
2932             if(!this.fit_content) {
2933                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2934                 return;
2935             }
2936             
2937             this.setSize(w, Math.min(
2938                 60 +
2939                 this.headerEl.getHeight() + 
2940                 this.footerEl.getHeight() + 
2941                 this.getChildHeight(this.bodyEl.dom.childNodes),
2942                 Roo.lib.Dom.getViewportHeight(true) - 60)
2943             );
2944         }
2945         
2946     },
2947
2948     setSize : function(w,h)
2949     {
2950         if (!w && !h) {
2951             return;
2952         }
2953         
2954         this.resizeTo(w,h);
2955     },
2956
2957     show : function() {
2958
2959         if (!this.rendered) {
2960             this.render();
2961         }
2962
2963         //this.el.setStyle('display', 'block');
2964         this.el.removeClass('hideing');
2965         this.el.dom.style.display='block';
2966         
2967         Roo.get(document.body).addClass('modal-open');
2968  
2969         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2970             var _this = this;
2971             (function(){
2972                 this.el.addClass('show');
2973                 this.el.addClass('in');
2974             }).defer(50, this);
2975         }else{
2976             this.el.addClass('show');
2977             this.el.addClass('in');
2978         }
2979
2980         // not sure how we can show data in here..
2981         //if (this.tmpl) {
2982         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2983         //}
2984
2985         Roo.get(document.body).addClass("x-body-masked");
2986         
2987         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2988         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2989         this.maskEl.dom.style.display = 'block';
2990         this.maskEl.addClass('show');
2991         
2992         
2993         this.resize();
2994         
2995         this.fireEvent('show', this);
2996
2997         // set zindex here - otherwise it appears to be ignored...
2998         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2999
3000         (function () {
3001             this.items.forEach( function(e) {
3002                 e.layout ? e.layout() : false;
3003
3004             });
3005         }).defer(100,this);
3006
3007     },
3008     hide : function()
3009     {
3010         if(this.fireEvent("beforehide", this) !== false){
3011             
3012             this.maskEl.removeClass('show');
3013             
3014             this.maskEl.dom.style.display = '';
3015             Roo.get(document.body).removeClass("x-body-masked");
3016             this.el.removeClass('in');
3017             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3018
3019             if(this.animate){ // why
3020                 this.el.addClass('hideing');
3021                 this.el.removeClass('show');
3022                 (function(){
3023                     if (!this.el.hasClass('hideing')) {
3024                         return; // it's been shown again...
3025                     }
3026                     
3027                     this.el.dom.style.display='';
3028
3029                     Roo.get(document.body).removeClass('modal-open');
3030                     this.el.removeClass('hideing');
3031                 }).defer(150,this);
3032                 
3033             }else{
3034                 this.el.removeClass('show');
3035                 this.el.dom.style.display='';
3036                 Roo.get(document.body).removeClass('modal-open');
3037
3038             }
3039             this.fireEvent('hide', this);
3040         }
3041     },
3042     isVisible : function()
3043     {
3044         
3045         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3046         
3047     },
3048
3049     addButton : function(str, cb)
3050     {
3051
3052
3053         var b = Roo.apply({}, { html : str } );
3054         b.xns = b.xns || Roo.bootstrap;
3055         b.xtype = b.xtype || 'Button';
3056         if (typeof(b.listeners) == 'undefined') {
3057             b.listeners = { click : cb.createDelegate(this)  };
3058         }
3059
3060         var btn = Roo.factory(b);
3061
3062         btn.render(this.el.select('.modal-footer div').first());
3063
3064         return btn;
3065
3066     },
3067
3068     setDefaultButton : function(btn)
3069     {
3070         //this.el.select('.modal-footer').()
3071     },
3072     diff : false,
3073
3074     resizeTo: function(w,h)
3075     {
3076         // skip.. ?? why??
3077
3078         this.dialogEl.setWidth(w);
3079         if (this.diff === false) {
3080             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3081         }
3082
3083         this.bodyEl.setHeight(h - this.diff);
3084
3085         this.fireEvent('resize', this);
3086
3087     },
3088     setContentSize  : function(w, h)
3089     {
3090
3091     },
3092     onButtonClick: function(btn,e)
3093     {
3094         //Roo.log([a,b,c]);
3095         this.fireEvent('btnclick', btn.name, e);
3096     },
3097      /**
3098      * Set the title of the Dialog
3099      * @param {String} str new Title
3100      */
3101     setTitle: function(str) {
3102         this.titleEl.dom.innerHTML = str;
3103     },
3104     /**
3105      * Set the body of the Dialog
3106      * @param {String} str new Title
3107      */
3108     setBody: function(str) {
3109         this.bodyEl.dom.innerHTML = str;
3110     },
3111     /**
3112      * Set the body of the Dialog using the template
3113      * @param {Obj} data - apply this data to the template and replace the body contents.
3114      */
3115     applyBody: function(obj)
3116     {
3117         if (!this.tmpl) {
3118             Roo.log("Error - using apply Body without a template");
3119             //code
3120         }
3121         this.tmpl.overwrite(this.bodyEl, obj);
3122     },
3123     
3124     getChildHeight : function(child_nodes)
3125     {
3126         if(
3127             !child_nodes ||
3128             child_nodes.length == 0
3129         ) {
3130             return;
3131         }
3132         
3133         var child_height = 0;
3134         
3135         for(var i = 0; i < child_nodes.length; i++) {
3136             
3137             /*
3138             * for modal with tabs...
3139             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3140                 
3141                 var layout_childs = child_nodes[i].childNodes;
3142                 
3143                 for(var j = 0; j < layout_childs.length; j++) {
3144                     
3145                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3146                         
3147                         var layout_body_childs = layout_childs[j].childNodes;
3148                         
3149                         for(var k = 0; k < layout_body_childs.length; k++) {
3150                             
3151                             if(layout_body_childs[k].classList.contains('navbar')) {
3152                                 child_height += layout_body_childs[k].offsetHeight;
3153                                 continue;
3154                             }
3155                             
3156                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3157                                 
3158                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3159                                 
3160                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3161                                     
3162                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3163                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3164                                         continue;
3165                                     }
3166                                     
3167                                 }
3168                                 
3169                             }
3170                             
3171                         }
3172                     }
3173                 }
3174                 continue;
3175             }
3176             */
3177             
3178             child_height += child_nodes[i].offsetHeight;
3179             // Roo.log(child_nodes[i].offsetHeight);
3180         }
3181         
3182         return child_height;
3183     }
3184
3185 });
3186
3187
3188 Roo.apply(Roo.bootstrap.Modal,  {
3189     /**
3190          * Button config that displays a single OK button
3191          * @type Object
3192          */
3193         OK :  [{
3194             name : 'ok',
3195             weight : 'primary',
3196             html : 'OK'
3197         }],
3198         /**
3199          * Button config that displays Yes and No buttons
3200          * @type Object
3201          */
3202         YESNO : [
3203             {
3204                 name  : 'no',
3205                 html : 'No'
3206             },
3207             {
3208                 name  :'yes',
3209                 weight : 'primary',
3210                 html : 'Yes'
3211             }
3212         ],
3213
3214         /**
3215          * Button config that displays OK and Cancel buttons
3216          * @type Object
3217          */
3218         OKCANCEL : [
3219             {
3220                name : 'cancel',
3221                 html : 'Cancel'
3222             },
3223             {
3224                 name : 'ok',
3225                 weight : 'primary',
3226                 html : 'OK'
3227             }
3228         ],
3229         /**
3230          * Button config that displays Yes, No and Cancel buttons
3231          * @type Object
3232          */
3233         YESNOCANCEL : [
3234             {
3235                 name : 'yes',
3236                 weight : 'primary',
3237                 html : 'Yes'
3238             },
3239             {
3240                 name : 'no',
3241                 html : 'No'
3242             },
3243             {
3244                 name : 'cancel',
3245                 html : 'Cancel'
3246             }
3247         ],
3248         
3249         zIndex : 10001
3250 });
3251 /*
3252  * - LGPL
3253  *
3254  * messagebox - can be used as a replace
3255  * 
3256  */
3257 /**
3258  * @class Roo.MessageBox
3259  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3260  * Example usage:
3261  *<pre><code>
3262 // Basic alert:
3263 Roo.Msg.alert('Status', 'Changes saved successfully.');
3264
3265 // Prompt for user data:
3266 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3267     if (btn == 'ok'){
3268         // process text value...
3269     }
3270 });
3271
3272 // Show a dialog using config options:
3273 Roo.Msg.show({
3274    title:'Save Changes?',
3275    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3276    buttons: Roo.Msg.YESNOCANCEL,
3277    fn: processResult,
3278    animEl: 'elId'
3279 });
3280 </code></pre>
3281  * @singleton
3282  */
3283 Roo.bootstrap.MessageBox = function(){
3284     var dlg, opt, mask, waitTimer;
3285     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3286     var buttons, activeTextEl, bwidth;
3287
3288     
3289     // private
3290     var handleButton = function(button){
3291         dlg.hide();
3292         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3293     };
3294
3295     // private
3296     var handleHide = function(){
3297         if(opt && opt.cls){
3298             dlg.el.removeClass(opt.cls);
3299         }
3300         //if(waitTimer){
3301         //    Roo.TaskMgr.stop(waitTimer);
3302         //    waitTimer = null;
3303         //}
3304     };
3305
3306     // private
3307     var updateButtons = function(b){
3308         var width = 0;
3309         if(!b){
3310             buttons["ok"].hide();
3311             buttons["cancel"].hide();
3312             buttons["yes"].hide();
3313             buttons["no"].hide();
3314             //dlg.footer.dom.style.display = 'none';
3315             return width;
3316         }
3317         dlg.footerEl.dom.style.display = '';
3318         for(var k in buttons){
3319             if(typeof buttons[k] != "function"){
3320                 if(b[k]){
3321                     buttons[k].show();
3322                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3323                     width += buttons[k].el.getWidth()+15;
3324                 }else{
3325                     buttons[k].hide();
3326                 }
3327             }
3328         }
3329         return width;
3330     };
3331
3332     // private
3333     var handleEsc = function(d, k, e){
3334         if(opt && opt.closable !== false){
3335             dlg.hide();
3336         }
3337         if(e){
3338             e.stopEvent();
3339         }
3340     };
3341
3342     return {
3343         /**
3344          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3345          * @return {Roo.BasicDialog} The BasicDialog element
3346          */
3347         getDialog : function(){
3348            if(!dlg){
3349                 dlg = new Roo.bootstrap.Modal( {
3350                     //draggable: true,
3351                     //resizable:false,
3352                     //constraintoviewport:false,
3353                     //fixedcenter:true,
3354                     //collapsible : false,
3355                     //shim:true,
3356                     //modal: true,
3357                 //    width: 'auto',
3358                   //  height:100,
3359                     //buttonAlign:"center",
3360                     closeClick : function(){
3361                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3362                             handleButton("no");
3363                         }else{
3364                             handleButton("cancel");
3365                         }
3366                     }
3367                 });
3368                 dlg.render();
3369                 dlg.on("hide", handleHide);
3370                 mask = dlg.mask;
3371                 //dlg.addKeyListener(27, handleEsc);
3372                 buttons = {};
3373                 this.buttons = buttons;
3374                 var bt = this.buttonText;
3375                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3376                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3377                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3378                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3379                 //Roo.log(buttons);
3380                 bodyEl = dlg.bodyEl.createChild({
3381
3382                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3383                         '<textarea class="roo-mb-textarea"></textarea>' +
3384                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3385                 });
3386                 msgEl = bodyEl.dom.firstChild;
3387                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3388                 textboxEl.enableDisplayMode();
3389                 textboxEl.addKeyListener([10,13], function(){
3390                     if(dlg.isVisible() && opt && opt.buttons){
3391                         if(opt.buttons.ok){
3392                             handleButton("ok");
3393                         }else if(opt.buttons.yes){
3394                             handleButton("yes");
3395                         }
3396                     }
3397                 });
3398                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3399                 textareaEl.enableDisplayMode();
3400                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3401                 progressEl.enableDisplayMode();
3402                 
3403                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3404                 var pf = progressEl.dom.firstChild;
3405                 if (pf) {
3406                     pp = Roo.get(pf.firstChild);
3407                     pp.setHeight(pf.offsetHeight);
3408                 }
3409                 
3410             }
3411             return dlg;
3412         },
3413
3414         /**
3415          * Updates the message box body text
3416          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3417          * the XHTML-compliant non-breaking space character '&amp;#160;')
3418          * @return {Roo.MessageBox} This message box
3419          */
3420         updateText : function(text)
3421         {
3422             if(!dlg.isVisible() && !opt.width){
3423                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3424                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3425             }
3426             msgEl.innerHTML = text || '&#160;';
3427       
3428             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3429             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3430             var w = Math.max(
3431                     Math.min(opt.width || cw , this.maxWidth), 
3432                     Math.max(opt.minWidth || this.minWidth, bwidth)
3433             );
3434             if(opt.prompt){
3435                 activeTextEl.setWidth(w);
3436             }
3437             if(dlg.isVisible()){
3438                 dlg.fixedcenter = false;
3439             }
3440             // to big, make it scroll. = But as usual stupid IE does not support
3441             // !important..
3442             
3443             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3444                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3445                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3446             } else {
3447                 bodyEl.dom.style.height = '';
3448                 bodyEl.dom.style.overflowY = '';
3449             }
3450             if (cw > w) {
3451                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3452             } else {
3453                 bodyEl.dom.style.overflowX = '';
3454             }
3455             
3456             dlg.setContentSize(w, bodyEl.getHeight());
3457             if(dlg.isVisible()){
3458                 dlg.fixedcenter = true;
3459             }
3460             return this;
3461         },
3462
3463         /**
3464          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3465          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3466          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3467          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3468          * @return {Roo.MessageBox} This message box
3469          */
3470         updateProgress : function(value, text){
3471             if(text){
3472                 this.updateText(text);
3473             }
3474             
3475             if (pp) { // weird bug on my firefox - for some reason this is not defined
3476                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3477                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3478             }
3479             return this;
3480         },        
3481
3482         /**
3483          * Returns true if the message box is currently displayed
3484          * @return {Boolean} True if the message box is visible, else false
3485          */
3486         isVisible : function(){
3487             return dlg && dlg.isVisible();  
3488         },
3489
3490         /**
3491          * Hides the message box if it is displayed
3492          */
3493         hide : function(){
3494             if(this.isVisible()){
3495                 dlg.hide();
3496             }  
3497         },
3498
3499         /**
3500          * Displays a new message box, or reinitializes an existing message box, based on the config options
3501          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3502          * The following config object properties are supported:
3503          * <pre>
3504 Property    Type             Description
3505 ----------  ---------------  ------------------------------------------------------------------------------------
3506 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3507                                    closes (defaults to undefined)
3508 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3509                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3510 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3511                                    progress and wait dialogs will ignore this property and always hide the
3512                                    close button as they can only be closed programmatically.
3513 cls               String           A custom CSS class to apply to the message box element
3514 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3515                                    displayed (defaults to 75)
3516 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3517                                    function will be btn (the name of the button that was clicked, if applicable,
3518                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3519                                    Progress and wait dialogs will ignore this option since they do not respond to
3520                                    user actions and can only be closed programmatically, so any required function
3521                                    should be called by the same code after it closes the dialog.
3522 icon              String           A CSS class that provides a background image to be used as an icon for
3523                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3524 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3525 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3526 modal             Boolean          False to allow user interaction with the page while the message box is
3527                                    displayed (defaults to true)
3528 msg               String           A string that will replace the existing message box body text (defaults
3529                                    to the XHTML-compliant non-breaking space character '&#160;')
3530 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3531 progress          Boolean          True to display a progress bar (defaults to false)
3532 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3533 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3534 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3535 title             String           The title text
3536 value             String           The string value to set into the active textbox element if displayed
3537 wait              Boolean          True to display a progress bar (defaults to false)
3538 width             Number           The width of the dialog in pixels
3539 </pre>
3540          *
3541          * Example usage:
3542          * <pre><code>
3543 Roo.Msg.show({
3544    title: 'Address',
3545    msg: 'Please enter your address:',
3546    width: 300,
3547    buttons: Roo.MessageBox.OKCANCEL,
3548    multiline: true,
3549    fn: saveAddress,
3550    animEl: 'addAddressBtn'
3551 });
3552 </code></pre>
3553          * @param {Object} config Configuration options
3554          * @return {Roo.MessageBox} This message box
3555          */
3556         show : function(options)
3557         {
3558             
3559             // this causes nightmares if you show one dialog after another
3560             // especially on callbacks..
3561              
3562             if(this.isVisible()){
3563                 
3564                 this.hide();
3565                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3566                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3567                 Roo.log("New Dialog Message:" +  options.msg )
3568                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3569                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3570                 
3571             }
3572             var d = this.getDialog();
3573             opt = options;
3574             d.setTitle(opt.title || "&#160;");
3575             d.closeEl.setDisplayed(opt.closable !== false);
3576             activeTextEl = textboxEl;
3577             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3578             if(opt.prompt){
3579                 if(opt.multiline){
3580                     textboxEl.hide();
3581                     textareaEl.show();
3582                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3583                         opt.multiline : this.defaultTextHeight);
3584                     activeTextEl = textareaEl;
3585                 }else{
3586                     textboxEl.show();
3587                     textareaEl.hide();
3588                 }
3589             }else{
3590                 textboxEl.hide();
3591                 textareaEl.hide();
3592             }
3593             progressEl.setDisplayed(opt.progress === true);
3594             this.updateProgress(0);
3595             activeTextEl.dom.value = opt.value || "";
3596             if(opt.prompt){
3597                 dlg.setDefaultButton(activeTextEl);
3598             }else{
3599                 var bs = opt.buttons;
3600                 var db = null;
3601                 if(bs && bs.ok){
3602                     db = buttons["ok"];
3603                 }else if(bs && bs.yes){
3604                     db = buttons["yes"];
3605                 }
3606                 dlg.setDefaultButton(db);
3607             }
3608             bwidth = updateButtons(opt.buttons);
3609             this.updateText(opt.msg);
3610             if(opt.cls){
3611                 d.el.addClass(opt.cls);
3612             }
3613             d.proxyDrag = opt.proxyDrag === true;
3614             d.modal = opt.modal !== false;
3615             d.mask = opt.modal !== false ? mask : false;
3616             if(!d.isVisible()){
3617                 // force it to the end of the z-index stack so it gets a cursor in FF
3618                 document.body.appendChild(dlg.el.dom);
3619                 d.animateTarget = null;
3620                 d.show(options.animEl);
3621             }
3622             return this;
3623         },
3624
3625         /**
3626          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3627          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3628          * and closing the message box when the process is complete.
3629          * @param {String} title The title bar text
3630          * @param {String} msg The message box body text
3631          * @return {Roo.MessageBox} This message box
3632          */
3633         progress : function(title, msg){
3634             this.show({
3635                 title : title,
3636                 msg : msg,
3637                 buttons: false,
3638                 progress:true,
3639                 closable:false,
3640                 minWidth: this.minProgressWidth,
3641                 modal : true
3642             });
3643             return this;
3644         },
3645
3646         /**
3647          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3648          * If a callback function is passed it will be called after the user clicks the button, and the
3649          * id of the button that was clicked will be passed as the only parameter to the callback
3650          * (could also be the top-right close button).
3651          * @param {String} title The title bar text
3652          * @param {String} msg The message box body text
3653          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3654          * @param {Object} scope (optional) The scope of the callback function
3655          * @return {Roo.MessageBox} This message box
3656          */
3657         alert : function(title, msg, fn, scope)
3658         {
3659             this.show({
3660                 title : title,
3661                 msg : msg,
3662                 buttons: this.OK,
3663                 fn: fn,
3664                 closable : false,
3665                 scope : scope,
3666                 modal : true
3667             });
3668             return this;
3669         },
3670
3671         /**
3672          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3673          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3674          * You are responsible for closing the message box when the process is complete.
3675          * @param {String} msg The message box body text
3676          * @param {String} title (optional) The title bar text
3677          * @return {Roo.MessageBox} This message box
3678          */
3679         wait : function(msg, title){
3680             this.show({
3681                 title : title,
3682                 msg : msg,
3683                 buttons: false,
3684                 closable:false,
3685                 progress:true,
3686                 modal:true,
3687                 width:300,
3688                 wait:true
3689             });
3690             waitTimer = Roo.TaskMgr.start({
3691                 run: function(i){
3692                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3693                 },
3694                 interval: 1000
3695             });
3696             return this;
3697         },
3698
3699         /**
3700          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3701          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3702          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3703          * @param {String} title The title bar text
3704          * @param {String} msg The message box body text
3705          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3706          * @param {Object} scope (optional) The scope of the callback function
3707          * @return {Roo.MessageBox} This message box
3708          */
3709         confirm : function(title, msg, fn, scope){
3710             this.show({
3711                 title : title,
3712                 msg : msg,
3713                 buttons: this.YESNO,
3714                 fn: fn,
3715                 scope : scope,
3716                 modal : true
3717             });
3718             return this;
3719         },
3720
3721         /**
3722          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3723          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3724          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3725          * (could also be the top-right close button) and the text that was entered will be passed as the two
3726          * parameters to the callback.
3727          * @param {String} title The title bar text
3728          * @param {String} msg The message box body text
3729          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3730          * @param {Object} scope (optional) The scope of the callback function
3731          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3732          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3733          * @return {Roo.MessageBox} This message box
3734          */
3735         prompt : function(title, msg, fn, scope, multiline){
3736             this.show({
3737                 title : title,
3738                 msg : msg,
3739                 buttons: this.OKCANCEL,
3740                 fn: fn,
3741                 minWidth:250,
3742                 scope : scope,
3743                 prompt:true,
3744                 multiline: multiline,
3745                 modal : true
3746             });
3747             return this;
3748         },
3749
3750         /**
3751          * Button config that displays a single OK button
3752          * @type Object
3753          */
3754         OK : {ok:true},
3755         /**
3756          * Button config that displays Yes and No buttons
3757          * @type Object
3758          */
3759         YESNO : {yes:true, no:true},
3760         /**
3761          * Button config that displays OK and Cancel buttons
3762          * @type Object
3763          */
3764         OKCANCEL : {ok:true, cancel:true},
3765         /**
3766          * Button config that displays Yes, No and Cancel buttons
3767          * @type Object
3768          */
3769         YESNOCANCEL : {yes:true, no:true, cancel:true},
3770
3771         /**
3772          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3773          * @type Number
3774          */
3775         defaultTextHeight : 75,
3776         /**
3777          * The maximum width in pixels of the message box (defaults to 600)
3778          * @type Number
3779          */
3780         maxWidth : 600,
3781         /**
3782          * The minimum width in pixels of the message box (defaults to 100)
3783          * @type Number
3784          */
3785         minWidth : 100,
3786         /**
3787          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3788          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3789          * @type Number
3790          */
3791         minProgressWidth : 250,
3792         /**
3793          * An object containing the default button text strings that can be overriden for localized language support.
3794          * Supported properties are: ok, cancel, yes and no.
3795          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3796          * @type Object
3797          */
3798         buttonText : {
3799             ok : "OK",
3800             cancel : "Cancel",
3801             yes : "Yes",
3802             no : "No"
3803         }
3804     };
3805 }();
3806
3807 /**
3808  * Shorthand for {@link Roo.MessageBox}
3809  */
3810 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3811 Roo.Msg = Roo.Msg || Roo.MessageBox;
3812 /*
3813  * - LGPL
3814  *
3815  * navbar
3816  * 
3817  */
3818
3819 /**
3820  * @class Roo.bootstrap.Navbar
3821  * @extends Roo.bootstrap.Component
3822  * Bootstrap Navbar class
3823
3824  * @constructor
3825  * Create a new Navbar
3826  * @param {Object} config The config object
3827  */
3828
3829
3830 Roo.bootstrap.Navbar = function(config){
3831     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3832     this.addEvents({
3833         // raw events
3834         /**
3835          * @event beforetoggle
3836          * Fire before toggle the menu
3837          * @param {Roo.EventObject} e
3838          */
3839         "beforetoggle" : true
3840     });
3841 };
3842
3843 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3844     
3845     
3846    
3847     // private
3848     navItems : false,
3849     loadMask : false,
3850     
3851     
3852     getAutoCreate : function(){
3853         
3854         
3855         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3856         
3857     },
3858     
3859     initEvents :function ()
3860     {
3861         //Roo.log(this.el.select('.navbar-toggle',true));
3862         this.el.select('.navbar-toggle',true).on('click', function() {
3863             if(this.fireEvent('beforetoggle', this) !== false){
3864                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3865             }
3866             
3867         }, this);
3868         
3869         var mark = {
3870             tag: "div",
3871             cls:"x-dlg-mask"
3872         };
3873         
3874         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3875         
3876         var size = this.el.getSize();
3877         this.maskEl.setSize(size.width, size.height);
3878         this.maskEl.enableDisplayMode("block");
3879         this.maskEl.hide();
3880         
3881         if(this.loadMask){
3882             this.maskEl.show();
3883         }
3884     },
3885     
3886     
3887     getChildContainer : function()
3888     {
3889         if (this.el.select('.collapse').getCount()) {
3890             return this.el.select('.collapse',true).first();
3891         }
3892         
3893         return this.el;
3894     },
3895     
3896     mask : function()
3897     {
3898         this.maskEl.show();
3899     },
3900     
3901     unmask : function()
3902     {
3903         this.maskEl.hide();
3904     } 
3905     
3906     
3907     
3908     
3909 });
3910
3911
3912
3913  
3914
3915  /*
3916  * - LGPL
3917  *
3918  * navbar
3919  * 
3920  */
3921
3922 /**
3923  * @class Roo.bootstrap.NavSimplebar
3924  * @extends Roo.bootstrap.Navbar
3925  * Bootstrap Sidebar class
3926  *
3927  * @cfg {Boolean} inverse is inverted color
3928  * 
3929  * @cfg {String} type (nav | pills | tabs)
3930  * @cfg {Boolean} arrangement stacked | justified
3931  * @cfg {String} align (left | right) alignment
3932  * 
3933  * @cfg {Boolean} main (true|false) main nav bar? default false
3934  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3935  * 
3936  * @cfg {String} tag (header|footer|nav|div) default is nav 
3937
3938  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3939  * 
3940  * 
3941  * @constructor
3942  * Create a new Sidebar
3943  * @param {Object} config The config object
3944  */
3945
3946
3947 Roo.bootstrap.NavSimplebar = function(config){
3948     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3949 };
3950
3951 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3952     
3953     inverse: false,
3954     
3955     type: false,
3956     arrangement: '',
3957     align : false,
3958     
3959     weight : 'light',
3960     
3961     main : false,
3962     
3963     
3964     tag : false,
3965     
3966     
3967     getAutoCreate : function(){
3968         
3969         
3970         var cfg = {
3971             tag : this.tag || 'div',
3972             cls : 'navbar navbar-expand-lg'
3973         };
3974         if (['light','white'].indexOf(this.weight) > -1) {
3975             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
3976         }
3977         cfg.cls += ' bg-' + this.weight;
3978         
3979           
3980         
3981         cfg.cn = [
3982             {
3983                 cls: 'nav',
3984                 tag : 'ul'
3985             }
3986         ];
3987         
3988          
3989         this.type = this.type || 'nav';
3990         if (['tabs','pills'].indexOf(this.type)!==-1) {
3991             cfg.cn[0].cls += ' nav-' + this.type
3992         
3993         
3994         } else {
3995             if (this.type!=='nav') {
3996                 Roo.log('nav type must be nav/tabs/pills')
3997             }
3998             cfg.cn[0].cls += ' navbar-nav'
3999         }
4000         
4001         
4002         
4003         
4004         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4005             cfg.cn[0].cls += ' nav-' + this.arrangement;
4006         }
4007         
4008         
4009         if (this.align === 'right') {
4010             cfg.cn[0].cls += ' navbar-right';
4011         }
4012         
4013         if (this.inverse) {
4014             cfg.cls += ' navbar-inverse';
4015             
4016         }
4017         
4018         
4019         return cfg;
4020     
4021         
4022     }
4023     
4024     
4025     
4026 });
4027
4028
4029
4030  
4031
4032  
4033        /*
4034  * - LGPL
4035  *
4036  * navbar
4037  * navbar-fixed-top
4038  * navbar-expand-md  fixed-top 
4039  */
4040
4041 /**
4042  * @class Roo.bootstrap.NavHeaderbar
4043  * @extends Roo.bootstrap.NavSimplebar
4044  * Bootstrap Sidebar class
4045  *
4046  * @cfg {String} brand what is brand
4047  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4048  * @cfg {String} brand_href href of the brand
4049  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4050  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4051  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4052  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4053  * 
4054  * @constructor
4055  * Create a new Sidebar
4056  * @param {Object} config The config object
4057  */
4058
4059
4060 Roo.bootstrap.NavHeaderbar = function(config){
4061     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4062       
4063 };
4064
4065 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4066     
4067     position: '',
4068     brand: '',
4069     brand_href: false,
4070     srButton : true,
4071     autohide : false,
4072     desktopCenter : false,
4073    
4074     
4075     getAutoCreate : function(){
4076         
4077         var   cfg = {
4078             tag: this.nav || 'nav',
4079             cls: 'navbar navbar-expand-md',
4080             role: 'navigation',
4081             cn: []
4082         };
4083         
4084         var cn = cfg.cn;
4085         if (this.desktopCenter) {
4086             cn.push({cls : 'container', cn : []});
4087             cn = cn[0].cn;
4088         }
4089         
4090         if(this.srButton){
4091             cn.push({
4092                 tag: 'div',
4093                 cls: 'navbar-header',
4094                 cn: [
4095                     {
4096                         tag: 'button',
4097                         type: 'button',
4098                         cls: 'navbar-toggle navbar-toggler',
4099                         'data-toggle': 'collapse',
4100                         cn: [
4101                             {
4102                                 tag: 'span',
4103                                 cls: 'sr-only',
4104                                 html: 'Toggle navigation'
4105                             },
4106                             {
4107                                 tag: 'span',
4108                                 cls: 'icon-bar navbar-toggler-icon'
4109                             },
4110                             {
4111                                 tag: 'span',
4112                                 cls: 'icon-bar'
4113                             },
4114                             {
4115                                 tag: 'span',
4116                                 cls: 'icon-bar'
4117                             }
4118                         ]
4119                     }
4120                 ]
4121             });
4122         }
4123         
4124         cn.push({
4125             tag: 'div',
4126             cls: 'collapse navbar-collapse',
4127             cn : []
4128         });
4129         
4130         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4131         
4132         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4133             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4134             
4135             // tag can override this..
4136             
4137             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4138         }
4139         
4140         if (this.brand !== '') {
4141             cn[0].cn.push({
4142                 tag: 'a',
4143                 href: this.brand_href ? this.brand_href : '#',
4144                 cls: 'navbar-brand',
4145                 cn: [
4146                 this.brand
4147                 ]
4148             });
4149         }
4150         
4151         if(this.main){
4152             cfg.cls += ' main-nav';
4153         }
4154         
4155         
4156         return cfg;
4157
4158         
4159     },
4160     getHeaderChildContainer : function()
4161     {
4162         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4163             return this.el.select('.navbar-header',true).first();
4164         }
4165         
4166         return this.getChildContainer();
4167     },
4168     
4169     
4170     initEvents : function()
4171     {
4172         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4173         
4174         if (this.autohide) {
4175             
4176             var prevScroll = 0;
4177             var ft = this.el;
4178             
4179             Roo.get(document).on('scroll',function(e) {
4180                 var ns = Roo.get(document).getScroll().top;
4181                 var os = prevScroll;
4182                 prevScroll = ns;
4183                 
4184                 if(ns > os){
4185                     ft.removeClass('slideDown');
4186                     ft.addClass('slideUp');
4187                     return;
4188                 }
4189                 ft.removeClass('slideUp');
4190                 ft.addClass('slideDown');
4191                  
4192               
4193           },this);
4194         }
4195     }    
4196     
4197 });
4198
4199
4200
4201  
4202
4203  /*
4204  * - LGPL
4205  *
4206  * navbar
4207  * 
4208  */
4209
4210 /**
4211  * @class Roo.bootstrap.NavSidebar
4212  * @extends Roo.bootstrap.Navbar
4213  * Bootstrap Sidebar class
4214  * 
4215  * @constructor
4216  * Create a new Sidebar
4217  * @param {Object} config The config object
4218  */
4219
4220
4221 Roo.bootstrap.NavSidebar = function(config){
4222     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4223 };
4224
4225 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4226     
4227     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4228     
4229     getAutoCreate : function(){
4230         
4231         
4232         return  {
4233             tag: 'div',
4234             cls: 'sidebar sidebar-nav'
4235         };
4236     
4237         
4238     }
4239     
4240     
4241     
4242 });
4243
4244
4245
4246  
4247
4248  /*
4249  * - LGPL
4250  *
4251  * nav group
4252  * 
4253  */
4254
4255 /**
4256  * @class Roo.bootstrap.NavGroup
4257  * @extends Roo.bootstrap.Component
4258  * Bootstrap NavGroup class
4259  * @cfg {String} align (left|right)
4260  * @cfg {Boolean} inverse
4261  * @cfg {String} type (nav|pills|tab) default nav
4262  * @cfg {String} navId - reference Id for navbar.
4263
4264  * 
4265  * @constructor
4266  * Create a new nav group
4267  * @param {Object} config The config object
4268  */
4269
4270 Roo.bootstrap.NavGroup = function(config){
4271     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4272     this.navItems = [];
4273    
4274     Roo.bootstrap.NavGroup.register(this);
4275      this.addEvents({
4276         /**
4277              * @event changed
4278              * Fires when the active item changes
4279              * @param {Roo.bootstrap.NavGroup} this
4280              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4281              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4282          */
4283         'changed': true
4284      });
4285     
4286 };
4287
4288 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4289     
4290     align: '',
4291     inverse: false,
4292     form: false,
4293     type: 'nav',
4294     navId : '',
4295     // private
4296     
4297     navItems : false, 
4298     
4299     getAutoCreate : function()
4300     {
4301         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4302         
4303         cfg = {
4304             tag : 'ul',
4305             cls: 'nav' 
4306         };
4307         
4308         if (['tabs','pills'].indexOf(this.type)!==-1) {
4309             cfg.cls += ' nav-' + this.type
4310         } else {
4311             if (this.type!=='nav') {
4312                 Roo.log('nav type must be nav/tabs/pills')
4313             }
4314             cfg.cls += ' navbar-nav'
4315         }
4316         
4317         if (this.parent() && this.parent().sidebar) {
4318             cfg = {
4319                 tag: 'ul',
4320                 cls: 'dashboard-menu sidebar-menu'
4321             };
4322             
4323             return cfg;
4324         }
4325         
4326         if (this.form === true) {
4327             cfg = {
4328                 tag: 'form',
4329                 cls: 'navbar-form'
4330             };
4331             
4332             if (this.align === 'right') {
4333                 cfg.cls += ' navbar-right ml-md-auto';
4334             } else {
4335                 cfg.cls += ' navbar-left';
4336             }
4337         }
4338         
4339         if (this.align === 'right') {
4340             cfg.cls += ' navbar-right ml-md-auto';
4341         } else {
4342             cfg.cls += ' mr-auto';
4343         }
4344         
4345         if (this.inverse) {
4346             cfg.cls += ' navbar-inverse';
4347             
4348         }
4349         
4350         
4351         return cfg;
4352     },
4353     /**
4354     * sets the active Navigation item
4355     * @param {Roo.bootstrap.NavItem} the new current navitem
4356     */
4357     setActiveItem : function(item)
4358     {
4359         var prev = false;
4360         Roo.each(this.navItems, function(v){
4361             if (v == item) {
4362                 return ;
4363             }
4364             if (v.isActive()) {
4365                 v.setActive(false, true);
4366                 prev = v;
4367                 
4368             }
4369             
4370         });
4371
4372         item.setActive(true, true);
4373         this.fireEvent('changed', this, item, prev);
4374         
4375         
4376     },
4377     /**
4378     * gets the active Navigation item
4379     * @return {Roo.bootstrap.NavItem} the current navitem
4380     */
4381     getActive : function()
4382     {
4383         
4384         var prev = false;
4385         Roo.each(this.navItems, function(v){
4386             
4387             if (v.isActive()) {
4388                 prev = v;
4389                 
4390             }
4391             
4392         });
4393         return prev;
4394     },
4395     
4396     indexOfNav : function()
4397     {
4398         
4399         var prev = false;
4400         Roo.each(this.navItems, function(v,i){
4401             
4402             if (v.isActive()) {
4403                 prev = i;
4404                 
4405             }
4406             
4407         });
4408         return prev;
4409     },
4410     /**
4411     * adds a Navigation item
4412     * @param {Roo.bootstrap.NavItem} the navitem to add
4413     */
4414     addItem : function(cfg)
4415     {
4416         var cn = new Roo.bootstrap.NavItem(cfg);
4417         this.register(cn);
4418         cn.parentId = this.id;
4419         cn.onRender(this.el, null);
4420         return cn;
4421     },
4422     /**
4423     * register a Navigation item
4424     * @param {Roo.bootstrap.NavItem} the navitem to add
4425     */
4426     register : function(item)
4427     {
4428         this.navItems.push( item);
4429         item.navId = this.navId;
4430     
4431     },
4432     
4433     /**
4434     * clear all the Navigation item
4435     */
4436    
4437     clearAll : function()
4438     {
4439         this.navItems = [];
4440         this.el.dom.innerHTML = '';
4441     },
4442     
4443     getNavItem: function(tabId)
4444     {
4445         var ret = false;
4446         Roo.each(this.navItems, function(e) {
4447             if (e.tabId == tabId) {
4448                ret =  e;
4449                return false;
4450             }
4451             return true;
4452             
4453         });
4454         return ret;
4455     },
4456     
4457     setActiveNext : function()
4458     {
4459         var i = this.indexOfNav(this.getActive());
4460         if (i > this.navItems.length) {
4461             return;
4462         }
4463         this.setActiveItem(this.navItems[i+1]);
4464     },
4465     setActivePrev : function()
4466     {
4467         var i = this.indexOfNav(this.getActive());
4468         if (i  < 1) {
4469             return;
4470         }
4471         this.setActiveItem(this.navItems[i-1]);
4472     },
4473     clearWasActive : function(except) {
4474         Roo.each(this.navItems, function(e) {
4475             if (e.tabId != except.tabId && e.was_active) {
4476                e.was_active = false;
4477                return false;
4478             }
4479             return true;
4480             
4481         });
4482     },
4483     getWasActive : function ()
4484     {
4485         var r = false;
4486         Roo.each(this.navItems, function(e) {
4487             if (e.was_active) {
4488                r = e;
4489                return false;
4490             }
4491             return true;
4492             
4493         });
4494         return r;
4495     }
4496     
4497     
4498 });
4499
4500  
4501 Roo.apply(Roo.bootstrap.NavGroup, {
4502     
4503     groups: {},
4504      /**
4505     * register a Navigation Group
4506     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4507     */
4508     register : function(navgrp)
4509     {
4510         this.groups[navgrp.navId] = navgrp;
4511         
4512     },
4513     /**
4514     * fetch a Navigation Group based on the navigation ID
4515     * @param {string} the navgroup to add
4516     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4517     */
4518     get: function(navId) {
4519         if (typeof(this.groups[navId]) == 'undefined') {
4520             return false;
4521             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4522         }
4523         return this.groups[navId] ;
4524     }
4525     
4526     
4527     
4528 });
4529
4530  /*
4531  * - LGPL
4532  *
4533  * row
4534  * 
4535  */
4536
4537 /**
4538  * @class Roo.bootstrap.NavItem
4539  * @extends Roo.bootstrap.Component
4540  * Bootstrap Navbar.NavItem class
4541  * @cfg {String} href  link to
4542  * @cfg {String} html content of button
4543  * @cfg {String} badge text inside badge
4544  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4545  * @cfg {String} glyphicon name of glyphicon
4546  * @cfg {String} icon name of font awesome icon
4547  * @cfg {Boolean} active Is item active
4548  * @cfg {Boolean} disabled Is item disabled
4549  
4550  * @cfg {Boolean} preventDefault (true | false) default false
4551  * @cfg {String} tabId the tab that this item activates.
4552  * @cfg {String} tagtype (a|span) render as a href or span?
4553  * @cfg {Boolean} animateRef (true|false) link to element default false  
4554   
4555  * @constructor
4556  * Create a new Navbar Item
4557  * @param {Object} config The config object
4558  */
4559 Roo.bootstrap.NavItem = function(config){
4560     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4561     this.addEvents({
4562         // raw events
4563         /**
4564          * @event click
4565          * The raw click event for the entire grid.
4566          * @param {Roo.EventObject} e
4567          */
4568         "click" : true,
4569          /**
4570             * @event changed
4571             * Fires when the active item active state changes
4572             * @param {Roo.bootstrap.NavItem} this
4573             * @param {boolean} state the new state
4574              
4575          */
4576         'changed': true,
4577         /**
4578             * @event scrollto
4579             * Fires when scroll to element
4580             * @param {Roo.bootstrap.NavItem} this
4581             * @param {Object} options
4582             * @param {Roo.EventObject} e
4583              
4584          */
4585         'scrollto': true
4586     });
4587    
4588 };
4589
4590 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4591     
4592     href: false,
4593     html: '',
4594     badge: '',
4595     icon: false,
4596     glyphicon: false,
4597     active: false,
4598     preventDefault : false,
4599     tabId : false,
4600     tagtype : 'a',
4601     disabled : false,
4602     animateRef : false,
4603     was_active : false,
4604     
4605     getAutoCreate : function(){
4606          
4607         var cfg = {
4608             tag: 'li',
4609             cls: 'nav-item'
4610             
4611         };
4612         
4613         if (this.active) {
4614             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4615         }
4616         if (this.disabled) {
4617             cfg.cls += ' disabled';
4618         }
4619         
4620         if (this.href || this.html || this.glyphicon || this.icon) {
4621             cfg.cn = [
4622                 {
4623                     tag: this.tagtype,
4624                     href : this.href || "#",
4625                     html: this.html || ''
4626                 }
4627             ];
4628             if (this.tagtype == 'a') {
4629                 cfg.cn[0].cls = 'nav-link';
4630             }
4631             if (this.icon) {
4632                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4633             }
4634
4635             if(this.glyphicon) {
4636                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4637             }
4638             
4639             if (this.menu) {
4640                 
4641                 cfg.cn[0].html += " <span class='caret'></span>";
4642              
4643             }
4644             
4645             if (this.badge !== '') {
4646                  
4647                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4648             }
4649         }
4650         
4651         
4652         
4653         return cfg;
4654     },
4655     initEvents: function() 
4656     {
4657         if (typeof (this.menu) != 'undefined') {
4658             this.menu.parentType = this.xtype;
4659             this.menu.triggerEl = this.el;
4660             this.menu = this.addxtype(Roo.apply({}, this.menu));
4661         }
4662         
4663         this.el.select('a',true).on('click', this.onClick, this);
4664         
4665         if(this.tagtype == 'span'){
4666             this.el.select('span',true).on('click', this.onClick, this);
4667         }
4668        
4669         // at this point parent should be available..
4670         this.parent().register(this);
4671     },
4672     
4673     onClick : function(e)
4674     {
4675         if (e.getTarget('.dropdown-menu-item')) {
4676             // did you click on a menu itemm.... - then don't trigger onclick..
4677             return;
4678         }
4679         
4680         if(
4681                 this.preventDefault || 
4682                 this.href == '#' 
4683         ){
4684             Roo.log("NavItem - prevent Default?");
4685             e.preventDefault();
4686         }
4687         
4688         if (this.disabled) {
4689             return;
4690         }
4691         
4692         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4693         if (tg && tg.transition) {
4694             Roo.log("waiting for the transitionend");
4695             return;
4696         }
4697         
4698         
4699         
4700         //Roo.log("fire event clicked");
4701         if(this.fireEvent('click', this, e) === false){
4702             return;
4703         };
4704         
4705         if(this.tagtype == 'span'){
4706             return;
4707         }
4708         
4709         //Roo.log(this.href);
4710         var ael = this.el.select('a',true).first();
4711         //Roo.log(ael);
4712         
4713         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4714             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4715             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4716                 return; // ignore... - it's a 'hash' to another page.
4717             }
4718             Roo.log("NavItem - prevent Default?");
4719             e.preventDefault();
4720             this.scrollToElement(e);
4721         }
4722         
4723         
4724         var p =  this.parent();
4725    
4726         if (['tabs','pills'].indexOf(p.type)!==-1) {
4727             if (typeof(p.setActiveItem) !== 'undefined') {
4728                 p.setActiveItem(this);
4729             }
4730         }
4731         
4732         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4733         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4734             // remove the collapsed menu expand...
4735             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4736         }
4737     },
4738     
4739     isActive: function () {
4740         return this.active
4741     },
4742     setActive : function(state, fire, is_was_active)
4743     {
4744         if (this.active && !state && this.navId) {
4745             this.was_active = true;
4746             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4747             if (nv) {
4748                 nv.clearWasActive(this);
4749             }
4750             
4751         }
4752         this.active = state;
4753         
4754         if (!state ) {
4755             this.el.removeClass('active');
4756         } else if (!this.el.hasClass('active')) {
4757             this.el.addClass('active');
4758         }
4759         if (fire) {
4760             this.fireEvent('changed', this, state);
4761         }
4762         
4763         // show a panel if it's registered and related..
4764         
4765         if (!this.navId || !this.tabId || !state || is_was_active) {
4766             return;
4767         }
4768         
4769         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4770         if (!tg) {
4771             return;
4772         }
4773         var pan = tg.getPanelByName(this.tabId);
4774         if (!pan) {
4775             return;
4776         }
4777         // if we can not flip to new panel - go back to old nav highlight..
4778         if (false == tg.showPanel(pan)) {
4779             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4780             if (nv) {
4781                 var onav = nv.getWasActive();
4782                 if (onav) {
4783                     onav.setActive(true, false, true);
4784                 }
4785             }
4786             
4787         }
4788         
4789         
4790         
4791     },
4792      // this should not be here...
4793     setDisabled : function(state)
4794     {
4795         this.disabled = state;
4796         if (!state ) {
4797             this.el.removeClass('disabled');
4798         } else if (!this.el.hasClass('disabled')) {
4799             this.el.addClass('disabled');
4800         }
4801         
4802     },
4803     
4804     /**
4805      * Fetch the element to display the tooltip on.
4806      * @return {Roo.Element} defaults to this.el
4807      */
4808     tooltipEl : function()
4809     {
4810         return this.el.select('' + this.tagtype + '', true).first();
4811     },
4812     
4813     scrollToElement : function(e)
4814     {
4815         var c = document.body;
4816         
4817         /*
4818          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4819          */
4820         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4821             c = document.documentElement;
4822         }
4823         
4824         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4825         
4826         if(!target){
4827             return;
4828         }
4829
4830         var o = target.calcOffsetsTo(c);
4831         
4832         var options = {
4833             target : target,
4834             value : o[1]
4835         };
4836         
4837         this.fireEvent('scrollto', this, options, e);
4838         
4839         Roo.get(c).scrollTo('top', options.value, true);
4840         
4841         return;
4842     }
4843 });
4844  
4845
4846  /*
4847  * - LGPL
4848  *
4849  * sidebar item
4850  *
4851  *  li
4852  *    <span> icon </span>
4853  *    <span> text </span>
4854  *    <span>badge </span>
4855  */
4856
4857 /**
4858  * @class Roo.bootstrap.NavSidebarItem
4859  * @extends Roo.bootstrap.NavItem
4860  * Bootstrap Navbar.NavSidebarItem class
4861  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4862  * {Boolean} open is the menu open
4863  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4864  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4865  * {String} buttonSize (sm|md|lg)the extra classes for the button
4866  * {Boolean} showArrow show arrow next to the text (default true)
4867  * @constructor
4868  * Create a new Navbar Button
4869  * @param {Object} config The config object
4870  */
4871 Roo.bootstrap.NavSidebarItem = function(config){
4872     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4873     this.addEvents({
4874         // raw events
4875         /**
4876          * @event click
4877          * The raw click event for the entire grid.
4878          * @param {Roo.EventObject} e
4879          */
4880         "click" : true,
4881          /**
4882             * @event changed
4883             * Fires when the active item active state changes
4884             * @param {Roo.bootstrap.NavSidebarItem} this
4885             * @param {boolean} state the new state
4886              
4887          */
4888         'changed': true
4889     });
4890    
4891 };
4892
4893 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4894     
4895     badgeWeight : 'default',
4896     
4897     open: false,
4898     
4899     buttonView : false,
4900     
4901     buttonWeight : 'default',
4902     
4903     buttonSize : 'md',
4904     
4905     showArrow : true,
4906     
4907     getAutoCreate : function(){
4908         
4909         
4910         var a = {
4911                 tag: 'a',
4912                 href : this.href || '#',
4913                 cls: '',
4914                 html : '',
4915                 cn : []
4916         };
4917         
4918         if(this.buttonView){
4919             a = {
4920                 tag: 'button',
4921                 href : this.href || '#',
4922                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4923                 html : this.html,
4924                 cn : []
4925             };
4926         }
4927         
4928         var cfg = {
4929             tag: 'li',
4930             cls: '',
4931             cn: [ a ]
4932         };
4933         
4934         if (this.active) {
4935             cfg.cls += ' active';
4936         }
4937         
4938         if (this.disabled) {
4939             cfg.cls += ' disabled';
4940         }
4941         if (this.open) {
4942             cfg.cls += ' open x-open';
4943         }
4944         // left icon..
4945         if (this.glyphicon || this.icon) {
4946             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4947             a.cn.push({ tag : 'i', cls : c }) ;
4948         }
4949         
4950         if(!this.buttonView){
4951             var span = {
4952                 tag: 'span',
4953                 html : this.html || ''
4954             };
4955
4956             a.cn.push(span);
4957             
4958         }
4959         
4960         if (this.badge !== '') {
4961             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4962         }
4963         
4964         if (this.menu) {
4965             
4966             if(this.showArrow){
4967                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4968             }
4969             
4970             a.cls += ' dropdown-toggle treeview' ;
4971         }
4972         
4973         return cfg;
4974     },
4975     
4976     initEvents : function()
4977     { 
4978         if (typeof (this.menu) != 'undefined') {
4979             this.menu.parentType = this.xtype;
4980             this.menu.triggerEl = this.el;
4981             this.menu = this.addxtype(Roo.apply({}, this.menu));
4982         }
4983         
4984         this.el.on('click', this.onClick, this);
4985         
4986         if(this.badge !== ''){
4987             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4988         }
4989         
4990     },
4991     
4992     onClick : function(e)
4993     {
4994         if(this.disabled){
4995             e.preventDefault();
4996             return;
4997         }
4998         
4999         if(this.preventDefault){
5000             e.preventDefault();
5001         }
5002         
5003         this.fireEvent('click', this);
5004     },
5005     
5006     disable : function()
5007     {
5008         this.setDisabled(true);
5009     },
5010     
5011     enable : function()
5012     {
5013         this.setDisabled(false);
5014     },
5015     
5016     setDisabled : function(state)
5017     {
5018         if(this.disabled == state){
5019             return;
5020         }
5021         
5022         this.disabled = state;
5023         
5024         if (state) {
5025             this.el.addClass('disabled');
5026             return;
5027         }
5028         
5029         this.el.removeClass('disabled');
5030         
5031         return;
5032     },
5033     
5034     setActive : function(state)
5035     {
5036         if(this.active == state){
5037             return;
5038         }
5039         
5040         this.active = state;
5041         
5042         if (state) {
5043             this.el.addClass('active');
5044             return;
5045         }
5046         
5047         this.el.removeClass('active');
5048         
5049         return;
5050     },
5051     
5052     isActive: function () 
5053     {
5054         return this.active;
5055     },
5056     
5057     setBadge : function(str)
5058     {
5059         if(!this.badgeEl){
5060             return;
5061         }
5062         
5063         this.badgeEl.dom.innerHTML = str;
5064     }
5065     
5066    
5067      
5068  
5069 });
5070  
5071
5072  /*
5073  * - LGPL
5074  *
5075  * row
5076  * 
5077  */
5078
5079 /**
5080  * @class Roo.bootstrap.Row
5081  * @extends Roo.bootstrap.Component
5082  * Bootstrap Row class (contains columns...)
5083  * 
5084  * @constructor
5085  * Create a new Row
5086  * @param {Object} config The config object
5087  */
5088
5089 Roo.bootstrap.Row = function(config){
5090     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5091 };
5092
5093 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5094     
5095     getAutoCreate : function(){
5096        return {
5097             cls: 'row clearfix'
5098        };
5099     }
5100     
5101     
5102 });
5103
5104  
5105
5106  /*
5107  * - LGPL
5108  *
5109  * element
5110  * 
5111  */
5112
5113 /**
5114  * @class Roo.bootstrap.Element
5115  * @extends Roo.bootstrap.Component
5116  * Bootstrap Element class
5117  * @cfg {String} html contents of the element
5118  * @cfg {String} tag tag of the element
5119  * @cfg {String} cls class of the element
5120  * @cfg {Boolean} preventDefault (true|false) default false
5121  * @cfg {Boolean} clickable (true|false) default false
5122  * 
5123  * @constructor
5124  * Create a new Element
5125  * @param {Object} config The config object
5126  */
5127
5128 Roo.bootstrap.Element = function(config){
5129     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5130     
5131     this.addEvents({
5132         // raw events
5133         /**
5134          * @event click
5135          * When a element is chick
5136          * @param {Roo.bootstrap.Element} this
5137          * @param {Roo.EventObject} e
5138          */
5139         "click" : true
5140     });
5141 };
5142
5143 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5144     
5145     tag: 'div',
5146     cls: '',
5147     html: '',
5148     preventDefault: false, 
5149     clickable: false,
5150     
5151     getAutoCreate : function(){
5152         
5153         var cfg = {
5154             tag: this.tag,
5155             // cls: this.cls, double assign in parent class Component.js :: onRender
5156             html: this.html
5157         };
5158         
5159         return cfg;
5160     },
5161     
5162     initEvents: function() 
5163     {
5164         Roo.bootstrap.Element.superclass.initEvents.call(this);
5165         
5166         if(this.clickable){
5167             this.el.on('click', this.onClick, this);
5168         }
5169         
5170     },
5171     
5172     onClick : function(e)
5173     {
5174         if(this.preventDefault){
5175             e.preventDefault();
5176         }
5177         
5178         this.fireEvent('click', this, e);
5179     },
5180     
5181     getValue : function()
5182     {
5183         return this.el.dom.innerHTML;
5184     },
5185     
5186     setValue : function(value)
5187     {
5188         this.el.dom.innerHTML = value;
5189     }
5190    
5191 });
5192
5193  
5194
5195  /*
5196  * - LGPL
5197  *
5198  * pagination
5199  * 
5200  */
5201
5202 /**
5203  * @class Roo.bootstrap.Pagination
5204  * @extends Roo.bootstrap.Component
5205  * Bootstrap Pagination class
5206  * @cfg {String} size xs | sm | md | lg
5207  * @cfg {Boolean} inverse false | true
5208  * 
5209  * @constructor
5210  * Create a new Pagination
5211  * @param {Object} config The config object
5212  */
5213
5214 Roo.bootstrap.Pagination = function(config){
5215     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5216 };
5217
5218 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5219     
5220     cls: false,
5221     size: false,
5222     inverse: false,
5223     
5224     getAutoCreate : function(){
5225         var cfg = {
5226             tag: 'ul',
5227                 cls: 'pagination'
5228         };
5229         if (this.inverse) {
5230             cfg.cls += ' inverse';
5231         }
5232         if (this.html) {
5233             cfg.html=this.html;
5234         }
5235         if (this.cls) {
5236             cfg.cls += " " + this.cls;
5237         }
5238         return cfg;
5239     }
5240    
5241 });
5242
5243  
5244
5245  /*
5246  * - LGPL
5247  *
5248  * Pagination item
5249  * 
5250  */
5251
5252
5253 /**
5254  * @class Roo.bootstrap.PaginationItem
5255  * @extends Roo.bootstrap.Component
5256  * Bootstrap PaginationItem class
5257  * @cfg {String} html text
5258  * @cfg {String} href the link
5259  * @cfg {Boolean} preventDefault (true | false) default true
5260  * @cfg {Boolean} active (true | false) default false
5261  * @cfg {Boolean} disabled default false
5262  * 
5263  * 
5264  * @constructor
5265  * Create a new PaginationItem
5266  * @param {Object} config The config object
5267  */
5268
5269
5270 Roo.bootstrap.PaginationItem = function(config){
5271     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5272     this.addEvents({
5273         // raw events
5274         /**
5275          * @event click
5276          * The raw click event for the entire grid.
5277          * @param {Roo.EventObject} e
5278          */
5279         "click" : true
5280     });
5281 };
5282
5283 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5284     
5285     href : false,
5286     html : false,
5287     preventDefault: true,
5288     active : false,
5289     cls : false,
5290     disabled: false,
5291     
5292     getAutoCreate : function(){
5293         var cfg= {
5294             tag: 'li',
5295             cn: [
5296                 {
5297                     tag : 'a',
5298                     href : this.href ? this.href : '#',
5299                     html : this.html ? this.html : ''
5300                 }
5301             ]
5302         };
5303         
5304         if(this.cls){
5305             cfg.cls = this.cls;
5306         }
5307         
5308         if(this.disabled){
5309             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5310         }
5311         
5312         if(this.active){
5313             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5314         }
5315         
5316         return cfg;
5317     },
5318     
5319     initEvents: function() {
5320         
5321         this.el.on('click', this.onClick, this);
5322         
5323     },
5324     onClick : function(e)
5325     {
5326         Roo.log('PaginationItem on click ');
5327         if(this.preventDefault){
5328             e.preventDefault();
5329         }
5330         
5331         if(this.disabled){
5332             return;
5333         }
5334         
5335         this.fireEvent('click', this, e);
5336     }
5337    
5338 });
5339
5340  
5341
5342  /*
5343  * - LGPL
5344  *
5345  * slider
5346  * 
5347  */
5348
5349
5350 /**
5351  * @class Roo.bootstrap.Slider
5352  * @extends Roo.bootstrap.Component
5353  * Bootstrap Slider class
5354  *    
5355  * @constructor
5356  * Create a new Slider
5357  * @param {Object} config The config object
5358  */
5359
5360 Roo.bootstrap.Slider = function(config){
5361     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5362 };
5363
5364 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5365     
5366     getAutoCreate : function(){
5367         
5368         var cfg = {
5369             tag: 'div',
5370             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5371             cn: [
5372                 {
5373                     tag: 'a',
5374                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5375                 }
5376             ]
5377         };
5378         
5379         return cfg;
5380     }
5381    
5382 });
5383
5384  /*
5385  * Based on:
5386  * Ext JS Library 1.1.1
5387  * Copyright(c) 2006-2007, Ext JS, LLC.
5388  *
5389  * Originally Released Under LGPL - original licence link has changed is not relivant.
5390  *
5391  * Fork - LGPL
5392  * <script type="text/javascript">
5393  */
5394  
5395
5396 /**
5397  * @class Roo.grid.ColumnModel
5398  * @extends Roo.util.Observable
5399  * This is the default implementation of a ColumnModel used by the Grid. It defines
5400  * the columns in the grid.
5401  * <br>Usage:<br>
5402  <pre><code>
5403  var colModel = new Roo.grid.ColumnModel([
5404         {header: "Ticker", width: 60, sortable: true, locked: true},
5405         {header: "Company Name", width: 150, sortable: true},
5406         {header: "Market Cap.", width: 100, sortable: true},
5407         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5408         {header: "Employees", width: 100, sortable: true, resizable: false}
5409  ]);
5410  </code></pre>
5411  * <p>
5412  
5413  * The config options listed for this class are options which may appear in each
5414  * individual column definition.
5415  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5416  * @constructor
5417  * @param {Object} config An Array of column config objects. See this class's
5418  * config objects for details.
5419 */
5420 Roo.grid.ColumnModel = function(config){
5421         /**
5422      * The config passed into the constructor
5423      */
5424     this.config = config;
5425     this.lookup = {};
5426
5427     // if no id, create one
5428     // if the column does not have a dataIndex mapping,
5429     // map it to the order it is in the config
5430     for(var i = 0, len = config.length; i < len; i++){
5431         var c = config[i];
5432         if(typeof c.dataIndex == "undefined"){
5433             c.dataIndex = i;
5434         }
5435         if(typeof c.renderer == "string"){
5436             c.renderer = Roo.util.Format[c.renderer];
5437         }
5438         if(typeof c.id == "undefined"){
5439             c.id = Roo.id();
5440         }
5441         if(c.editor && c.editor.xtype){
5442             c.editor  = Roo.factory(c.editor, Roo.grid);
5443         }
5444         if(c.editor && c.editor.isFormField){
5445             c.editor = new Roo.grid.GridEditor(c.editor);
5446         }
5447         this.lookup[c.id] = c;
5448     }
5449
5450     /**
5451      * The width of columns which have no width specified (defaults to 100)
5452      * @type Number
5453      */
5454     this.defaultWidth = 100;
5455
5456     /**
5457      * Default sortable of columns which have no sortable specified (defaults to false)
5458      * @type Boolean
5459      */
5460     this.defaultSortable = false;
5461
5462     this.addEvents({
5463         /**
5464              * @event widthchange
5465              * Fires when the width of a column changes.
5466              * @param {ColumnModel} this
5467              * @param {Number} columnIndex The column index
5468              * @param {Number} newWidth The new width
5469              */
5470             "widthchange": true,
5471         /**
5472              * @event headerchange
5473              * Fires when the text of a header changes.
5474              * @param {ColumnModel} this
5475              * @param {Number} columnIndex The column index
5476              * @param {Number} newText The new header text
5477              */
5478             "headerchange": true,
5479         /**
5480              * @event hiddenchange
5481              * Fires when a column is hidden or "unhidden".
5482              * @param {ColumnModel} this
5483              * @param {Number} columnIndex The column index
5484              * @param {Boolean} hidden true if hidden, false otherwise
5485              */
5486             "hiddenchange": true,
5487             /**
5488          * @event columnmoved
5489          * Fires when a column is moved.
5490          * @param {ColumnModel} this
5491          * @param {Number} oldIndex
5492          * @param {Number} newIndex
5493          */
5494         "columnmoved" : true,
5495         /**
5496          * @event columlockchange
5497          * Fires when a column's locked state is changed
5498          * @param {ColumnModel} this
5499          * @param {Number} colIndex
5500          * @param {Boolean} locked true if locked
5501          */
5502         "columnlockchange" : true
5503     });
5504     Roo.grid.ColumnModel.superclass.constructor.call(this);
5505 };
5506 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5507     /**
5508      * @cfg {String} header The header text to display in the Grid view.
5509      */
5510     /**
5511      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5512      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5513      * specified, the column's index is used as an index into the Record's data Array.
5514      */
5515     /**
5516      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5517      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5518      */
5519     /**
5520      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5521      * Defaults to the value of the {@link #defaultSortable} property.
5522      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5523      */
5524     /**
5525      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5526      */
5527     /**
5528      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5529      */
5530     /**
5531      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5532      */
5533     /**
5534      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5535      */
5536     /**
5537      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5538      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5539      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5540      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5541      */
5542        /**
5543      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5544      */
5545     /**
5546      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5547      */
5548     /**
5549      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5550      */
5551     /**
5552      * @cfg {String} cursor (Optional)
5553      */
5554     /**
5555      * @cfg {String} tooltip (Optional)
5556      */
5557     /**
5558      * @cfg {Number} xs (Optional)
5559      */
5560     /**
5561      * @cfg {Number} sm (Optional)
5562      */
5563     /**
5564      * @cfg {Number} md (Optional)
5565      */
5566     /**
5567      * @cfg {Number} lg (Optional)
5568      */
5569     /**
5570      * Returns the id of the column at the specified index.
5571      * @param {Number} index The column index
5572      * @return {String} the id
5573      */
5574     getColumnId : function(index){
5575         return this.config[index].id;
5576     },
5577
5578     /**
5579      * Returns the column for a specified id.
5580      * @param {String} id The column id
5581      * @return {Object} the column
5582      */
5583     getColumnById : function(id){
5584         return this.lookup[id];
5585     },
5586
5587     
5588     /**
5589      * Returns the column for a specified dataIndex.
5590      * @param {String} dataIndex The column dataIndex
5591      * @return {Object|Boolean} the column or false if not found
5592      */
5593     getColumnByDataIndex: function(dataIndex){
5594         var index = this.findColumnIndex(dataIndex);
5595         return index > -1 ? this.config[index] : false;
5596     },
5597     
5598     /**
5599      * Returns the index for a specified column id.
5600      * @param {String} id The column id
5601      * @return {Number} the index, or -1 if not found
5602      */
5603     getIndexById : function(id){
5604         for(var i = 0, len = this.config.length; i < len; i++){
5605             if(this.config[i].id == id){
5606                 return i;
5607             }
5608         }
5609         return -1;
5610     },
5611     
5612     /**
5613      * Returns the index for a specified column dataIndex.
5614      * @param {String} dataIndex The column dataIndex
5615      * @return {Number} the index, or -1 if not found
5616      */
5617     
5618     findColumnIndex : function(dataIndex){
5619         for(var i = 0, len = this.config.length; i < len; i++){
5620             if(this.config[i].dataIndex == dataIndex){
5621                 return i;
5622             }
5623         }
5624         return -1;
5625     },
5626     
5627     
5628     moveColumn : function(oldIndex, newIndex){
5629         var c = this.config[oldIndex];
5630         this.config.splice(oldIndex, 1);
5631         this.config.splice(newIndex, 0, c);
5632         this.dataMap = null;
5633         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5634     },
5635
5636     isLocked : function(colIndex){
5637         return this.config[colIndex].locked === true;
5638     },
5639
5640     setLocked : function(colIndex, value, suppressEvent){
5641         if(this.isLocked(colIndex) == value){
5642             return;
5643         }
5644         this.config[colIndex].locked = value;
5645         if(!suppressEvent){
5646             this.fireEvent("columnlockchange", this, colIndex, value);
5647         }
5648     },
5649
5650     getTotalLockedWidth : function(){
5651         var totalWidth = 0;
5652         for(var i = 0; i < this.config.length; i++){
5653             if(this.isLocked(i) && !this.isHidden(i)){
5654                 this.totalWidth += this.getColumnWidth(i);
5655             }
5656         }
5657         return totalWidth;
5658     },
5659
5660     getLockedCount : function(){
5661         for(var i = 0, len = this.config.length; i < len; i++){
5662             if(!this.isLocked(i)){
5663                 return i;
5664             }
5665         }
5666         
5667         return this.config.length;
5668     },
5669
5670     /**
5671      * Returns the number of columns.
5672      * @return {Number}
5673      */
5674     getColumnCount : function(visibleOnly){
5675         if(visibleOnly === true){
5676             var c = 0;
5677             for(var i = 0, len = this.config.length; i < len; i++){
5678                 if(!this.isHidden(i)){
5679                     c++;
5680                 }
5681             }
5682             return c;
5683         }
5684         return this.config.length;
5685     },
5686
5687     /**
5688      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5689      * @param {Function} fn
5690      * @param {Object} scope (optional)
5691      * @return {Array} result
5692      */
5693     getColumnsBy : function(fn, scope){
5694         var r = [];
5695         for(var i = 0, len = this.config.length; i < len; i++){
5696             var c = this.config[i];
5697             if(fn.call(scope||this, c, i) === true){
5698                 r[r.length] = c;
5699             }
5700         }
5701         return r;
5702     },
5703
5704     /**
5705      * Returns true if the specified column is sortable.
5706      * @param {Number} col The column index
5707      * @return {Boolean}
5708      */
5709     isSortable : function(col){
5710         if(typeof this.config[col].sortable == "undefined"){
5711             return this.defaultSortable;
5712         }
5713         return this.config[col].sortable;
5714     },
5715
5716     /**
5717      * Returns the rendering (formatting) function defined for the column.
5718      * @param {Number} col The column index.
5719      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5720      */
5721     getRenderer : function(col){
5722         if(!this.config[col].renderer){
5723             return Roo.grid.ColumnModel.defaultRenderer;
5724         }
5725         return this.config[col].renderer;
5726     },
5727
5728     /**
5729      * Sets the rendering (formatting) function for a column.
5730      * @param {Number} col The column index
5731      * @param {Function} fn The function to use to process the cell's raw data
5732      * to return HTML markup for the grid view. The render function is called with
5733      * the following parameters:<ul>
5734      * <li>Data value.</li>
5735      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5736      * <li>css A CSS style string to apply to the table cell.</li>
5737      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5738      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5739      * <li>Row index</li>
5740      * <li>Column index</li>
5741      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5742      */
5743     setRenderer : function(col, fn){
5744         this.config[col].renderer = fn;
5745     },
5746
5747     /**
5748      * Returns the width for the specified column.
5749      * @param {Number} col The column index
5750      * @return {Number}
5751      */
5752     getColumnWidth : function(col){
5753         return this.config[col].width * 1 || this.defaultWidth;
5754     },
5755
5756     /**
5757      * Sets the width for a column.
5758      * @param {Number} col The column index
5759      * @param {Number} width The new width
5760      */
5761     setColumnWidth : function(col, width, suppressEvent){
5762         this.config[col].width = width;
5763         this.totalWidth = null;
5764         if(!suppressEvent){
5765              this.fireEvent("widthchange", this, col, width);
5766         }
5767     },
5768
5769     /**
5770      * Returns the total width of all columns.
5771      * @param {Boolean} includeHidden True to include hidden column widths
5772      * @return {Number}
5773      */
5774     getTotalWidth : function(includeHidden){
5775         if(!this.totalWidth){
5776             this.totalWidth = 0;
5777             for(var i = 0, len = this.config.length; i < len; i++){
5778                 if(includeHidden || !this.isHidden(i)){
5779                     this.totalWidth += this.getColumnWidth(i);
5780                 }
5781             }
5782         }
5783         return this.totalWidth;
5784     },
5785
5786     /**
5787      * Returns the header for the specified column.
5788      * @param {Number} col The column index
5789      * @return {String}
5790      */
5791     getColumnHeader : function(col){
5792         return this.config[col].header;
5793     },
5794
5795     /**
5796      * Sets the header for a column.
5797      * @param {Number} col The column index
5798      * @param {String} header The new header
5799      */
5800     setColumnHeader : function(col, header){
5801         this.config[col].header = header;
5802         this.fireEvent("headerchange", this, col, header);
5803     },
5804
5805     /**
5806      * Returns the tooltip for the specified column.
5807      * @param {Number} col The column index
5808      * @return {String}
5809      */
5810     getColumnTooltip : function(col){
5811             return this.config[col].tooltip;
5812     },
5813     /**
5814      * Sets the tooltip for a column.
5815      * @param {Number} col The column index
5816      * @param {String} tooltip The new tooltip
5817      */
5818     setColumnTooltip : function(col, tooltip){
5819             this.config[col].tooltip = tooltip;
5820     },
5821
5822     /**
5823      * Returns the dataIndex for the specified column.
5824      * @param {Number} col The column index
5825      * @return {Number}
5826      */
5827     getDataIndex : function(col){
5828         return this.config[col].dataIndex;
5829     },
5830
5831     /**
5832      * Sets the dataIndex for a column.
5833      * @param {Number} col The column index
5834      * @param {Number} dataIndex The new dataIndex
5835      */
5836     setDataIndex : function(col, dataIndex){
5837         this.config[col].dataIndex = dataIndex;
5838     },
5839
5840     
5841     
5842     /**
5843      * Returns true if the cell is editable.
5844      * @param {Number} colIndex The column index
5845      * @param {Number} rowIndex The row index - this is nto actually used..?
5846      * @return {Boolean}
5847      */
5848     isCellEditable : function(colIndex, rowIndex){
5849         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5850     },
5851
5852     /**
5853      * Returns the editor defined for the cell/column.
5854      * return false or null to disable editing.
5855      * @param {Number} colIndex The column index
5856      * @param {Number} rowIndex The row index
5857      * @return {Object}
5858      */
5859     getCellEditor : function(colIndex, rowIndex){
5860         return this.config[colIndex].editor;
5861     },
5862
5863     /**
5864      * Sets if a column is editable.
5865      * @param {Number} col The column index
5866      * @param {Boolean} editable True if the column is editable
5867      */
5868     setEditable : function(col, editable){
5869         this.config[col].editable = editable;
5870     },
5871
5872
5873     /**
5874      * Returns true if the column is hidden.
5875      * @param {Number} colIndex The column index
5876      * @return {Boolean}
5877      */
5878     isHidden : function(colIndex){
5879         return this.config[colIndex].hidden;
5880     },
5881
5882
5883     /**
5884      * Returns true if the column width cannot be changed
5885      */
5886     isFixed : function(colIndex){
5887         return this.config[colIndex].fixed;
5888     },
5889
5890     /**
5891      * Returns true if the column can be resized
5892      * @return {Boolean}
5893      */
5894     isResizable : function(colIndex){
5895         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5896     },
5897     /**
5898      * Sets if a column is hidden.
5899      * @param {Number} colIndex The column index
5900      * @param {Boolean} hidden True if the column is hidden
5901      */
5902     setHidden : function(colIndex, hidden){
5903         this.config[colIndex].hidden = hidden;
5904         this.totalWidth = null;
5905         this.fireEvent("hiddenchange", this, colIndex, hidden);
5906     },
5907
5908     /**
5909      * Sets the editor for a column.
5910      * @param {Number} col The column index
5911      * @param {Object} editor The editor object
5912      */
5913     setEditor : function(col, editor){
5914         this.config[col].editor = editor;
5915     }
5916 });
5917
5918 Roo.grid.ColumnModel.defaultRenderer = function(value)
5919 {
5920     if(typeof value == "object") {
5921         return value;
5922     }
5923         if(typeof value == "string" && value.length < 1){
5924             return "&#160;";
5925         }
5926     
5927         return String.format("{0}", value);
5928 };
5929
5930 // Alias for backwards compatibility
5931 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5932 /*
5933  * Based on:
5934  * Ext JS Library 1.1.1
5935  * Copyright(c) 2006-2007, Ext JS, LLC.
5936  *
5937  * Originally Released Under LGPL - original licence link has changed is not relivant.
5938  *
5939  * Fork - LGPL
5940  * <script type="text/javascript">
5941  */
5942  
5943 /**
5944  * @class Roo.LoadMask
5945  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5946  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5947  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5948  * element's UpdateManager load indicator and will be destroyed after the initial load.
5949  * @constructor
5950  * Create a new LoadMask
5951  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5952  * @param {Object} config The config object
5953  */
5954 Roo.LoadMask = function(el, config){
5955     this.el = Roo.get(el);
5956     Roo.apply(this, config);
5957     if(this.store){
5958         this.store.on('beforeload', this.onBeforeLoad, this);
5959         this.store.on('load', this.onLoad, this);
5960         this.store.on('loadexception', this.onLoadException, this);
5961         this.removeMask = false;
5962     }else{
5963         var um = this.el.getUpdateManager();
5964         um.showLoadIndicator = false; // disable the default indicator
5965         um.on('beforeupdate', this.onBeforeLoad, this);
5966         um.on('update', this.onLoad, this);
5967         um.on('failure', this.onLoad, this);
5968         this.removeMask = true;
5969     }
5970 };
5971
5972 Roo.LoadMask.prototype = {
5973     /**
5974      * @cfg {Boolean} removeMask
5975      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5976      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5977      */
5978     /**
5979      * @cfg {String} msg
5980      * The text to display in a centered loading message box (defaults to 'Loading...')
5981      */
5982     msg : 'Loading...',
5983     /**
5984      * @cfg {String} msgCls
5985      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5986      */
5987     msgCls : 'x-mask-loading',
5988
5989     /**
5990      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5991      * @type Boolean
5992      */
5993     disabled: false,
5994
5995     /**
5996      * Disables the mask to prevent it from being displayed
5997      */
5998     disable : function(){
5999        this.disabled = true;
6000     },
6001
6002     /**
6003      * Enables the mask so that it can be displayed
6004      */
6005     enable : function(){
6006         this.disabled = false;
6007     },
6008     
6009     onLoadException : function()
6010     {
6011         Roo.log(arguments);
6012         
6013         if (typeof(arguments[3]) != 'undefined') {
6014             Roo.MessageBox.alert("Error loading",arguments[3]);
6015         } 
6016         /*
6017         try {
6018             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6019                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6020             }   
6021         } catch(e) {
6022             
6023         }
6024         */
6025     
6026         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6027     },
6028     // private
6029     onLoad : function()
6030     {
6031         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6032     },
6033
6034     // private
6035     onBeforeLoad : function(){
6036         if(!this.disabled){
6037             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6038         }
6039     },
6040
6041     // private
6042     destroy : function(){
6043         if(this.store){
6044             this.store.un('beforeload', this.onBeforeLoad, this);
6045             this.store.un('load', this.onLoad, this);
6046             this.store.un('loadexception', this.onLoadException, this);
6047         }else{
6048             var um = this.el.getUpdateManager();
6049             um.un('beforeupdate', this.onBeforeLoad, this);
6050             um.un('update', this.onLoad, this);
6051             um.un('failure', this.onLoad, this);
6052         }
6053     }
6054 };/*
6055  * - LGPL
6056  *
6057  * table
6058  * 
6059  */
6060
6061 /**
6062  * @class Roo.bootstrap.Table
6063  * @extends Roo.bootstrap.Component
6064  * Bootstrap Table class
6065  * @cfg {String} cls table class
6066  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6067  * @cfg {String} bgcolor Specifies the background color for a table
6068  * @cfg {Number} border Specifies whether the table cells should have borders or not
6069  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6070  * @cfg {Number} cellspacing Specifies the space between cells
6071  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6072  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6073  * @cfg {String} sortable Specifies that the table should be sortable
6074  * @cfg {String} summary Specifies a summary of the content of a table
6075  * @cfg {Number} width Specifies the width of a table
6076  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6077  * 
6078  * @cfg {boolean} striped Should the rows be alternative striped
6079  * @cfg {boolean} bordered Add borders to the table
6080  * @cfg {boolean} hover Add hover highlighting
6081  * @cfg {boolean} condensed Format condensed
6082  * @cfg {boolean} responsive Format condensed
6083  * @cfg {Boolean} loadMask (true|false) default false
6084  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6085  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6086  * @cfg {Boolean} rowSelection (true|false) default false
6087  * @cfg {Boolean} cellSelection (true|false) default false
6088  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6089  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6090  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6091  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6092  
6093  * 
6094  * @constructor
6095  * Create a new Table
6096  * @param {Object} config The config object
6097  */
6098
6099 Roo.bootstrap.Table = function(config){
6100     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6101     
6102   
6103     
6104     // BC...
6105     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6106     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6107     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6108     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6109     
6110     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6111     if (this.sm) {
6112         this.sm.grid = this;
6113         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6114         this.sm = this.selModel;
6115         this.sm.xmodule = this.xmodule || false;
6116     }
6117     
6118     if (this.cm && typeof(this.cm.config) == 'undefined') {
6119         this.colModel = new Roo.grid.ColumnModel(this.cm);
6120         this.cm = this.colModel;
6121         this.cm.xmodule = this.xmodule || false;
6122     }
6123     if (this.store) {
6124         this.store= Roo.factory(this.store, Roo.data);
6125         this.ds = this.store;
6126         this.ds.xmodule = this.xmodule || false;
6127          
6128     }
6129     if (this.footer && this.store) {
6130         this.footer.dataSource = this.ds;
6131         this.footer = Roo.factory(this.footer);
6132     }
6133     
6134     /** @private */
6135     this.addEvents({
6136         /**
6137          * @event cellclick
6138          * Fires when a cell is clicked
6139          * @param {Roo.bootstrap.Table} this
6140          * @param {Roo.Element} el
6141          * @param {Number} rowIndex
6142          * @param {Number} columnIndex
6143          * @param {Roo.EventObject} e
6144          */
6145         "cellclick" : true,
6146         /**
6147          * @event celldblclick
6148          * Fires when a cell is double clicked
6149          * @param {Roo.bootstrap.Table} this
6150          * @param {Roo.Element} el
6151          * @param {Number} rowIndex
6152          * @param {Number} columnIndex
6153          * @param {Roo.EventObject} e
6154          */
6155         "celldblclick" : true,
6156         /**
6157          * @event rowclick
6158          * Fires when a row is clicked
6159          * @param {Roo.bootstrap.Table} this
6160          * @param {Roo.Element} el
6161          * @param {Number} rowIndex
6162          * @param {Roo.EventObject} e
6163          */
6164         "rowclick" : true,
6165         /**
6166          * @event rowdblclick
6167          * Fires when a row is double clicked
6168          * @param {Roo.bootstrap.Table} this
6169          * @param {Roo.Element} el
6170          * @param {Number} rowIndex
6171          * @param {Roo.EventObject} e
6172          */
6173         "rowdblclick" : true,
6174         /**
6175          * @event mouseover
6176          * Fires when a mouseover occur
6177          * @param {Roo.bootstrap.Table} this
6178          * @param {Roo.Element} el
6179          * @param {Number} rowIndex
6180          * @param {Number} columnIndex
6181          * @param {Roo.EventObject} e
6182          */
6183         "mouseover" : true,
6184         /**
6185          * @event mouseout
6186          * Fires when a mouseout occur
6187          * @param {Roo.bootstrap.Table} this
6188          * @param {Roo.Element} el
6189          * @param {Number} rowIndex
6190          * @param {Number} columnIndex
6191          * @param {Roo.EventObject} e
6192          */
6193         "mouseout" : true,
6194         /**
6195          * @event rowclass
6196          * Fires when a row is rendered, so you can change add a style to it.
6197          * @param {Roo.bootstrap.Table} this
6198          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6199          */
6200         'rowclass' : true,
6201           /**
6202          * @event rowsrendered
6203          * Fires when all the  rows have been rendered
6204          * @param {Roo.bootstrap.Table} this
6205          */
6206         'rowsrendered' : true,
6207         /**
6208          * @event contextmenu
6209          * The raw contextmenu event for the entire grid.
6210          * @param {Roo.EventObject} e
6211          */
6212         "contextmenu" : true,
6213         /**
6214          * @event rowcontextmenu
6215          * Fires when a row is right clicked
6216          * @param {Roo.bootstrap.Table} this
6217          * @param {Number} rowIndex
6218          * @param {Roo.EventObject} e
6219          */
6220         "rowcontextmenu" : true,
6221         /**
6222          * @event cellcontextmenu
6223          * Fires when a cell is right clicked
6224          * @param {Roo.bootstrap.Table} this
6225          * @param {Number} rowIndex
6226          * @param {Number} cellIndex
6227          * @param {Roo.EventObject} e
6228          */
6229          "cellcontextmenu" : true,
6230          /**
6231          * @event headercontextmenu
6232          * Fires when a header is right clicked
6233          * @param {Roo.bootstrap.Table} this
6234          * @param {Number} columnIndex
6235          * @param {Roo.EventObject} e
6236          */
6237         "headercontextmenu" : true
6238     });
6239 };
6240
6241 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6242     
6243     cls: false,
6244     align: false,
6245     bgcolor: false,
6246     border: false,
6247     cellpadding: false,
6248     cellspacing: false,
6249     frame: false,
6250     rules: false,
6251     sortable: false,
6252     summary: false,
6253     width: false,
6254     striped : false,
6255     scrollBody : false,
6256     bordered: false,
6257     hover:  false,
6258     condensed : false,
6259     responsive : false,
6260     sm : false,
6261     cm : false,
6262     store : false,
6263     loadMask : false,
6264     footerShow : true,
6265     headerShow : true,
6266   
6267     rowSelection : false,
6268     cellSelection : false,
6269     layout : false,
6270     
6271     // Roo.Element - the tbody
6272     mainBody: false,
6273     // Roo.Element - thead element
6274     mainHead: false,
6275     
6276     container: false, // used by gridpanel...
6277     
6278     lazyLoad : false,
6279     
6280     CSS : Roo.util.CSS,
6281     
6282     auto_hide_footer : false,
6283     
6284     getAutoCreate : function()
6285     {
6286         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6287         
6288         cfg = {
6289             tag: 'table',
6290             cls : 'table',
6291             cn : []
6292         };
6293         if (this.scrollBody) {
6294             cfg.cls += ' table-body-fixed';
6295         }    
6296         if (this.striped) {
6297             cfg.cls += ' table-striped';
6298         }
6299         
6300         if (this.hover) {
6301             cfg.cls += ' table-hover';
6302         }
6303         if (this.bordered) {
6304             cfg.cls += ' table-bordered';
6305         }
6306         if (this.condensed) {
6307             cfg.cls += ' table-condensed';
6308         }
6309         if (this.responsive) {
6310             cfg.cls += ' table-responsive';
6311         }
6312         
6313         if (this.cls) {
6314             cfg.cls+=  ' ' +this.cls;
6315         }
6316         
6317         // this lot should be simplifed...
6318         var _t = this;
6319         var cp = [
6320             'align',
6321             'bgcolor',
6322             'border',
6323             'cellpadding',
6324             'cellspacing',
6325             'frame',
6326             'rules',
6327             'sortable',
6328             'summary',
6329             'width'
6330         ].forEach(function(k) {
6331             if (_t[k]) {
6332                 cfg[k] = _t[k];
6333             }
6334         });
6335         
6336         
6337         if (this.layout) {
6338             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6339         }
6340         
6341         if(this.store || this.cm){
6342             if(this.headerShow){
6343                 cfg.cn.push(this.renderHeader());
6344             }
6345             
6346             cfg.cn.push(this.renderBody());
6347             
6348             if(this.footerShow){
6349                 cfg.cn.push(this.renderFooter());
6350             }
6351             // where does this come from?
6352             //cfg.cls+=  ' TableGrid';
6353         }
6354         
6355         return { cn : [ cfg ] };
6356     },
6357     
6358     initEvents : function()
6359     {   
6360         if(!this.store || !this.cm){
6361             return;
6362         }
6363         if (this.selModel) {
6364             this.selModel.initEvents();
6365         }
6366         
6367         
6368         //Roo.log('initEvents with ds!!!!');
6369         
6370         this.mainBody = this.el.select('tbody', true).first();
6371         this.mainHead = this.el.select('thead', true).first();
6372         this.mainFoot = this.el.select('tfoot', true).first();
6373         
6374         
6375         
6376         var _this = this;
6377         
6378         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6379             e.on('click', _this.sort, _this);
6380         });
6381         
6382         this.mainBody.on("click", this.onClick, this);
6383         this.mainBody.on("dblclick", this.onDblClick, this);
6384         
6385         // why is this done????? = it breaks dialogs??
6386         //this.parent().el.setStyle('position', 'relative');
6387         
6388         
6389         if (this.footer) {
6390             this.footer.parentId = this.id;
6391             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6392             
6393             if(this.lazyLoad){
6394                 this.el.select('tfoot tr td').first().addClass('hide');
6395             }
6396         } 
6397         
6398         if(this.loadMask) {
6399             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6400         }
6401         
6402         this.store.on('load', this.onLoad, this);
6403         this.store.on('beforeload', this.onBeforeLoad, this);
6404         this.store.on('update', this.onUpdate, this);
6405         this.store.on('add', this.onAdd, this);
6406         this.store.on("clear", this.clear, this);
6407         
6408         this.el.on("contextmenu", this.onContextMenu, this);
6409         
6410         this.mainBody.on('scroll', this.onBodyScroll, this);
6411         
6412         this.cm.on("headerchange", this.onHeaderChange, this);
6413         
6414         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6415         
6416     },
6417     
6418     onContextMenu : function(e, t)
6419     {
6420         this.processEvent("contextmenu", e);
6421     },
6422     
6423     processEvent : function(name, e)
6424     {
6425         if (name != 'touchstart' ) {
6426             this.fireEvent(name, e);    
6427         }
6428         
6429         var t = e.getTarget();
6430         
6431         var cell = Roo.get(t);
6432         
6433         if(!cell){
6434             return;
6435         }
6436         
6437         if(cell.findParent('tfoot', false, true)){
6438             return;
6439         }
6440         
6441         if(cell.findParent('thead', false, true)){
6442             
6443             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6444                 cell = Roo.get(t).findParent('th', false, true);
6445                 if (!cell) {
6446                     Roo.log("failed to find th in thead?");
6447                     Roo.log(e.getTarget());
6448                     return;
6449                 }
6450             }
6451             
6452             var cellIndex = cell.dom.cellIndex;
6453             
6454             var ename = name == 'touchstart' ? 'click' : name;
6455             this.fireEvent("header" + ename, this, cellIndex, e);
6456             
6457             return;
6458         }
6459         
6460         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6461             cell = Roo.get(t).findParent('td', false, true);
6462             if (!cell) {
6463                 Roo.log("failed to find th in tbody?");
6464                 Roo.log(e.getTarget());
6465                 return;
6466             }
6467         }
6468         
6469         var row = cell.findParent('tr', false, true);
6470         var cellIndex = cell.dom.cellIndex;
6471         var rowIndex = row.dom.rowIndex - 1;
6472         
6473         if(row !== false){
6474             
6475             this.fireEvent("row" + name, this, rowIndex, e);
6476             
6477             if(cell !== false){
6478             
6479                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6480             }
6481         }
6482         
6483     },
6484     
6485     onMouseover : function(e, el)
6486     {
6487         var cell = Roo.get(el);
6488         
6489         if(!cell){
6490             return;
6491         }
6492         
6493         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6494             cell = cell.findParent('td', false, true);
6495         }
6496         
6497         var row = cell.findParent('tr', false, true);
6498         var cellIndex = cell.dom.cellIndex;
6499         var rowIndex = row.dom.rowIndex - 1; // start from 0
6500         
6501         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6502         
6503     },
6504     
6505     onMouseout : function(e, el)
6506     {
6507         var cell = Roo.get(el);
6508         
6509         if(!cell){
6510             return;
6511         }
6512         
6513         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6514             cell = cell.findParent('td', false, true);
6515         }
6516         
6517         var row = cell.findParent('tr', false, true);
6518         var cellIndex = cell.dom.cellIndex;
6519         var rowIndex = row.dom.rowIndex - 1; // start from 0
6520         
6521         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6522         
6523     },
6524     
6525     onClick : function(e, el)
6526     {
6527         var cell = Roo.get(el);
6528         
6529         if(!cell || (!this.cellSelection && !this.rowSelection)){
6530             return;
6531         }
6532         
6533         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6534             cell = cell.findParent('td', false, true);
6535         }
6536         
6537         if(!cell || typeof(cell) == 'undefined'){
6538             return;
6539         }
6540         
6541         var row = cell.findParent('tr', false, true);
6542         
6543         if(!row || typeof(row) == 'undefined'){
6544             return;
6545         }
6546         
6547         var cellIndex = cell.dom.cellIndex;
6548         var rowIndex = this.getRowIndex(row);
6549         
6550         // why??? - should these not be based on SelectionModel?
6551         if(this.cellSelection){
6552             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6553         }
6554         
6555         if(this.rowSelection){
6556             this.fireEvent('rowclick', this, row, rowIndex, e);
6557         }
6558         
6559         
6560     },
6561         
6562     onDblClick : function(e,el)
6563     {
6564         var cell = Roo.get(el);
6565         
6566         if(!cell || (!this.cellSelection && !this.rowSelection)){
6567             return;
6568         }
6569         
6570         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6571             cell = cell.findParent('td', false, true);
6572         }
6573         
6574         if(!cell || typeof(cell) == 'undefined'){
6575             return;
6576         }
6577         
6578         var row = cell.findParent('tr', false, true);
6579         
6580         if(!row || typeof(row) == 'undefined'){
6581             return;
6582         }
6583         
6584         var cellIndex = cell.dom.cellIndex;
6585         var rowIndex = this.getRowIndex(row);
6586         
6587         if(this.cellSelection){
6588             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6589         }
6590         
6591         if(this.rowSelection){
6592             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6593         }
6594     },
6595     
6596     sort : function(e,el)
6597     {
6598         var col = Roo.get(el);
6599         
6600         if(!col.hasClass('sortable')){
6601             return;
6602         }
6603         
6604         var sort = col.attr('sort');
6605         var dir = 'ASC';
6606         
6607         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6608             dir = 'DESC';
6609         }
6610         
6611         this.store.sortInfo = {field : sort, direction : dir};
6612         
6613         if (this.footer) {
6614             Roo.log("calling footer first");
6615             this.footer.onClick('first');
6616         } else {
6617         
6618             this.store.load({ params : { start : 0 } });
6619         }
6620     },
6621     
6622     renderHeader : function()
6623     {
6624         var header = {
6625             tag: 'thead',
6626             cn : []
6627         };
6628         
6629         var cm = this.cm;
6630         this.totalWidth = 0;
6631         
6632         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6633             
6634             var config = cm.config[i];
6635             
6636             var c = {
6637                 tag: 'th',
6638                 cls : 'x-hcol-' + i,
6639                 style : '',
6640                 html: cm.getColumnHeader(i)
6641             };
6642             
6643             var hh = '';
6644             
6645             if(typeof(config.sortable) != 'undefined' && config.sortable){
6646                 c.cls = 'sortable';
6647                 c.html = '<i class="glyphicon"></i>' + c.html;
6648             }
6649             
6650             if(typeof(config.lgHeader) != 'undefined'){
6651                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6652             }
6653             
6654             if(typeof(config.mdHeader) != 'undefined'){
6655                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6656             }
6657             
6658             if(typeof(config.smHeader) != 'undefined'){
6659                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6660             }
6661             
6662             if(typeof(config.xsHeader) != 'undefined'){
6663                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6664             }
6665             
6666             if(hh.length){
6667                 c.html = hh;
6668             }
6669             
6670             if(typeof(config.tooltip) != 'undefined'){
6671                 c.tooltip = config.tooltip;
6672             }
6673             
6674             if(typeof(config.colspan) != 'undefined'){
6675                 c.colspan = config.colspan;
6676             }
6677             
6678             if(typeof(config.hidden) != 'undefined' && config.hidden){
6679                 c.style += ' display:none;';
6680             }
6681             
6682             if(typeof(config.dataIndex) != 'undefined'){
6683                 c.sort = config.dataIndex;
6684             }
6685             
6686            
6687             
6688             if(typeof(config.align) != 'undefined' && config.align.length){
6689                 c.style += ' text-align:' + config.align + ';';
6690             }
6691             
6692             if(typeof(config.width) != 'undefined'){
6693                 c.style += ' width:' + config.width + 'px;';
6694                 this.totalWidth += config.width;
6695             } else {
6696                 this.totalWidth += 100; // assume minimum of 100 per column?
6697             }
6698             
6699             if(typeof(config.cls) != 'undefined'){
6700                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6701             }
6702             
6703             ['xs','sm','md','lg'].map(function(size){
6704                 
6705                 if(typeof(config[size]) == 'undefined'){
6706                     return;
6707                 }
6708                 
6709                 if (!config[size]) { // 0 = hidden
6710                     c.cls += ' hidden-' + size;
6711                     return;
6712                 }
6713                 
6714                 c.cls += ' col-' + size + '-' + config[size];
6715
6716             });
6717             
6718             header.cn.push(c)
6719         }
6720         
6721         return header;
6722     },
6723     
6724     renderBody : function()
6725     {
6726         var body = {
6727             tag: 'tbody',
6728             cn : [
6729                 {
6730                     tag: 'tr',
6731                     cn : [
6732                         {
6733                             tag : 'td',
6734                             colspan :  this.cm.getColumnCount()
6735                         }
6736                     ]
6737                 }
6738             ]
6739         };
6740         
6741         return body;
6742     },
6743     
6744     renderFooter : function()
6745     {
6746         var footer = {
6747             tag: 'tfoot',
6748             cn : [
6749                 {
6750                     tag: 'tr',
6751                     cn : [
6752                         {
6753                             tag : 'td',
6754                             colspan :  this.cm.getColumnCount()
6755                         }
6756                     ]
6757                 }
6758             ]
6759         };
6760         
6761         return footer;
6762     },
6763     
6764     
6765     
6766     onLoad : function()
6767     {
6768 //        Roo.log('ds onload');
6769         this.clear();
6770         
6771         var _this = this;
6772         var cm = this.cm;
6773         var ds = this.store;
6774         
6775         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6776             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6777             if (_this.store.sortInfo) {
6778                     
6779                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6780                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6781                 }
6782                 
6783                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6784                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6785                 }
6786             }
6787         });
6788         
6789         var tbody =  this.mainBody;
6790               
6791         if(ds.getCount() > 0){
6792             ds.data.each(function(d,rowIndex){
6793                 var row =  this.renderRow(cm, ds, rowIndex);
6794                 
6795                 tbody.createChild(row);
6796                 
6797                 var _this = this;
6798                 
6799                 if(row.cellObjects.length){
6800                     Roo.each(row.cellObjects, function(r){
6801                         _this.renderCellObject(r);
6802                     })
6803                 }
6804                 
6805             }, this);
6806         }
6807         
6808         var tfoot = this.el.select('tfoot', true).first();
6809         
6810         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6811             
6812             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6813             
6814             var total = this.ds.getTotalCount();
6815             
6816             if(this.footer.pageSize < total){
6817                 this.mainFoot.show();
6818             }
6819         }
6820         
6821         Roo.each(this.el.select('tbody td', true).elements, function(e){
6822             e.on('mouseover', _this.onMouseover, _this);
6823         });
6824         
6825         Roo.each(this.el.select('tbody td', true).elements, function(e){
6826             e.on('mouseout', _this.onMouseout, _this);
6827         });
6828         this.fireEvent('rowsrendered', this);
6829         
6830         this.autoSize();
6831     },
6832     
6833     
6834     onUpdate : function(ds,record)
6835     {
6836         this.refreshRow(record);
6837         this.autoSize();
6838     },
6839     
6840     onRemove : function(ds, record, index, isUpdate){
6841         if(isUpdate !== true){
6842             this.fireEvent("beforerowremoved", this, index, record);
6843         }
6844         var bt = this.mainBody.dom;
6845         
6846         var rows = this.el.select('tbody > tr', true).elements;
6847         
6848         if(typeof(rows[index]) != 'undefined'){
6849             bt.removeChild(rows[index].dom);
6850         }
6851         
6852 //        if(bt.rows[index]){
6853 //            bt.removeChild(bt.rows[index]);
6854 //        }
6855         
6856         if(isUpdate !== true){
6857             //this.stripeRows(index);
6858             //this.syncRowHeights(index, index);
6859             //this.layout();
6860             this.fireEvent("rowremoved", this, index, record);
6861         }
6862     },
6863     
6864     onAdd : function(ds, records, rowIndex)
6865     {
6866         //Roo.log('on Add called');
6867         // - note this does not handle multiple adding very well..
6868         var bt = this.mainBody.dom;
6869         for (var i =0 ; i < records.length;i++) {
6870             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6871             //Roo.log(records[i]);
6872             //Roo.log(this.store.getAt(rowIndex+i));
6873             this.insertRow(this.store, rowIndex + i, false);
6874             return;
6875         }
6876         
6877     },
6878     
6879     
6880     refreshRow : function(record){
6881         var ds = this.store, index;
6882         if(typeof record == 'number'){
6883             index = record;
6884             record = ds.getAt(index);
6885         }else{
6886             index = ds.indexOf(record);
6887         }
6888         this.insertRow(ds, index, true);
6889         this.autoSize();
6890         this.onRemove(ds, record, index+1, true);
6891         this.autoSize();
6892         //this.syncRowHeights(index, index);
6893         //this.layout();
6894         this.fireEvent("rowupdated", this, index, record);
6895     },
6896     
6897     insertRow : function(dm, rowIndex, isUpdate){
6898         
6899         if(!isUpdate){
6900             this.fireEvent("beforerowsinserted", this, rowIndex);
6901         }
6902             //var s = this.getScrollState();
6903         var row = this.renderRow(this.cm, this.store, rowIndex);
6904         // insert before rowIndex..
6905         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6906         
6907         var _this = this;
6908                 
6909         if(row.cellObjects.length){
6910             Roo.each(row.cellObjects, function(r){
6911                 _this.renderCellObject(r);
6912             })
6913         }
6914             
6915         if(!isUpdate){
6916             this.fireEvent("rowsinserted", this, rowIndex);
6917             //this.syncRowHeights(firstRow, lastRow);
6918             //this.stripeRows(firstRow);
6919             //this.layout();
6920         }
6921         
6922     },
6923     
6924     
6925     getRowDom : function(rowIndex)
6926     {
6927         var rows = this.el.select('tbody > tr', true).elements;
6928         
6929         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6930         
6931     },
6932     // returns the object tree for a tr..
6933   
6934     
6935     renderRow : function(cm, ds, rowIndex) 
6936     {
6937         var d = ds.getAt(rowIndex);
6938         
6939         var row = {
6940             tag : 'tr',
6941             cls : 'x-row-' + rowIndex,
6942             cn : []
6943         };
6944             
6945         var cellObjects = [];
6946         
6947         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6948             var config = cm.config[i];
6949             
6950             var renderer = cm.getRenderer(i);
6951             var value = '';
6952             var id = false;
6953             
6954             if(typeof(renderer) !== 'undefined'){
6955                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6956             }
6957             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6958             // and are rendered into the cells after the row is rendered - using the id for the element.
6959             
6960             if(typeof(value) === 'object'){
6961                 id = Roo.id();
6962                 cellObjects.push({
6963                     container : id,
6964                     cfg : value 
6965                 })
6966             }
6967             
6968             var rowcfg = {
6969                 record: d,
6970                 rowIndex : rowIndex,
6971                 colIndex : i,
6972                 rowClass : ''
6973             };
6974
6975             this.fireEvent('rowclass', this, rowcfg);
6976             
6977             var td = {
6978                 tag: 'td',
6979                 cls : rowcfg.rowClass + ' x-col-' + i,
6980                 style: '',
6981                 html: (typeof(value) === 'object') ? '' : value
6982             };
6983             
6984             if (id) {
6985                 td.id = id;
6986             }
6987             
6988             if(typeof(config.colspan) != 'undefined'){
6989                 td.colspan = config.colspan;
6990             }
6991             
6992             if(typeof(config.hidden) != 'undefined' && config.hidden){
6993                 td.style += ' display:none;';
6994             }
6995             
6996             if(typeof(config.align) != 'undefined' && config.align.length){
6997                 td.style += ' text-align:' + config.align + ';';
6998             }
6999             if(typeof(config.valign) != 'undefined' && config.valign.length){
7000                 td.style += ' vertical-align:' + config.valign + ';';
7001             }
7002             
7003             if(typeof(config.width) != 'undefined'){
7004                 td.style += ' width:' +  config.width + 'px;';
7005             }
7006             
7007             if(typeof(config.cursor) != 'undefined'){
7008                 td.style += ' cursor:' +  config.cursor + ';';
7009             }
7010             
7011             if(typeof(config.cls) != 'undefined'){
7012                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7013             }
7014             
7015             ['xs','sm','md','lg'].map(function(size){
7016                 
7017                 if(typeof(config[size]) == 'undefined'){
7018                     return;
7019                 }
7020                 
7021                 if (!config[size]) { // 0 = hidden
7022                     td.cls += ' hidden-' + size;
7023                     return;
7024                 }
7025                 
7026                 td.cls += ' col-' + size + '-' + config[size];
7027
7028             });
7029             
7030             row.cn.push(td);
7031            
7032         }
7033         
7034         row.cellObjects = cellObjects;
7035         
7036         return row;
7037           
7038     },
7039     
7040     
7041     
7042     onBeforeLoad : function()
7043     {
7044         
7045     },
7046      /**
7047      * Remove all rows
7048      */
7049     clear : function()
7050     {
7051         this.el.select('tbody', true).first().dom.innerHTML = '';
7052     },
7053     /**
7054      * Show or hide a row.
7055      * @param {Number} rowIndex to show or hide
7056      * @param {Boolean} state hide
7057      */
7058     setRowVisibility : function(rowIndex, state)
7059     {
7060         var bt = this.mainBody.dom;
7061         
7062         var rows = this.el.select('tbody > tr', true).elements;
7063         
7064         if(typeof(rows[rowIndex]) == 'undefined'){
7065             return;
7066         }
7067         rows[rowIndex].dom.style.display = state ? '' : 'none';
7068     },
7069     
7070     
7071     getSelectionModel : function(){
7072         if(!this.selModel){
7073             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7074         }
7075         return this.selModel;
7076     },
7077     /*
7078      * Render the Roo.bootstrap object from renderder
7079      */
7080     renderCellObject : function(r)
7081     {
7082         var _this = this;
7083         
7084         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7085         
7086         var t = r.cfg.render(r.container);
7087         
7088         if(r.cfg.cn){
7089             Roo.each(r.cfg.cn, function(c){
7090                 var child = {
7091                     container: t.getChildContainer(),
7092                     cfg: c
7093                 };
7094                 _this.renderCellObject(child);
7095             })
7096         }
7097     },
7098     
7099     getRowIndex : function(row)
7100     {
7101         var rowIndex = -1;
7102         
7103         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7104             if(el != row){
7105                 return;
7106             }
7107             
7108             rowIndex = index;
7109         });
7110         
7111         return rowIndex;
7112     },
7113      /**
7114      * Returns the grid's underlying element = used by panel.Grid
7115      * @return {Element} The element
7116      */
7117     getGridEl : function(){
7118         return this.el;
7119     },
7120      /**
7121      * Forces a resize - used by panel.Grid
7122      * @return {Element} The element
7123      */
7124     autoSize : function()
7125     {
7126         //var ctr = Roo.get(this.container.dom.parentElement);
7127         var ctr = Roo.get(this.el.dom);
7128         
7129         var thd = this.getGridEl().select('thead',true).first();
7130         var tbd = this.getGridEl().select('tbody', true).first();
7131         var tfd = this.getGridEl().select('tfoot', true).first();
7132         
7133         var cw = ctr.getWidth();
7134         
7135         if (tbd) {
7136             
7137             tbd.setSize(ctr.getWidth(),
7138                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7139             );
7140             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7141             cw -= barsize;
7142         }
7143         cw = Math.max(cw, this.totalWidth);
7144         this.getGridEl().select('tr',true).setWidth(cw);
7145         // resize 'expandable coloumn?
7146         
7147         return; // we doe not have a view in this design..
7148         
7149     },
7150     onBodyScroll: function()
7151     {
7152         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7153         if(this.mainHead){
7154             this.mainHead.setStyle({
7155                 'position' : 'relative',
7156                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7157             });
7158         }
7159         
7160         if(this.lazyLoad){
7161             
7162             var scrollHeight = this.mainBody.dom.scrollHeight;
7163             
7164             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7165             
7166             var height = this.mainBody.getHeight();
7167             
7168             if(scrollHeight - height == scrollTop) {
7169                 
7170                 var total = this.ds.getTotalCount();
7171                 
7172                 if(this.footer.cursor + this.footer.pageSize < total){
7173                     
7174                     this.footer.ds.load({
7175                         params : {
7176                             start : this.footer.cursor + this.footer.pageSize,
7177                             limit : this.footer.pageSize
7178                         },
7179                         add : true
7180                     });
7181                 }
7182             }
7183             
7184         }
7185     },
7186     
7187     onHeaderChange : function()
7188     {
7189         var header = this.renderHeader();
7190         var table = this.el.select('table', true).first();
7191         
7192         this.mainHead.remove();
7193         this.mainHead = table.createChild(header, this.mainBody, false);
7194     },
7195     
7196     onHiddenChange : function(colModel, colIndex, hidden)
7197     {
7198         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7199         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7200         
7201         this.CSS.updateRule(thSelector, "display", "");
7202         this.CSS.updateRule(tdSelector, "display", "");
7203         
7204         if(hidden){
7205             this.CSS.updateRule(thSelector, "display", "none");
7206             this.CSS.updateRule(tdSelector, "display", "none");
7207         }
7208         
7209         this.onHeaderChange();
7210         this.onLoad();
7211     },
7212     
7213     setColumnWidth: function(col_index, width)
7214     {
7215         // width = "md-2 xs-2..."
7216         if(!this.colModel.config[col_index]) {
7217             return;
7218         }
7219         
7220         var w = width.split(" ");
7221         
7222         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7223         
7224         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7225         
7226         
7227         for(var j = 0; j < w.length; j++) {
7228             
7229             if(!w[j]) {
7230                 continue;
7231             }
7232             
7233             var size_cls = w[j].split("-");
7234             
7235             if(!Number.isInteger(size_cls[1] * 1)) {
7236                 continue;
7237             }
7238             
7239             if(!this.colModel.config[col_index][size_cls[0]]) {
7240                 continue;
7241             }
7242             
7243             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7244                 continue;
7245             }
7246             
7247             h_row[0].classList.replace(
7248                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7249                 "col-"+size_cls[0]+"-"+size_cls[1]
7250             );
7251             
7252             for(var i = 0; i < rows.length; i++) {
7253                 
7254                 var size_cls = w[j].split("-");
7255                 
7256                 if(!Number.isInteger(size_cls[1] * 1)) {
7257                     continue;
7258                 }
7259                 
7260                 if(!this.colModel.config[col_index][size_cls[0]]) {
7261                     continue;
7262                 }
7263                 
7264                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7265                     continue;
7266                 }
7267                 
7268                 rows[i].classList.replace(
7269                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7270                     "col-"+size_cls[0]+"-"+size_cls[1]
7271                 );
7272             }
7273             
7274             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7275         }
7276     }
7277 });
7278
7279  
7280
7281  /*
7282  * - LGPL
7283  *
7284  * table cell
7285  * 
7286  */
7287
7288 /**
7289  * @class Roo.bootstrap.TableCell
7290  * @extends Roo.bootstrap.Component
7291  * Bootstrap TableCell class
7292  * @cfg {String} html cell contain text
7293  * @cfg {String} cls cell class
7294  * @cfg {String} tag cell tag (td|th) default td
7295  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7296  * @cfg {String} align Aligns the content in a cell
7297  * @cfg {String} axis Categorizes cells
7298  * @cfg {String} bgcolor Specifies the background color of a cell
7299  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7300  * @cfg {Number} colspan Specifies the number of columns a cell should span
7301  * @cfg {String} headers Specifies one or more header cells a cell is related to
7302  * @cfg {Number} height Sets the height of a cell
7303  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7304  * @cfg {Number} rowspan Sets the number of rows a cell should span
7305  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7306  * @cfg {String} valign Vertical aligns the content in a cell
7307  * @cfg {Number} width Specifies the width of a cell
7308  * 
7309  * @constructor
7310  * Create a new TableCell
7311  * @param {Object} config The config object
7312  */
7313
7314 Roo.bootstrap.TableCell = function(config){
7315     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7316 };
7317
7318 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7319     
7320     html: false,
7321     cls: false,
7322     tag: false,
7323     abbr: false,
7324     align: false,
7325     axis: false,
7326     bgcolor: false,
7327     charoff: false,
7328     colspan: false,
7329     headers: false,
7330     height: false,
7331     nowrap: false,
7332     rowspan: false,
7333     scope: false,
7334     valign: false,
7335     width: false,
7336     
7337     
7338     getAutoCreate : function(){
7339         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7340         
7341         cfg = {
7342             tag: 'td'
7343         };
7344         
7345         if(this.tag){
7346             cfg.tag = this.tag;
7347         }
7348         
7349         if (this.html) {
7350             cfg.html=this.html
7351         }
7352         if (this.cls) {
7353             cfg.cls=this.cls
7354         }
7355         if (this.abbr) {
7356             cfg.abbr=this.abbr
7357         }
7358         if (this.align) {
7359             cfg.align=this.align
7360         }
7361         if (this.axis) {
7362             cfg.axis=this.axis
7363         }
7364         if (this.bgcolor) {
7365             cfg.bgcolor=this.bgcolor
7366         }
7367         if (this.charoff) {
7368             cfg.charoff=this.charoff
7369         }
7370         if (this.colspan) {
7371             cfg.colspan=this.colspan
7372         }
7373         if (this.headers) {
7374             cfg.headers=this.headers
7375         }
7376         if (this.height) {
7377             cfg.height=this.height
7378         }
7379         if (this.nowrap) {
7380             cfg.nowrap=this.nowrap
7381         }
7382         if (this.rowspan) {
7383             cfg.rowspan=this.rowspan
7384         }
7385         if (this.scope) {
7386             cfg.scope=this.scope
7387         }
7388         if (this.valign) {
7389             cfg.valign=this.valign
7390         }
7391         if (this.width) {
7392             cfg.width=this.width
7393         }
7394         
7395         
7396         return cfg;
7397     }
7398    
7399 });
7400
7401  
7402
7403  /*
7404  * - LGPL
7405  *
7406  * table row
7407  * 
7408  */
7409
7410 /**
7411  * @class Roo.bootstrap.TableRow
7412  * @extends Roo.bootstrap.Component
7413  * Bootstrap TableRow class
7414  * @cfg {String} cls row class
7415  * @cfg {String} align Aligns the content in a table row
7416  * @cfg {String} bgcolor Specifies a background color for a table row
7417  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7418  * @cfg {String} valign Vertical aligns the content in a table row
7419  * 
7420  * @constructor
7421  * Create a new TableRow
7422  * @param {Object} config The config object
7423  */
7424
7425 Roo.bootstrap.TableRow = function(config){
7426     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7427 };
7428
7429 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7430     
7431     cls: false,
7432     align: false,
7433     bgcolor: false,
7434     charoff: false,
7435     valign: false,
7436     
7437     getAutoCreate : function(){
7438         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7439         
7440         cfg = {
7441             tag: 'tr'
7442         };
7443             
7444         if(this.cls){
7445             cfg.cls = this.cls;
7446         }
7447         if(this.align){
7448             cfg.align = this.align;
7449         }
7450         if(this.bgcolor){
7451             cfg.bgcolor = this.bgcolor;
7452         }
7453         if(this.charoff){
7454             cfg.charoff = this.charoff;
7455         }
7456         if(this.valign){
7457             cfg.valign = this.valign;
7458         }
7459         
7460         return cfg;
7461     }
7462    
7463 });
7464
7465  
7466
7467  /*
7468  * - LGPL
7469  *
7470  * table body
7471  * 
7472  */
7473
7474 /**
7475  * @class Roo.bootstrap.TableBody
7476  * @extends Roo.bootstrap.Component
7477  * Bootstrap TableBody class
7478  * @cfg {String} cls element class
7479  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7480  * @cfg {String} align Aligns the content inside the element
7481  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7482  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7483  * 
7484  * @constructor
7485  * Create a new TableBody
7486  * @param {Object} config The config object
7487  */
7488
7489 Roo.bootstrap.TableBody = function(config){
7490     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7491 };
7492
7493 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7494     
7495     cls: false,
7496     tag: false,
7497     align: false,
7498     charoff: false,
7499     valign: false,
7500     
7501     getAutoCreate : function(){
7502         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7503         
7504         cfg = {
7505             tag: 'tbody'
7506         };
7507             
7508         if (this.cls) {
7509             cfg.cls=this.cls
7510         }
7511         if(this.tag){
7512             cfg.tag = this.tag;
7513         }
7514         
7515         if(this.align){
7516             cfg.align = this.align;
7517         }
7518         if(this.charoff){
7519             cfg.charoff = this.charoff;
7520         }
7521         if(this.valign){
7522             cfg.valign = this.valign;
7523         }
7524         
7525         return cfg;
7526     }
7527     
7528     
7529 //    initEvents : function()
7530 //    {
7531 //        
7532 //        if(!this.store){
7533 //            return;
7534 //        }
7535 //        
7536 //        this.store = Roo.factory(this.store, Roo.data);
7537 //        this.store.on('load', this.onLoad, this);
7538 //        
7539 //        this.store.load();
7540 //        
7541 //    },
7542 //    
7543 //    onLoad: function () 
7544 //    {   
7545 //        this.fireEvent('load', this);
7546 //    }
7547 //    
7548 //   
7549 });
7550
7551  
7552
7553  /*
7554  * Based on:
7555  * Ext JS Library 1.1.1
7556  * Copyright(c) 2006-2007, Ext JS, LLC.
7557  *
7558  * Originally Released Under LGPL - original licence link has changed is not relivant.
7559  *
7560  * Fork - LGPL
7561  * <script type="text/javascript">
7562  */
7563
7564 // as we use this in bootstrap.
7565 Roo.namespace('Roo.form');
7566  /**
7567  * @class Roo.form.Action
7568  * Internal Class used to handle form actions
7569  * @constructor
7570  * @param {Roo.form.BasicForm} el The form element or its id
7571  * @param {Object} config Configuration options
7572  */
7573
7574  
7575  
7576 // define the action interface
7577 Roo.form.Action = function(form, options){
7578     this.form = form;
7579     this.options = options || {};
7580 };
7581 /**
7582  * Client Validation Failed
7583  * @const 
7584  */
7585 Roo.form.Action.CLIENT_INVALID = 'client';
7586 /**
7587  * Server Validation Failed
7588  * @const 
7589  */
7590 Roo.form.Action.SERVER_INVALID = 'server';
7591  /**
7592  * Connect to Server Failed
7593  * @const 
7594  */
7595 Roo.form.Action.CONNECT_FAILURE = 'connect';
7596 /**
7597  * Reading Data from Server Failed
7598  * @const 
7599  */
7600 Roo.form.Action.LOAD_FAILURE = 'load';
7601
7602 Roo.form.Action.prototype = {
7603     type : 'default',
7604     failureType : undefined,
7605     response : undefined,
7606     result : undefined,
7607
7608     // interface method
7609     run : function(options){
7610
7611     },
7612
7613     // interface method
7614     success : function(response){
7615
7616     },
7617
7618     // interface method
7619     handleResponse : function(response){
7620
7621     },
7622
7623     // default connection failure
7624     failure : function(response){
7625         
7626         this.response = response;
7627         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7628         this.form.afterAction(this, false);
7629     },
7630
7631     processResponse : function(response){
7632         this.response = response;
7633         if(!response.responseText){
7634             return true;
7635         }
7636         this.result = this.handleResponse(response);
7637         return this.result;
7638     },
7639
7640     // utility functions used internally
7641     getUrl : function(appendParams){
7642         var url = this.options.url || this.form.url || this.form.el.dom.action;
7643         if(appendParams){
7644             var p = this.getParams();
7645             if(p){
7646                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7647             }
7648         }
7649         return url;
7650     },
7651
7652     getMethod : function(){
7653         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7654     },
7655
7656     getParams : function(){
7657         var bp = this.form.baseParams;
7658         var p = this.options.params;
7659         if(p){
7660             if(typeof p == "object"){
7661                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7662             }else if(typeof p == 'string' && bp){
7663                 p += '&' + Roo.urlEncode(bp);
7664             }
7665         }else if(bp){
7666             p = Roo.urlEncode(bp);
7667         }
7668         return p;
7669     },
7670
7671     createCallback : function(){
7672         return {
7673             success: this.success,
7674             failure: this.failure,
7675             scope: this,
7676             timeout: (this.form.timeout*1000),
7677             upload: this.form.fileUpload ? this.success : undefined
7678         };
7679     }
7680 };
7681
7682 Roo.form.Action.Submit = function(form, options){
7683     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7684 };
7685
7686 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7687     type : 'submit',
7688
7689     haveProgress : false,
7690     uploadComplete : false,
7691     
7692     // uploadProgress indicator.
7693     uploadProgress : function()
7694     {
7695         if (!this.form.progressUrl) {
7696             return;
7697         }
7698         
7699         if (!this.haveProgress) {
7700             Roo.MessageBox.progress("Uploading", "Uploading");
7701         }
7702         if (this.uploadComplete) {
7703            Roo.MessageBox.hide();
7704            return;
7705         }
7706         
7707         this.haveProgress = true;
7708    
7709         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7710         
7711         var c = new Roo.data.Connection();
7712         c.request({
7713             url : this.form.progressUrl,
7714             params: {
7715                 id : uid
7716             },
7717             method: 'GET',
7718             success : function(req){
7719                //console.log(data);
7720                 var rdata = false;
7721                 var edata;
7722                 try  {
7723                    rdata = Roo.decode(req.responseText)
7724                 } catch (e) {
7725                     Roo.log("Invalid data from server..");
7726                     Roo.log(edata);
7727                     return;
7728                 }
7729                 if (!rdata || !rdata.success) {
7730                     Roo.log(rdata);
7731                     Roo.MessageBox.alert(Roo.encode(rdata));
7732                     return;
7733                 }
7734                 var data = rdata.data;
7735                 
7736                 if (this.uploadComplete) {
7737                    Roo.MessageBox.hide();
7738                    return;
7739                 }
7740                    
7741                 if (data){
7742                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7743                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7744                     );
7745                 }
7746                 this.uploadProgress.defer(2000,this);
7747             },
7748        
7749             failure: function(data) {
7750                 Roo.log('progress url failed ');
7751                 Roo.log(data);
7752             },
7753             scope : this
7754         });
7755            
7756     },
7757     
7758     
7759     run : function()
7760     {
7761         // run get Values on the form, so it syncs any secondary forms.
7762         this.form.getValues();
7763         
7764         var o = this.options;
7765         var method = this.getMethod();
7766         var isPost = method == 'POST';
7767         if(o.clientValidation === false || this.form.isValid()){
7768             
7769             if (this.form.progressUrl) {
7770                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7771                     (new Date() * 1) + '' + Math.random());
7772                     
7773             } 
7774             
7775             
7776             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7777                 form:this.form.el.dom,
7778                 url:this.getUrl(!isPost),
7779                 method: method,
7780                 params:isPost ? this.getParams() : null,
7781                 isUpload: this.form.fileUpload
7782             }));
7783             
7784             this.uploadProgress();
7785
7786         }else if (o.clientValidation !== false){ // client validation failed
7787             this.failureType = Roo.form.Action.CLIENT_INVALID;
7788             this.form.afterAction(this, false);
7789         }
7790     },
7791
7792     success : function(response)
7793     {
7794         this.uploadComplete= true;
7795         if (this.haveProgress) {
7796             Roo.MessageBox.hide();
7797         }
7798         
7799         
7800         var result = this.processResponse(response);
7801         if(result === true || result.success){
7802             this.form.afterAction(this, true);
7803             return;
7804         }
7805         if(result.errors){
7806             this.form.markInvalid(result.errors);
7807             this.failureType = Roo.form.Action.SERVER_INVALID;
7808         }
7809         this.form.afterAction(this, false);
7810     },
7811     failure : function(response)
7812     {
7813         this.uploadComplete= true;
7814         if (this.haveProgress) {
7815             Roo.MessageBox.hide();
7816         }
7817         
7818         this.response = response;
7819         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7820         this.form.afterAction(this, false);
7821     },
7822     
7823     handleResponse : function(response){
7824         if(this.form.errorReader){
7825             var rs = this.form.errorReader.read(response);
7826             var errors = [];
7827             if(rs.records){
7828                 for(var i = 0, len = rs.records.length; i < len; i++) {
7829                     var r = rs.records[i];
7830                     errors[i] = r.data;
7831                 }
7832             }
7833             if(errors.length < 1){
7834                 errors = null;
7835             }
7836             return {
7837                 success : rs.success,
7838                 errors : errors
7839             };
7840         }
7841         var ret = false;
7842         try {
7843             ret = Roo.decode(response.responseText);
7844         } catch (e) {
7845             ret = {
7846                 success: false,
7847                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7848                 errors : []
7849             };
7850         }
7851         return ret;
7852         
7853     }
7854 });
7855
7856
7857 Roo.form.Action.Load = function(form, options){
7858     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7859     this.reader = this.form.reader;
7860 };
7861
7862 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7863     type : 'load',
7864
7865     run : function(){
7866         
7867         Roo.Ajax.request(Roo.apply(
7868                 this.createCallback(), {
7869                     method:this.getMethod(),
7870                     url:this.getUrl(false),
7871                     params:this.getParams()
7872         }));
7873     },
7874
7875     success : function(response){
7876         
7877         var result = this.processResponse(response);
7878         if(result === true || !result.success || !result.data){
7879             this.failureType = Roo.form.Action.LOAD_FAILURE;
7880             this.form.afterAction(this, false);
7881             return;
7882         }
7883         this.form.clearInvalid();
7884         this.form.setValues(result.data);
7885         this.form.afterAction(this, true);
7886     },
7887
7888     handleResponse : function(response){
7889         if(this.form.reader){
7890             var rs = this.form.reader.read(response);
7891             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7892             return {
7893                 success : rs.success,
7894                 data : data
7895             };
7896         }
7897         return Roo.decode(response.responseText);
7898     }
7899 });
7900
7901 Roo.form.Action.ACTION_TYPES = {
7902     'load' : Roo.form.Action.Load,
7903     'submit' : Roo.form.Action.Submit
7904 };/*
7905  * - LGPL
7906  *
7907  * form
7908  *
7909  */
7910
7911 /**
7912  * @class Roo.bootstrap.Form
7913  * @extends Roo.bootstrap.Component
7914  * Bootstrap Form class
7915  * @cfg {String} method  GET | POST (default POST)
7916  * @cfg {String} labelAlign top | left (default top)
7917  * @cfg {String} align left  | right - for navbars
7918  * @cfg {Boolean} loadMask load mask when submit (default true)
7919
7920  *
7921  * @constructor
7922  * Create a new Form
7923  * @param {Object} config The config object
7924  */
7925
7926
7927 Roo.bootstrap.Form = function(config){
7928     
7929     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7930     
7931     Roo.bootstrap.Form.popover.apply();
7932     
7933     this.addEvents({
7934         /**
7935          * @event clientvalidation
7936          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7937          * @param {Form} this
7938          * @param {Boolean} valid true if the form has passed client-side validation
7939          */
7940         clientvalidation: true,
7941         /**
7942          * @event beforeaction
7943          * Fires before any action is performed. Return false to cancel the action.
7944          * @param {Form} this
7945          * @param {Action} action The action to be performed
7946          */
7947         beforeaction: true,
7948         /**
7949          * @event actionfailed
7950          * Fires when an action fails.
7951          * @param {Form} this
7952          * @param {Action} action The action that failed
7953          */
7954         actionfailed : true,
7955         /**
7956          * @event actioncomplete
7957          * Fires when an action is completed.
7958          * @param {Form} this
7959          * @param {Action} action The action that completed
7960          */
7961         actioncomplete : true
7962     });
7963 };
7964
7965 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7966
7967      /**
7968      * @cfg {String} method
7969      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7970      */
7971     method : 'POST',
7972     /**
7973      * @cfg {String} url
7974      * The URL to use for form actions if one isn't supplied in the action options.
7975      */
7976     /**
7977      * @cfg {Boolean} fileUpload
7978      * Set to true if this form is a file upload.
7979      */
7980
7981     /**
7982      * @cfg {Object} baseParams
7983      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7984      */
7985
7986     /**
7987      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7988      */
7989     timeout: 30,
7990     /**
7991      * @cfg {Sting} align (left|right) for navbar forms
7992      */
7993     align : 'left',
7994
7995     // private
7996     activeAction : null,
7997
7998     /**
7999      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8000      * element by passing it or its id or mask the form itself by passing in true.
8001      * @type Mixed
8002      */
8003     waitMsgTarget : false,
8004
8005     loadMask : true,
8006     
8007     /**
8008      * @cfg {Boolean} errorMask (true|false) default false
8009      */
8010     errorMask : false,
8011     
8012     /**
8013      * @cfg {Number} maskOffset Default 100
8014      */
8015     maskOffset : 100,
8016     
8017     /**
8018      * @cfg {Boolean} maskBody
8019      */
8020     maskBody : false,
8021
8022     getAutoCreate : function(){
8023
8024         var cfg = {
8025             tag: 'form',
8026             method : this.method || 'POST',
8027             id : this.id || Roo.id(),
8028             cls : ''
8029         };
8030         if (this.parent().xtype.match(/^Nav/)) {
8031             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8032
8033         }
8034
8035         if (this.labelAlign == 'left' ) {
8036             cfg.cls += ' form-horizontal';
8037         }
8038
8039
8040         return cfg;
8041     },
8042     initEvents : function()
8043     {
8044         this.el.on('submit', this.onSubmit, this);
8045         // this was added as random key presses on the form where triggering form submit.
8046         this.el.on('keypress', function(e) {
8047             if (e.getCharCode() != 13) {
8048                 return true;
8049             }
8050             // we might need to allow it for textareas.. and some other items.
8051             // check e.getTarget().
8052
8053             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8054                 return true;
8055             }
8056
8057             Roo.log("keypress blocked");
8058
8059             e.preventDefault();
8060             return false;
8061         });
8062         
8063     },
8064     // private
8065     onSubmit : function(e){
8066         e.stopEvent();
8067     },
8068
8069      /**
8070      * Returns true if client-side validation on the form is successful.
8071      * @return Boolean
8072      */
8073     isValid : function(){
8074         var items = this.getItems();
8075         var valid = true;
8076         var target = false;
8077         
8078         items.each(function(f){
8079             
8080             if(f.validate()){
8081                 return;
8082             }
8083             
8084             Roo.log('invalid field: ' + f.name);
8085             
8086             valid = false;
8087
8088             if(!target && f.el.isVisible(true)){
8089                 target = f;
8090             }
8091            
8092         });
8093         
8094         if(this.errorMask && !valid){
8095             Roo.bootstrap.Form.popover.mask(this, target);
8096         }
8097         
8098         return valid;
8099     },
8100     
8101     /**
8102      * Returns true if any fields in this form have changed since their original load.
8103      * @return Boolean
8104      */
8105     isDirty : function(){
8106         var dirty = false;
8107         var items = this.getItems();
8108         items.each(function(f){
8109            if(f.isDirty()){
8110                dirty = true;
8111                return false;
8112            }
8113            return true;
8114         });
8115         return dirty;
8116     },
8117      /**
8118      * Performs a predefined action (submit or load) or custom actions you define on this form.
8119      * @param {String} actionName The name of the action type
8120      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8121      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8122      * accept other config options):
8123      * <pre>
8124 Property          Type             Description
8125 ----------------  ---------------  ----------------------------------------------------------------------------------
8126 url               String           The url for the action (defaults to the form's url)
8127 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8128 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8129 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8130                                    validate the form on the client (defaults to false)
8131      * </pre>
8132      * @return {BasicForm} this
8133      */
8134     doAction : function(action, options){
8135         if(typeof action == 'string'){
8136             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8137         }
8138         if(this.fireEvent('beforeaction', this, action) !== false){
8139             this.beforeAction(action);
8140             action.run.defer(100, action);
8141         }
8142         return this;
8143     },
8144
8145     // private
8146     beforeAction : function(action){
8147         var o = action.options;
8148         
8149         if(this.loadMask){
8150             
8151             if(this.maskBody){
8152                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8153             } else {
8154                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8155             }
8156         }
8157         // not really supported yet.. ??
8158
8159         //if(this.waitMsgTarget === true){
8160         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8161         //}else if(this.waitMsgTarget){
8162         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8163         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8164         //}else {
8165         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8166        // }
8167
8168     },
8169
8170     // private
8171     afterAction : function(action, success){
8172         this.activeAction = null;
8173         var o = action.options;
8174
8175         if(this.loadMask){
8176             
8177             if(this.maskBody){
8178                 Roo.get(document.body).unmask();
8179             } else {
8180                 this.el.unmask();
8181             }
8182         }
8183         
8184         //if(this.waitMsgTarget === true){
8185 //            this.el.unmask();
8186         //}else if(this.waitMsgTarget){
8187         //    this.waitMsgTarget.unmask();
8188         //}else{
8189         //    Roo.MessageBox.updateProgress(1);
8190         //    Roo.MessageBox.hide();
8191        // }
8192         //
8193         if(success){
8194             if(o.reset){
8195                 this.reset();
8196             }
8197             Roo.callback(o.success, o.scope, [this, action]);
8198             this.fireEvent('actioncomplete', this, action);
8199
8200         }else{
8201
8202             // failure condition..
8203             // we have a scenario where updates need confirming.
8204             // eg. if a locking scenario exists..
8205             // we look for { errors : { needs_confirm : true }} in the response.
8206             if (
8207                 (typeof(action.result) != 'undefined')  &&
8208                 (typeof(action.result.errors) != 'undefined')  &&
8209                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8210            ){
8211                 var _t = this;
8212                 Roo.log("not supported yet");
8213                  /*
8214
8215                 Roo.MessageBox.confirm(
8216                     "Change requires confirmation",
8217                     action.result.errorMsg,
8218                     function(r) {
8219                         if (r != 'yes') {
8220                             return;
8221                         }
8222                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8223                     }
8224
8225                 );
8226                 */
8227
8228
8229                 return;
8230             }
8231
8232             Roo.callback(o.failure, o.scope, [this, action]);
8233             // show an error message if no failed handler is set..
8234             if (!this.hasListener('actionfailed')) {
8235                 Roo.log("need to add dialog support");
8236                 /*
8237                 Roo.MessageBox.alert("Error",
8238                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8239                         action.result.errorMsg :
8240                         "Saving Failed, please check your entries or try again"
8241                 );
8242                 */
8243             }
8244
8245             this.fireEvent('actionfailed', this, action);
8246         }
8247
8248     },
8249     /**
8250      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8251      * @param {String} id The value to search for
8252      * @return Field
8253      */
8254     findField : function(id){
8255         var items = this.getItems();
8256         var field = items.get(id);
8257         if(!field){
8258              items.each(function(f){
8259                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8260                     field = f;
8261                     return false;
8262                 }
8263                 return true;
8264             });
8265         }
8266         return field || null;
8267     },
8268      /**
8269      * Mark fields in this form invalid in bulk.
8270      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8271      * @return {BasicForm} this
8272      */
8273     markInvalid : function(errors){
8274         if(errors instanceof Array){
8275             for(var i = 0, len = errors.length; i < len; i++){
8276                 var fieldError = errors[i];
8277                 var f = this.findField(fieldError.id);
8278                 if(f){
8279                     f.markInvalid(fieldError.msg);
8280                 }
8281             }
8282         }else{
8283             var field, id;
8284             for(id in errors){
8285                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8286                     field.markInvalid(errors[id]);
8287                 }
8288             }
8289         }
8290         //Roo.each(this.childForms || [], function (f) {
8291         //    f.markInvalid(errors);
8292         //});
8293
8294         return this;
8295     },
8296
8297     /**
8298      * Set values for fields in this form in bulk.
8299      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8300      * @return {BasicForm} this
8301      */
8302     setValues : function(values){
8303         if(values instanceof Array){ // array of objects
8304             for(var i = 0, len = values.length; i < len; i++){
8305                 var v = values[i];
8306                 var f = this.findField(v.id);
8307                 if(f){
8308                     f.setValue(v.value);
8309                     if(this.trackResetOnLoad){
8310                         f.originalValue = f.getValue();
8311                     }
8312                 }
8313             }
8314         }else{ // object hash
8315             var field, id;
8316             for(id in values){
8317                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8318
8319                     if (field.setFromData &&
8320                         field.valueField &&
8321                         field.displayField &&
8322                         // combos' with local stores can
8323                         // be queried via setValue()
8324                         // to set their value..
8325                         (field.store && !field.store.isLocal)
8326                         ) {
8327                         // it's a combo
8328                         var sd = { };
8329                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8330                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8331                         field.setFromData(sd);
8332
8333                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8334                         
8335                         field.setFromData(values);
8336                         
8337                     } else {
8338                         field.setValue(values[id]);
8339                     }
8340
8341
8342                     if(this.trackResetOnLoad){
8343                         field.originalValue = field.getValue();
8344                     }
8345                 }
8346             }
8347         }
8348
8349         //Roo.each(this.childForms || [], function (f) {
8350         //    f.setValues(values);
8351         //});
8352
8353         return this;
8354     },
8355
8356     /**
8357      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8358      * they are returned as an array.
8359      * @param {Boolean} asString
8360      * @return {Object}
8361      */
8362     getValues : function(asString){
8363         //if (this.childForms) {
8364             // copy values from the child forms
8365         //    Roo.each(this.childForms, function (f) {
8366         //        this.setValues(f.getValues());
8367         //    }, this);
8368         //}
8369
8370
8371
8372         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8373         if(asString === true){
8374             return fs;
8375         }
8376         return Roo.urlDecode(fs);
8377     },
8378
8379     /**
8380      * Returns the fields in this form as an object with key/value pairs.
8381      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8382      * @return {Object}
8383      */
8384     getFieldValues : function(with_hidden)
8385     {
8386         var items = this.getItems();
8387         var ret = {};
8388         items.each(function(f){
8389             
8390             if (!f.getName()) {
8391                 return;
8392             }
8393             
8394             var v = f.getValue();
8395             
8396             if (f.inputType =='radio') {
8397                 if (typeof(ret[f.getName()]) == 'undefined') {
8398                     ret[f.getName()] = ''; // empty..
8399                 }
8400
8401                 if (!f.el.dom.checked) {
8402                     return;
8403
8404                 }
8405                 v = f.el.dom.value;
8406
8407             }
8408             
8409             if(f.xtype == 'MoneyField'){
8410                 ret[f.currencyName] = f.getCurrency();
8411             }
8412
8413             // not sure if this supported any more..
8414             if ((typeof(v) == 'object') && f.getRawValue) {
8415                 v = f.getRawValue() ; // dates..
8416             }
8417             // combo boxes where name != hiddenName...
8418             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8419                 ret[f.name] = f.getRawValue();
8420             }
8421             ret[f.getName()] = v;
8422         });
8423
8424         return ret;
8425     },
8426
8427     /**
8428      * Clears all invalid messages in this form.
8429      * @return {BasicForm} this
8430      */
8431     clearInvalid : function(){
8432         var items = this.getItems();
8433
8434         items.each(function(f){
8435            f.clearInvalid();
8436         });
8437
8438         return this;
8439     },
8440
8441     /**
8442      * Resets this form.
8443      * @return {BasicForm} this
8444      */
8445     reset : function(){
8446         var items = this.getItems();
8447         items.each(function(f){
8448             f.reset();
8449         });
8450
8451         Roo.each(this.childForms || [], function (f) {
8452             f.reset();
8453         });
8454
8455
8456         return this;
8457     },
8458     
8459     getItems : function()
8460     {
8461         var r=new Roo.util.MixedCollection(false, function(o){
8462             return o.id || (o.id = Roo.id());
8463         });
8464         var iter = function(el) {
8465             if (el.inputEl) {
8466                 r.add(el);
8467             }
8468             if (!el.items) {
8469                 return;
8470             }
8471             Roo.each(el.items,function(e) {
8472                 iter(e);
8473             });
8474         };
8475
8476         iter(this);
8477         return r;
8478     },
8479     
8480     hideFields : function(items)
8481     {
8482         Roo.each(items, function(i){
8483             
8484             var f = this.findField(i);
8485             
8486             if(!f){
8487                 return;
8488             }
8489             
8490             f.hide();
8491             
8492         }, this);
8493     },
8494     
8495     showFields : function(items)
8496     {
8497         Roo.each(items, function(i){
8498             
8499             var f = this.findField(i);
8500             
8501             if(!f){
8502                 return;
8503             }
8504             
8505             f.show();
8506             
8507         }, this);
8508     }
8509
8510 });
8511
8512 Roo.apply(Roo.bootstrap.Form, {
8513     
8514     popover : {
8515         
8516         padding : 5,
8517         
8518         isApplied : false,
8519         
8520         isMasked : false,
8521         
8522         form : false,
8523         
8524         target : false,
8525         
8526         toolTip : false,
8527         
8528         intervalID : false,
8529         
8530         maskEl : false,
8531         
8532         apply : function()
8533         {
8534             if(this.isApplied){
8535                 return;
8536             }
8537             
8538             this.maskEl = {
8539                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8540                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8541                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8542                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8543             };
8544             
8545             this.maskEl.top.enableDisplayMode("block");
8546             this.maskEl.left.enableDisplayMode("block");
8547             this.maskEl.bottom.enableDisplayMode("block");
8548             this.maskEl.right.enableDisplayMode("block");
8549             
8550             this.toolTip = new Roo.bootstrap.Tooltip({
8551                 cls : 'roo-form-error-popover',
8552                 alignment : {
8553                     'left' : ['r-l', [-2,0], 'right'],
8554                     'right' : ['l-r', [2,0], 'left'],
8555                     'bottom' : ['tl-bl', [0,2], 'top'],
8556                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8557                 }
8558             });
8559             
8560             this.toolTip.render(Roo.get(document.body));
8561
8562             this.toolTip.el.enableDisplayMode("block");
8563             
8564             Roo.get(document.body).on('click', function(){
8565                 this.unmask();
8566             }, this);
8567             
8568             Roo.get(document.body).on('touchstart', function(){
8569                 this.unmask();
8570             }, this);
8571             
8572             this.isApplied = true
8573         },
8574         
8575         mask : function(form, target)
8576         {
8577             this.form = form;
8578             
8579             this.target = target;
8580             
8581             if(!this.form.errorMask || !target.el){
8582                 return;
8583             }
8584             
8585             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8586             
8587             Roo.log(scrollable);
8588             
8589             var ot = this.target.el.calcOffsetsTo(scrollable);
8590             
8591             var scrollTo = ot[1] - this.form.maskOffset;
8592             
8593             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8594             
8595             scrollable.scrollTo('top', scrollTo);
8596             
8597             var box = this.target.el.getBox();
8598             Roo.log(box);
8599             var zIndex = Roo.bootstrap.Modal.zIndex++;
8600
8601             
8602             this.maskEl.top.setStyle('position', 'absolute');
8603             this.maskEl.top.setStyle('z-index', zIndex);
8604             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8605             this.maskEl.top.setLeft(0);
8606             this.maskEl.top.setTop(0);
8607             this.maskEl.top.show();
8608             
8609             this.maskEl.left.setStyle('position', 'absolute');
8610             this.maskEl.left.setStyle('z-index', zIndex);
8611             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8612             this.maskEl.left.setLeft(0);
8613             this.maskEl.left.setTop(box.y - this.padding);
8614             this.maskEl.left.show();
8615
8616             this.maskEl.bottom.setStyle('position', 'absolute');
8617             this.maskEl.bottom.setStyle('z-index', zIndex);
8618             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8619             this.maskEl.bottom.setLeft(0);
8620             this.maskEl.bottom.setTop(box.bottom + this.padding);
8621             this.maskEl.bottom.show();
8622
8623             this.maskEl.right.setStyle('position', 'absolute');
8624             this.maskEl.right.setStyle('z-index', zIndex);
8625             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8626             this.maskEl.right.setLeft(box.right + this.padding);
8627             this.maskEl.right.setTop(box.y - this.padding);
8628             this.maskEl.right.show();
8629
8630             this.toolTip.bindEl = this.target.el;
8631
8632             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8633
8634             var tip = this.target.blankText;
8635
8636             if(this.target.getValue() !== '' ) {
8637                 
8638                 if (this.target.invalidText.length) {
8639                     tip = this.target.invalidText;
8640                 } else if (this.target.regexText.length){
8641                     tip = this.target.regexText;
8642                 }
8643             }
8644
8645             this.toolTip.show(tip);
8646
8647             this.intervalID = window.setInterval(function() {
8648                 Roo.bootstrap.Form.popover.unmask();
8649             }, 10000);
8650
8651             window.onwheel = function(){ return false;};
8652             
8653             (function(){ this.isMasked = true; }).defer(500, this);
8654             
8655         },
8656         
8657         unmask : function()
8658         {
8659             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8660                 return;
8661             }
8662             
8663             this.maskEl.top.setStyle('position', 'absolute');
8664             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8665             this.maskEl.top.hide();
8666
8667             this.maskEl.left.setStyle('position', 'absolute');
8668             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8669             this.maskEl.left.hide();
8670
8671             this.maskEl.bottom.setStyle('position', 'absolute');
8672             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8673             this.maskEl.bottom.hide();
8674
8675             this.maskEl.right.setStyle('position', 'absolute');
8676             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8677             this.maskEl.right.hide();
8678             
8679             this.toolTip.hide();
8680             
8681             this.toolTip.el.hide();
8682             
8683             window.onwheel = function(){ return true;};
8684             
8685             if(this.intervalID){
8686                 window.clearInterval(this.intervalID);
8687                 this.intervalID = false;
8688             }
8689             
8690             this.isMasked = false;
8691             
8692         }
8693         
8694     }
8695     
8696 });
8697
8698 /*
8699  * Based on:
8700  * Ext JS Library 1.1.1
8701  * Copyright(c) 2006-2007, Ext JS, LLC.
8702  *
8703  * Originally Released Under LGPL - original licence link has changed is not relivant.
8704  *
8705  * Fork - LGPL
8706  * <script type="text/javascript">
8707  */
8708 /**
8709  * @class Roo.form.VTypes
8710  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8711  * @singleton
8712  */
8713 Roo.form.VTypes = function(){
8714     // closure these in so they are only created once.
8715     var alpha = /^[a-zA-Z_]+$/;
8716     var alphanum = /^[a-zA-Z0-9_]+$/;
8717     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8718     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8719
8720     // All these messages and functions are configurable
8721     return {
8722         /**
8723          * The function used to validate email addresses
8724          * @param {String} value The email address
8725          */
8726         'email' : function(v){
8727             return email.test(v);
8728         },
8729         /**
8730          * The error text to display when the email validation function returns false
8731          * @type String
8732          */
8733         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8734         /**
8735          * The keystroke filter mask to be applied on email input
8736          * @type RegExp
8737          */
8738         'emailMask' : /[a-z0-9_\.\-@]/i,
8739
8740         /**
8741          * The function used to validate URLs
8742          * @param {String} value The URL
8743          */
8744         'url' : function(v){
8745             return url.test(v);
8746         },
8747         /**
8748          * The error text to display when the url validation function returns false
8749          * @type String
8750          */
8751         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8752         
8753         /**
8754          * The function used to validate alpha values
8755          * @param {String} value The value
8756          */
8757         'alpha' : function(v){
8758             return alpha.test(v);
8759         },
8760         /**
8761          * The error text to display when the alpha validation function returns false
8762          * @type String
8763          */
8764         'alphaText' : 'This field should only contain letters and _',
8765         /**
8766          * The keystroke filter mask to be applied on alpha input
8767          * @type RegExp
8768          */
8769         'alphaMask' : /[a-z_]/i,
8770
8771         /**
8772          * The function used to validate alphanumeric values
8773          * @param {String} value The value
8774          */
8775         'alphanum' : function(v){
8776             return alphanum.test(v);
8777         },
8778         /**
8779          * The error text to display when the alphanumeric validation function returns false
8780          * @type String
8781          */
8782         'alphanumText' : 'This field should only contain letters, numbers and _',
8783         /**
8784          * The keystroke filter mask to be applied on alphanumeric input
8785          * @type RegExp
8786          */
8787         'alphanumMask' : /[a-z0-9_]/i
8788     };
8789 }();/*
8790  * - LGPL
8791  *
8792  * Input
8793  * 
8794  */
8795
8796 /**
8797  * @class Roo.bootstrap.Input
8798  * @extends Roo.bootstrap.Component
8799  * Bootstrap Input class
8800  * @cfg {Boolean} disabled is it disabled
8801  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8802  * @cfg {String} name name of the input
8803  * @cfg {string} fieldLabel - the label associated
8804  * @cfg {string} placeholder - placeholder to put in text.
8805  * @cfg {string}  before - input group add on before
8806  * @cfg {string} after - input group add on after
8807  * @cfg {string} size - (lg|sm) or leave empty..
8808  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8809  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8810  * @cfg {Number} md colspan out of 12 for computer-sized screens
8811  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8812  * @cfg {string} value default value of the input
8813  * @cfg {Number} labelWidth set the width of label 
8814  * @cfg {Number} labellg set the width of label (1-12)
8815  * @cfg {Number} labelmd set the width of label (1-12)
8816  * @cfg {Number} labelsm set the width of label (1-12)
8817  * @cfg {Number} labelxs set the width of label (1-12)
8818  * @cfg {String} labelAlign (top|left)
8819  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8820  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8821  * @cfg {String} indicatorpos (left|right) default left
8822  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8823  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8824
8825  * @cfg {String} align (left|center|right) Default left
8826  * @cfg {Boolean} forceFeedback (true|false) Default false
8827  * 
8828  * @constructor
8829  * Create a new Input
8830  * @param {Object} config The config object
8831  */
8832
8833 Roo.bootstrap.Input = function(config){
8834     
8835     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8836     
8837     this.addEvents({
8838         /**
8839          * @event focus
8840          * Fires when this field receives input focus.
8841          * @param {Roo.form.Field} this
8842          */
8843         focus : true,
8844         /**
8845          * @event blur
8846          * Fires when this field loses input focus.
8847          * @param {Roo.form.Field} this
8848          */
8849         blur : true,
8850         /**
8851          * @event specialkey
8852          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8853          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8854          * @param {Roo.form.Field} this
8855          * @param {Roo.EventObject} e The event object
8856          */
8857         specialkey : true,
8858         /**
8859          * @event change
8860          * Fires just before the field blurs if the field value has changed.
8861          * @param {Roo.form.Field} this
8862          * @param {Mixed} newValue The new value
8863          * @param {Mixed} oldValue The original value
8864          */
8865         change : true,
8866         /**
8867          * @event invalid
8868          * Fires after the field has been marked as invalid.
8869          * @param {Roo.form.Field} this
8870          * @param {String} msg The validation message
8871          */
8872         invalid : true,
8873         /**
8874          * @event valid
8875          * Fires after the field has been validated with no errors.
8876          * @param {Roo.form.Field} this
8877          */
8878         valid : true,
8879          /**
8880          * @event keyup
8881          * Fires after the key up
8882          * @param {Roo.form.Field} this
8883          * @param {Roo.EventObject}  e The event Object
8884          */
8885         keyup : true
8886     });
8887 };
8888
8889 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8890      /**
8891      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8892       automatic validation (defaults to "keyup").
8893      */
8894     validationEvent : "keyup",
8895      /**
8896      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8897      */
8898     validateOnBlur : true,
8899     /**
8900      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8901      */
8902     validationDelay : 250,
8903      /**
8904      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8905      */
8906     focusClass : "x-form-focus",  // not needed???
8907     
8908        
8909     /**
8910      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8911      */
8912     invalidClass : "has-warning",
8913     
8914     /**
8915      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8916      */
8917     validClass : "has-success",
8918     
8919     /**
8920      * @cfg {Boolean} hasFeedback (true|false) default true
8921      */
8922     hasFeedback : true,
8923     
8924     /**
8925      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8926      */
8927     invalidFeedbackClass : "glyphicon-warning-sign",
8928     
8929     /**
8930      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8931      */
8932     validFeedbackClass : "glyphicon-ok",
8933     
8934     /**
8935      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8936      */
8937     selectOnFocus : false,
8938     
8939      /**
8940      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8941      */
8942     maskRe : null,
8943        /**
8944      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8945      */
8946     vtype : null,
8947     
8948       /**
8949      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8950      */
8951     disableKeyFilter : false,
8952     
8953        /**
8954      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8955      */
8956     disabled : false,
8957      /**
8958      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8959      */
8960     allowBlank : true,
8961     /**
8962      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8963      */
8964     blankText : "Please complete this mandatory field",
8965     
8966      /**
8967      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8968      */
8969     minLength : 0,
8970     /**
8971      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8972      */
8973     maxLength : Number.MAX_VALUE,
8974     /**
8975      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8976      */
8977     minLengthText : "The minimum length for this field is {0}",
8978     /**
8979      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8980      */
8981     maxLengthText : "The maximum length for this field is {0}",
8982   
8983     
8984     /**
8985      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8986      * If available, this function will be called only after the basic validators all return true, and will be passed the
8987      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8988      */
8989     validator : null,
8990     /**
8991      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8992      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8993      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8994      */
8995     regex : null,
8996     /**
8997      * @cfg {String} regexText -- Depricated - use Invalid Text
8998      */
8999     regexText : "",
9000     
9001     /**
9002      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9003      */
9004     invalidText : "",
9005     
9006     
9007     
9008     autocomplete: false,
9009     
9010     
9011     fieldLabel : '',
9012     inputType : 'text',
9013     
9014     name : false,
9015     placeholder: false,
9016     before : false,
9017     after : false,
9018     size : false,
9019     hasFocus : false,
9020     preventMark: false,
9021     isFormField : true,
9022     value : '',
9023     labelWidth : 2,
9024     labelAlign : false,
9025     readOnly : false,
9026     align : false,
9027     formatedValue : false,
9028     forceFeedback : false,
9029     
9030     indicatorpos : 'left',
9031     
9032     labellg : 0,
9033     labelmd : 0,
9034     labelsm : 0,
9035     labelxs : 0,
9036     
9037     capture : '',
9038     accept : '',
9039     
9040     parentLabelAlign : function()
9041     {
9042         var parent = this;
9043         while (parent.parent()) {
9044             parent = parent.parent();
9045             if (typeof(parent.labelAlign) !='undefined') {
9046                 return parent.labelAlign;
9047             }
9048         }
9049         return 'left';
9050         
9051     },
9052     
9053     getAutoCreate : function()
9054     {
9055         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9056         
9057         var id = Roo.id();
9058         
9059         var cfg = {};
9060         
9061         if(this.inputType != 'hidden'){
9062             cfg.cls = 'form-group' //input-group
9063         }
9064         
9065         var input =  {
9066             tag: 'input',
9067             id : id,
9068             type : this.inputType,
9069             value : this.value,
9070             cls : 'form-control',
9071             placeholder : this.placeholder || '',
9072             autocomplete : this.autocomplete || 'new-password'
9073         };
9074         
9075         if(this.capture.length){
9076             input.capture = this.capture;
9077         }
9078         
9079         if(this.accept.length){
9080             input.accept = this.accept + "/*";
9081         }
9082         
9083         if(this.align){
9084             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9085         }
9086         
9087         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9088             input.maxLength = this.maxLength;
9089         }
9090         
9091         if (this.disabled) {
9092             input.disabled=true;
9093         }
9094         
9095         if (this.readOnly) {
9096             input.readonly=true;
9097         }
9098         
9099         if (this.name) {
9100             input.name = this.name;
9101         }
9102         
9103         if (this.size) {
9104             input.cls += ' input-' + this.size;
9105         }
9106         
9107         var settings=this;
9108         ['xs','sm','md','lg'].map(function(size){
9109             if (settings[size]) {
9110                 cfg.cls += ' col-' + size + '-' + settings[size];
9111             }
9112         });
9113         
9114         var inputblock = input;
9115         
9116         var feedback = {
9117             tag: 'span',
9118             cls: 'glyphicon form-control-feedback'
9119         };
9120             
9121         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9122             
9123             inputblock = {
9124                 cls : 'has-feedback',
9125                 cn :  [
9126                     input,
9127                     feedback
9128                 ] 
9129             };  
9130         }
9131         
9132         if (this.before || this.after) {
9133             
9134             inputblock = {
9135                 cls : 'input-group',
9136                 cn :  [] 
9137             };
9138             
9139             if (this.before && typeof(this.before) == 'string') {
9140                 
9141                 inputblock.cn.push({
9142                     tag :'span',
9143                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9144                     html : this.before
9145                 });
9146             }
9147             if (this.before && typeof(this.before) == 'object') {
9148                 this.before = Roo.factory(this.before);
9149                 
9150                 inputblock.cn.push({
9151                     tag :'span',
9152                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9153                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9154                 });
9155             }
9156             
9157             inputblock.cn.push(input);
9158             
9159             if (this.after && typeof(this.after) == 'string') {
9160                 inputblock.cn.push({
9161                     tag :'span',
9162                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9163                     html : this.after
9164                 });
9165             }
9166             if (this.after && typeof(this.after) == 'object') {
9167                 this.after = Roo.factory(this.after);
9168                 
9169                 inputblock.cn.push({
9170                     tag :'span',
9171                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9172                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9173                 });
9174             }
9175             
9176             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9177                 inputblock.cls += ' has-feedback';
9178                 inputblock.cn.push(feedback);
9179             }
9180         };
9181         var indicator = {
9182             tag : 'i',
9183             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9184             tooltip : 'This field is required'
9185         };
9186         if (Roo.bootstrap.version == 4) {
9187             indicator = {
9188                 tag : 'i',
9189                 style : 'display-none'
9190             };
9191         }
9192         if (align ==='left' && this.fieldLabel.length) {
9193             
9194             cfg.cls += ' roo-form-group-label-left row';
9195             
9196             cfg.cn = [
9197                 indicator,
9198                 {
9199                     tag: 'label',
9200                     'for' :  id,
9201                     cls : 'control-label col-form-label',
9202                     html : this.fieldLabel
9203
9204                 },
9205                 {
9206                     cls : "", 
9207                     cn: [
9208                         inputblock
9209                     ]
9210                 }
9211             ];
9212             
9213             var labelCfg = cfg.cn[1];
9214             var contentCfg = cfg.cn[2];
9215             
9216             if(this.indicatorpos == 'right'){
9217                 cfg.cn = [
9218                     {
9219                         tag: 'label',
9220                         'for' :  id,
9221                         cls : 'control-label col-form-label',
9222                         cn : [
9223                             {
9224                                 tag : 'span',
9225                                 html : this.fieldLabel
9226                             },
9227                             indicator
9228                         ]
9229                     },
9230                     {
9231                         cls : "",
9232                         cn: [
9233                             inputblock
9234                         ]
9235                     }
9236
9237                 ];
9238                 
9239                 labelCfg = cfg.cn[0];
9240                 contentCfg = cfg.cn[1];
9241             
9242             }
9243             
9244             if(this.labelWidth > 12){
9245                 labelCfg.style = "width: " + this.labelWidth + 'px';
9246             }
9247             
9248             if(this.labelWidth < 13 && this.labelmd == 0){
9249                 this.labelmd = this.labelWidth;
9250             }
9251             
9252             if(this.labellg > 0){
9253                 labelCfg.cls += ' col-lg-' + this.labellg;
9254                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9255             }
9256             
9257             if(this.labelmd > 0){
9258                 labelCfg.cls += ' col-md-' + this.labelmd;
9259                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9260             }
9261             
9262             if(this.labelsm > 0){
9263                 labelCfg.cls += ' col-sm-' + this.labelsm;
9264                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9265             }
9266             
9267             if(this.labelxs > 0){
9268                 labelCfg.cls += ' col-xs-' + this.labelxs;
9269                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9270             }
9271             
9272             
9273         } else if ( this.fieldLabel.length) {
9274                 
9275             cfg.cn = [
9276                 {
9277                     tag : 'i',
9278                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9279                     tooltip : 'This field is required'
9280                 },
9281                 {
9282                     tag: 'label',
9283                    //cls : 'input-group-addon',
9284                     html : this.fieldLabel
9285
9286                 },
9287
9288                inputblock
9289
9290            ];
9291            
9292            if(this.indicatorpos == 'right'){
9293                 
9294                 cfg.cn = [
9295                     {
9296                         tag: 'label',
9297                        //cls : 'input-group-addon',
9298                         html : this.fieldLabel
9299
9300                     },
9301                     {
9302                         tag : 'i',
9303                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9304                         tooltip : 'This field is required'
9305                     },
9306
9307                    inputblock
9308
9309                ];
9310
9311             }
9312
9313         } else {
9314             
9315             cfg.cn = [
9316
9317                     inputblock
9318
9319             ];
9320                 
9321                 
9322         };
9323         
9324         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9325            cfg.cls += ' navbar-form';
9326         }
9327         
9328         if (this.parentType === 'NavGroup') {
9329            cfg.cls += ' navbar-form';
9330            cfg.tag = 'li';
9331         }
9332         
9333         return cfg;
9334         
9335     },
9336     /**
9337      * return the real input element.
9338      */
9339     inputEl: function ()
9340     {
9341         return this.el.select('input.form-control',true).first();
9342     },
9343     
9344     tooltipEl : function()
9345     {
9346         return this.inputEl();
9347     },
9348     
9349     indicatorEl : function()
9350     {
9351         if (Roo.bootstrap.version == 4) {
9352             return false; // not enabled in v4 yet.
9353         }
9354         
9355         var indicator = this.el.select('i.roo-required-indicator',true).first();
9356         
9357         if(!indicator){
9358             return false;
9359         }
9360         
9361         return indicator;
9362         
9363     },
9364     
9365     setDisabled : function(v)
9366     {
9367         var i  = this.inputEl().dom;
9368         if (!v) {
9369             i.removeAttribute('disabled');
9370             return;
9371             
9372         }
9373         i.setAttribute('disabled','true');
9374     },
9375     initEvents : function()
9376     {
9377           
9378         this.inputEl().on("keydown" , this.fireKey,  this);
9379         this.inputEl().on("focus", this.onFocus,  this);
9380         this.inputEl().on("blur", this.onBlur,  this);
9381         
9382         this.inputEl().relayEvent('keyup', this);
9383         
9384         this.indicator = this.indicatorEl();
9385         
9386         if(this.indicator){
9387             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9388         }
9389  
9390         // reference to original value for reset
9391         this.originalValue = this.getValue();
9392         //Roo.form.TextField.superclass.initEvents.call(this);
9393         if(this.validationEvent == 'keyup'){
9394             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9395             this.inputEl().on('keyup', this.filterValidation, this);
9396         }
9397         else if(this.validationEvent !== false){
9398             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9399         }
9400         
9401         if(this.selectOnFocus){
9402             this.on("focus", this.preFocus, this);
9403             
9404         }
9405         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9406             this.inputEl().on("keypress", this.filterKeys, this);
9407         } else {
9408             this.inputEl().relayEvent('keypress', this);
9409         }
9410        /* if(this.grow){
9411             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9412             this.el.on("click", this.autoSize,  this);
9413         }
9414         */
9415         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9416             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9417         }
9418         
9419         if (typeof(this.before) == 'object') {
9420             this.before.render(this.el.select('.roo-input-before',true).first());
9421         }
9422         if (typeof(this.after) == 'object') {
9423             this.after.render(this.el.select('.roo-input-after',true).first());
9424         }
9425         
9426         this.inputEl().on('change', this.onChange, this);
9427         
9428     },
9429     filterValidation : function(e){
9430         if(!e.isNavKeyPress()){
9431             this.validationTask.delay(this.validationDelay);
9432         }
9433     },
9434      /**
9435      * Validates the field value
9436      * @return {Boolean} True if the value is valid, else false
9437      */
9438     validate : function(){
9439         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9440         if(this.disabled || this.validateValue(this.getRawValue())){
9441             this.markValid();
9442             return true;
9443         }
9444         
9445         this.markInvalid();
9446         return false;
9447     },
9448     
9449     
9450     /**
9451      * Validates a value according to the field's validation rules and marks the field as invalid
9452      * if the validation fails
9453      * @param {Mixed} value The value to validate
9454      * @return {Boolean} True if the value is valid, else false
9455      */
9456     validateValue : function(value)
9457     {
9458         if(this.getVisibilityEl().hasClass('hidden')){
9459             return true;
9460         }
9461         
9462         if(value.length < 1)  { // if it's blank
9463             if(this.allowBlank){
9464                 return true;
9465             }
9466             return false;
9467         }
9468         
9469         if(value.length < this.minLength){
9470             return false;
9471         }
9472         if(value.length > this.maxLength){
9473             return false;
9474         }
9475         if(this.vtype){
9476             var vt = Roo.form.VTypes;
9477             if(!vt[this.vtype](value, this)){
9478                 return false;
9479             }
9480         }
9481         if(typeof this.validator == "function"){
9482             var msg = this.validator(value);
9483             if(msg !== true){
9484                 return false;
9485             }
9486             if (typeof(msg) == 'string') {
9487                 this.invalidText = msg;
9488             }
9489         }
9490         
9491         if(this.regex && !this.regex.test(value)){
9492             return false;
9493         }
9494         
9495         return true;
9496     },
9497     
9498      // private
9499     fireKey : function(e){
9500         //Roo.log('field ' + e.getKey());
9501         if(e.isNavKeyPress()){
9502             this.fireEvent("specialkey", this, e);
9503         }
9504     },
9505     focus : function (selectText){
9506         if(this.rendered){
9507             this.inputEl().focus();
9508             if(selectText === true){
9509                 this.inputEl().dom.select();
9510             }
9511         }
9512         return this;
9513     } ,
9514     
9515     onFocus : function(){
9516         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9517            // this.el.addClass(this.focusClass);
9518         }
9519         if(!this.hasFocus){
9520             this.hasFocus = true;
9521             this.startValue = this.getValue();
9522             this.fireEvent("focus", this);
9523         }
9524     },
9525     
9526     beforeBlur : Roo.emptyFn,
9527
9528     
9529     // private
9530     onBlur : function(){
9531         this.beforeBlur();
9532         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9533             //this.el.removeClass(this.focusClass);
9534         }
9535         this.hasFocus = false;
9536         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9537             this.validate();
9538         }
9539         var v = this.getValue();
9540         if(String(v) !== String(this.startValue)){
9541             this.fireEvent('change', this, v, this.startValue);
9542         }
9543         this.fireEvent("blur", this);
9544     },
9545     
9546     onChange : function(e)
9547     {
9548         var v = this.getValue();
9549         if(String(v) !== String(this.startValue)){
9550             this.fireEvent('change', this, v, this.startValue);
9551         }
9552         
9553     },
9554     
9555     /**
9556      * Resets the current field value to the originally loaded value and clears any validation messages
9557      */
9558     reset : function(){
9559         this.setValue(this.originalValue);
9560         this.validate();
9561     },
9562      /**
9563      * Returns the name of the field
9564      * @return {Mixed} name The name field
9565      */
9566     getName: function(){
9567         return this.name;
9568     },
9569      /**
9570      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9571      * @return {Mixed} value The field value
9572      */
9573     getValue : function(){
9574         
9575         var v = this.inputEl().getValue();
9576         
9577         return v;
9578     },
9579     /**
9580      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9581      * @return {Mixed} value The field value
9582      */
9583     getRawValue : function(){
9584         var v = this.inputEl().getValue();
9585         
9586         return v;
9587     },
9588     
9589     /**
9590      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9591      * @param {Mixed} value The value to set
9592      */
9593     setRawValue : function(v){
9594         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9595     },
9596     
9597     selectText : function(start, end){
9598         var v = this.getRawValue();
9599         if(v.length > 0){
9600             start = start === undefined ? 0 : start;
9601             end = end === undefined ? v.length : end;
9602             var d = this.inputEl().dom;
9603             if(d.setSelectionRange){
9604                 d.setSelectionRange(start, end);
9605             }else if(d.createTextRange){
9606                 var range = d.createTextRange();
9607                 range.moveStart("character", start);
9608                 range.moveEnd("character", v.length-end);
9609                 range.select();
9610             }
9611         }
9612     },
9613     
9614     /**
9615      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9616      * @param {Mixed} value The value to set
9617      */
9618     setValue : function(v){
9619         this.value = v;
9620         if(this.rendered){
9621             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9622             this.validate();
9623         }
9624     },
9625     
9626     /*
9627     processValue : function(value){
9628         if(this.stripCharsRe){
9629             var newValue = value.replace(this.stripCharsRe, '');
9630             if(newValue !== value){
9631                 this.setRawValue(newValue);
9632                 return newValue;
9633             }
9634         }
9635         return value;
9636     },
9637   */
9638     preFocus : function(){
9639         
9640         if(this.selectOnFocus){
9641             this.inputEl().dom.select();
9642         }
9643     },
9644     filterKeys : function(e){
9645         var k = e.getKey();
9646         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9647             return;
9648         }
9649         var c = e.getCharCode(), cc = String.fromCharCode(c);
9650         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9651             return;
9652         }
9653         if(!this.maskRe.test(cc)){
9654             e.stopEvent();
9655         }
9656     },
9657      /**
9658      * Clear any invalid styles/messages for this field
9659      */
9660     clearInvalid : function(){
9661         
9662         if(!this.el || this.preventMark){ // not rendered
9663             return;
9664         }
9665         
9666      
9667         this.el.removeClass(this.invalidClass);
9668         
9669         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9670             
9671             var feedback = this.el.select('.form-control-feedback', true).first();
9672             
9673             if(feedback){
9674                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9675             }
9676             
9677         }
9678         
9679         if(this.indicator){
9680             this.indicator.removeClass('visible');
9681             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9682         }
9683         
9684         this.fireEvent('valid', this);
9685     },
9686     
9687      /**
9688      * Mark this field as valid
9689      */
9690     markValid : function()
9691     {
9692         if(!this.el  || this.preventMark){ // not rendered...
9693             return;
9694         }
9695         
9696         this.el.removeClass([this.invalidClass, this.validClass]);
9697         
9698         var feedback = this.el.select('.form-control-feedback', true).first();
9699             
9700         if(feedback){
9701             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9702         }
9703         
9704         if(this.indicator){
9705             this.indicator.removeClass('visible');
9706             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9707         }
9708         
9709         if(this.disabled){
9710             return;
9711         }
9712         
9713         if(this.allowBlank && !this.getRawValue().length){
9714             return;
9715         }
9716         
9717         this.el.addClass(this.validClass);
9718         
9719         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9720             
9721             var feedback = this.el.select('.form-control-feedback', true).first();
9722             
9723             if(feedback){
9724                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9725                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9726             }
9727             
9728         }
9729         
9730         this.fireEvent('valid', this);
9731     },
9732     
9733      /**
9734      * Mark this field as invalid
9735      * @param {String} msg The validation message
9736      */
9737     markInvalid : function(msg)
9738     {
9739         if(!this.el  || this.preventMark){ // not rendered
9740             return;
9741         }
9742         
9743         this.el.removeClass([this.invalidClass, this.validClass]);
9744         
9745         var feedback = this.el.select('.form-control-feedback', true).first();
9746             
9747         if(feedback){
9748             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9749         }
9750
9751         if(this.disabled){
9752             return;
9753         }
9754         
9755         if(this.allowBlank && !this.getRawValue().length){
9756             return;
9757         }
9758         
9759         if(this.indicator){
9760             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9761             this.indicator.addClass('visible');
9762         }
9763         
9764         this.el.addClass(this.invalidClass);
9765         
9766         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9767             
9768             var feedback = this.el.select('.form-control-feedback', true).first();
9769             
9770             if(feedback){
9771                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9772                 
9773                 if(this.getValue().length || this.forceFeedback){
9774                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9775                 }
9776                 
9777             }
9778             
9779         }
9780         
9781         this.fireEvent('invalid', this, msg);
9782     },
9783     // private
9784     SafariOnKeyDown : function(event)
9785     {
9786         // this is a workaround for a password hang bug on chrome/ webkit.
9787         if (this.inputEl().dom.type != 'password') {
9788             return;
9789         }
9790         
9791         var isSelectAll = false;
9792         
9793         if(this.inputEl().dom.selectionEnd > 0){
9794             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9795         }
9796         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9797             event.preventDefault();
9798             this.setValue('');
9799             return;
9800         }
9801         
9802         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9803             
9804             event.preventDefault();
9805             // this is very hacky as keydown always get's upper case.
9806             //
9807             var cc = String.fromCharCode(event.getCharCode());
9808             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9809             
9810         }
9811     },
9812     adjustWidth : function(tag, w){
9813         tag = tag.toLowerCase();
9814         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9815             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9816                 if(tag == 'input'){
9817                     return w + 2;
9818                 }
9819                 if(tag == 'textarea'){
9820                     return w-2;
9821                 }
9822             }else if(Roo.isOpera){
9823                 if(tag == 'input'){
9824                     return w + 2;
9825                 }
9826                 if(tag == 'textarea'){
9827                     return w-2;
9828                 }
9829             }
9830         }
9831         return w;
9832     },
9833     
9834     setFieldLabel : function(v)
9835     {
9836         if(!this.rendered){
9837             return;
9838         }
9839         
9840         if(this.indicatorEl()){
9841             var ar = this.el.select('label > span',true);
9842             
9843             if (ar.elements.length) {
9844                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9845                 this.fieldLabel = v;
9846                 return;
9847             }
9848             
9849             var br = this.el.select('label',true);
9850             
9851             if(br.elements.length) {
9852                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9853                 this.fieldLabel = v;
9854                 return;
9855             }
9856             
9857             Roo.log('Cannot Found any of label > span || label in input');
9858             return;
9859         }
9860         
9861         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9862         this.fieldLabel = v;
9863         
9864         
9865     }
9866 });
9867
9868  
9869 /*
9870  * - LGPL
9871  *
9872  * Input
9873  * 
9874  */
9875
9876 /**
9877  * @class Roo.bootstrap.TextArea
9878  * @extends Roo.bootstrap.Input
9879  * Bootstrap TextArea class
9880  * @cfg {Number} cols Specifies the visible width of a text area
9881  * @cfg {Number} rows Specifies the visible number of lines in a text area
9882  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9883  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9884  * @cfg {string} html text
9885  * 
9886  * @constructor
9887  * Create a new TextArea
9888  * @param {Object} config The config object
9889  */
9890
9891 Roo.bootstrap.TextArea = function(config){
9892     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9893    
9894 };
9895
9896 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9897      
9898     cols : false,
9899     rows : 5,
9900     readOnly : false,
9901     warp : 'soft',
9902     resize : false,
9903     value: false,
9904     html: false,
9905     
9906     getAutoCreate : function(){
9907         
9908         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9909         
9910         var id = Roo.id();
9911         
9912         var cfg = {};
9913         
9914         if(this.inputType != 'hidden'){
9915             cfg.cls = 'form-group' //input-group
9916         }
9917         
9918         var input =  {
9919             tag: 'textarea',
9920             id : id,
9921             warp : this.warp,
9922             rows : this.rows,
9923             value : this.value || '',
9924             html: this.html || '',
9925             cls : 'form-control',
9926             placeholder : this.placeholder || '' 
9927             
9928         };
9929         
9930         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9931             input.maxLength = this.maxLength;
9932         }
9933         
9934         if(this.resize){
9935             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9936         }
9937         
9938         if(this.cols){
9939             input.cols = this.cols;
9940         }
9941         
9942         if (this.readOnly) {
9943             input.readonly = true;
9944         }
9945         
9946         if (this.name) {
9947             input.name = this.name;
9948         }
9949         
9950         if (this.size) {
9951             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9952         }
9953         
9954         var settings=this;
9955         ['xs','sm','md','lg'].map(function(size){
9956             if (settings[size]) {
9957                 cfg.cls += ' col-' + size + '-' + settings[size];
9958             }
9959         });
9960         
9961         var inputblock = input;
9962         
9963         if(this.hasFeedback && !this.allowBlank){
9964             
9965             var feedback = {
9966                 tag: 'span',
9967                 cls: 'glyphicon form-control-feedback'
9968             };
9969
9970             inputblock = {
9971                 cls : 'has-feedback',
9972                 cn :  [
9973                     input,
9974                     feedback
9975                 ] 
9976             };  
9977         }
9978         
9979         
9980         if (this.before || this.after) {
9981             
9982             inputblock = {
9983                 cls : 'input-group',
9984                 cn :  [] 
9985             };
9986             if (this.before) {
9987                 inputblock.cn.push({
9988                     tag :'span',
9989                     cls : 'input-group-addon',
9990                     html : this.before
9991                 });
9992             }
9993             
9994             inputblock.cn.push(input);
9995             
9996             if(this.hasFeedback && !this.allowBlank){
9997                 inputblock.cls += ' has-feedback';
9998                 inputblock.cn.push(feedback);
9999             }
10000             
10001             if (this.after) {
10002                 inputblock.cn.push({
10003                     tag :'span',
10004                     cls : 'input-group-addon',
10005                     html : this.after
10006                 });
10007             }
10008             
10009         }
10010         
10011         if (align ==='left' && this.fieldLabel.length) {
10012             cfg.cn = [
10013                 {
10014                     tag: 'label',
10015                     'for' :  id,
10016                     cls : 'control-label',
10017                     html : this.fieldLabel
10018                 },
10019                 {
10020                     cls : "",
10021                     cn: [
10022                         inputblock
10023                     ]
10024                 }
10025
10026             ];
10027             
10028             if(this.labelWidth > 12){
10029                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10030             }
10031
10032             if(this.labelWidth < 13 && this.labelmd == 0){
10033                 this.labelmd = this.labelWidth;
10034             }
10035
10036             if(this.labellg > 0){
10037                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10038                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10039             }
10040
10041             if(this.labelmd > 0){
10042                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10043                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10044             }
10045
10046             if(this.labelsm > 0){
10047                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10048                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10049             }
10050
10051             if(this.labelxs > 0){
10052                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10053                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10054             }
10055             
10056         } else if ( this.fieldLabel.length) {
10057             cfg.cn = [
10058
10059                {
10060                    tag: 'label',
10061                    //cls : 'input-group-addon',
10062                    html : this.fieldLabel
10063
10064                },
10065
10066                inputblock
10067
10068            ];
10069
10070         } else {
10071
10072             cfg.cn = [
10073
10074                 inputblock
10075
10076             ];
10077                 
10078         }
10079         
10080         if (this.disabled) {
10081             input.disabled=true;
10082         }
10083         
10084         return cfg;
10085         
10086     },
10087     /**
10088      * return the real textarea element.
10089      */
10090     inputEl: function ()
10091     {
10092         return this.el.select('textarea.form-control',true).first();
10093     },
10094     
10095     /**
10096      * Clear any invalid styles/messages for this field
10097      */
10098     clearInvalid : function()
10099     {
10100         
10101         if(!this.el || this.preventMark){ // not rendered
10102             return;
10103         }
10104         
10105         var label = this.el.select('label', true).first();
10106         var icon = this.el.select('i.fa-star', true).first();
10107         
10108         if(label && icon){
10109             icon.remove();
10110         }
10111         
10112         this.el.removeClass(this.invalidClass);
10113         
10114         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10115             
10116             var feedback = this.el.select('.form-control-feedback', true).first();
10117             
10118             if(feedback){
10119                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10120             }
10121             
10122         }
10123         
10124         this.fireEvent('valid', this);
10125     },
10126     
10127      /**
10128      * Mark this field as valid
10129      */
10130     markValid : function()
10131     {
10132         if(!this.el  || this.preventMark){ // not rendered
10133             return;
10134         }
10135         
10136         this.el.removeClass([this.invalidClass, this.validClass]);
10137         
10138         var feedback = this.el.select('.form-control-feedback', true).first();
10139             
10140         if(feedback){
10141             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10142         }
10143
10144         if(this.disabled || this.allowBlank){
10145             return;
10146         }
10147         
10148         var label = this.el.select('label', true).first();
10149         var icon = this.el.select('i.fa-star', true).first();
10150         
10151         if(label && icon){
10152             icon.remove();
10153         }
10154         
10155         this.el.addClass(this.validClass);
10156         
10157         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10158             
10159             var feedback = this.el.select('.form-control-feedback', true).first();
10160             
10161             if(feedback){
10162                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10163                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10164             }
10165             
10166         }
10167         
10168         this.fireEvent('valid', this);
10169     },
10170     
10171      /**
10172      * Mark this field as invalid
10173      * @param {String} msg The validation message
10174      */
10175     markInvalid : function(msg)
10176     {
10177         if(!this.el  || this.preventMark){ // not rendered
10178             return;
10179         }
10180         
10181         this.el.removeClass([this.invalidClass, this.validClass]);
10182         
10183         var feedback = this.el.select('.form-control-feedback', true).first();
10184             
10185         if(feedback){
10186             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10187         }
10188
10189         if(this.disabled || this.allowBlank){
10190             return;
10191         }
10192         
10193         var label = this.el.select('label', true).first();
10194         var icon = this.el.select('i.fa-star', true).first();
10195         
10196         if(!this.getValue().length && label && !icon){
10197             this.el.createChild({
10198                 tag : 'i',
10199                 cls : 'text-danger fa fa-lg fa-star',
10200                 tooltip : 'This field is required',
10201                 style : 'margin-right:5px;'
10202             }, label, true);
10203         }
10204
10205         this.el.addClass(this.invalidClass);
10206         
10207         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10208             
10209             var feedback = this.el.select('.form-control-feedback', true).first();
10210             
10211             if(feedback){
10212                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10213                 
10214                 if(this.getValue().length || this.forceFeedback){
10215                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10216                 }
10217                 
10218             }
10219             
10220         }
10221         
10222         this.fireEvent('invalid', this, msg);
10223     }
10224 });
10225
10226  
10227 /*
10228  * - LGPL
10229  *
10230  * trigger field - base class for combo..
10231  * 
10232  */
10233  
10234 /**
10235  * @class Roo.bootstrap.TriggerField
10236  * @extends Roo.bootstrap.Input
10237  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10238  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10239  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10240  * for which you can provide a custom implementation.  For example:
10241  * <pre><code>
10242 var trigger = new Roo.bootstrap.TriggerField();
10243 trigger.onTriggerClick = myTriggerFn;
10244 trigger.applyTo('my-field');
10245 </code></pre>
10246  *
10247  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10248  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10249  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10250  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10251  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10252
10253  * @constructor
10254  * Create a new TriggerField.
10255  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10256  * to the base TextField)
10257  */
10258 Roo.bootstrap.TriggerField = function(config){
10259     this.mimicing = false;
10260     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10261 };
10262
10263 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10264     /**
10265      * @cfg {String} triggerClass A CSS class to apply to the trigger
10266      */
10267      /**
10268      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10269      */
10270     hideTrigger:false,
10271
10272     /**
10273      * @cfg {Boolean} removable (true|false) special filter default false
10274      */
10275     removable : false,
10276     
10277     /** @cfg {Boolean} grow @hide */
10278     /** @cfg {Number} growMin @hide */
10279     /** @cfg {Number} growMax @hide */
10280
10281     /**
10282      * @hide 
10283      * @method
10284      */
10285     autoSize: Roo.emptyFn,
10286     // private
10287     monitorTab : true,
10288     // private
10289     deferHeight : true,
10290
10291     
10292     actionMode : 'wrap',
10293     
10294     caret : false,
10295     
10296     
10297     getAutoCreate : function(){
10298        
10299         var align = this.labelAlign || this.parentLabelAlign();
10300         
10301         var id = Roo.id();
10302         
10303         var cfg = {
10304             cls: 'form-group' //input-group
10305         };
10306         
10307         
10308         var input =  {
10309             tag: 'input',
10310             id : id,
10311             type : this.inputType,
10312             cls : 'form-control',
10313             autocomplete: 'new-password',
10314             placeholder : this.placeholder || '' 
10315             
10316         };
10317         if (this.name) {
10318             input.name = this.name;
10319         }
10320         if (this.size) {
10321             input.cls += ' input-' + this.size;
10322         }
10323         
10324         if (this.disabled) {
10325             input.disabled=true;
10326         }
10327         
10328         var inputblock = input;
10329         
10330         if(this.hasFeedback && !this.allowBlank){
10331             
10332             var feedback = {
10333                 tag: 'span',
10334                 cls: 'glyphicon form-control-feedback'
10335             };
10336             
10337             if(this.removable && !this.editable && !this.tickable){
10338                 inputblock = {
10339                     cls : 'has-feedback',
10340                     cn :  [
10341                         inputblock,
10342                         {
10343                             tag: 'button',
10344                             html : 'x',
10345                             cls : 'roo-combo-removable-btn close'
10346                         },
10347                         feedback
10348                     ] 
10349                 };
10350             } else {
10351                 inputblock = {
10352                     cls : 'has-feedback',
10353                     cn :  [
10354                         inputblock,
10355                         feedback
10356                     ] 
10357                 };
10358             }
10359
10360         } else {
10361             if(this.removable && !this.editable && !this.tickable){
10362                 inputblock = {
10363                     cls : 'roo-removable',
10364                     cn :  [
10365                         inputblock,
10366                         {
10367                             tag: 'button',
10368                             html : 'x',
10369                             cls : 'roo-combo-removable-btn close'
10370                         }
10371                     ] 
10372                 };
10373             }
10374         }
10375         
10376         if (this.before || this.after) {
10377             
10378             inputblock = {
10379                 cls : 'input-group',
10380                 cn :  [] 
10381             };
10382             if (this.before) {
10383                 inputblock.cn.push({
10384                     tag :'span',
10385                     cls : 'input-group-addon input-group-prepend input-group-text',
10386                     html : this.before
10387                 });
10388             }
10389             
10390             inputblock.cn.push(input);
10391             
10392             if(this.hasFeedback && !this.allowBlank){
10393                 inputblock.cls += ' has-feedback';
10394                 inputblock.cn.push(feedback);
10395             }
10396             
10397             if (this.after) {
10398                 inputblock.cn.push({
10399                     tag :'span',
10400                     cls : 'input-group-addon input-group-append input-group-text',
10401                     html : this.after
10402                 });
10403             }
10404             
10405         };
10406         
10407       
10408         
10409         var ibwrap = inputblock;
10410         
10411         if(this.multiple){
10412             ibwrap = {
10413                 tag: 'ul',
10414                 cls: 'roo-select2-choices',
10415                 cn:[
10416                     {
10417                         tag: 'li',
10418                         cls: 'roo-select2-search-field',
10419                         cn: [
10420
10421                             inputblock
10422                         ]
10423                     }
10424                 ]
10425             };
10426                 
10427         }
10428         
10429         var combobox = {
10430             cls: 'roo-select2-container input-group',
10431             cn: [
10432                  {
10433                     tag: 'input',
10434                     type : 'hidden',
10435                     cls: 'form-hidden-field'
10436                 },
10437                 ibwrap
10438             ]
10439         };
10440         
10441         if(!this.multiple && this.showToggleBtn){
10442             
10443             var caret = {
10444                         tag: 'span',
10445                         cls: 'caret'
10446              };
10447             if (this.caret != false) {
10448                 caret = {
10449                      tag: 'i',
10450                      cls: 'fa fa-' + this.caret
10451                 };
10452                 
10453             }
10454             
10455             combobox.cn.push({
10456                 tag :'span',
10457                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10458                 cn : [
10459                     caret,
10460                     {
10461                         tag: 'span',
10462                         cls: 'combobox-clear',
10463                         cn  : [
10464                             {
10465                                 tag : 'i',
10466                                 cls: 'icon-remove'
10467                             }
10468                         ]
10469                     }
10470                 ]
10471
10472             })
10473         }
10474         
10475         if(this.multiple){
10476             combobox.cls += ' roo-select2-container-multi';
10477         }
10478          var indicator = {
10479             tag : 'i',
10480             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10481             tooltip : 'This field is required'
10482         };
10483         if (Roo.bootstrap.version == 4) {
10484             indicator = {
10485                 tag : 'i',
10486                 style : 'display:none'
10487             };
10488         }
10489         
10490         
10491         if (align ==='left' && this.fieldLabel.length) {
10492             
10493             cfg.cls += ' roo-form-group-label-left row';
10494
10495             cfg.cn = [
10496                 indicator,
10497                 {
10498                     tag: 'label',
10499                     'for' :  id,
10500                     cls : 'control-label',
10501                     html : this.fieldLabel
10502
10503                 },
10504                 {
10505                     cls : "", 
10506                     cn: [
10507                         combobox
10508                     ]
10509                 }
10510
10511             ];
10512             
10513             var labelCfg = cfg.cn[1];
10514             var contentCfg = cfg.cn[2];
10515             
10516             if(this.indicatorpos == 'right'){
10517                 cfg.cn = [
10518                     {
10519                         tag: 'label',
10520                         'for' :  id,
10521                         cls : 'control-label',
10522                         cn : [
10523                             {
10524                                 tag : 'span',
10525                                 html : this.fieldLabel
10526                             },
10527                             indicator
10528                         ]
10529                     },
10530                     {
10531                         cls : "", 
10532                         cn: [
10533                             combobox
10534                         ]
10535                     }
10536
10537                 ];
10538                 
10539                 labelCfg = cfg.cn[0];
10540                 contentCfg = cfg.cn[1];
10541             }
10542             
10543             if(this.labelWidth > 12){
10544                 labelCfg.style = "width: " + this.labelWidth + 'px';
10545             }
10546             
10547             if(this.labelWidth < 13 && this.labelmd == 0){
10548                 this.labelmd = this.labelWidth;
10549             }
10550             
10551             if(this.labellg > 0){
10552                 labelCfg.cls += ' col-lg-' + this.labellg;
10553                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10554             }
10555             
10556             if(this.labelmd > 0){
10557                 labelCfg.cls += ' col-md-' + this.labelmd;
10558                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10559             }
10560             
10561             if(this.labelsm > 0){
10562                 labelCfg.cls += ' col-sm-' + this.labelsm;
10563                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10564             }
10565             
10566             if(this.labelxs > 0){
10567                 labelCfg.cls += ' col-xs-' + this.labelxs;
10568                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10569             }
10570             
10571         } else if ( this.fieldLabel.length) {
10572 //                Roo.log(" label");
10573             cfg.cn = [
10574                 indicator,
10575                {
10576                    tag: 'label',
10577                    //cls : 'input-group-addon',
10578                    html : this.fieldLabel
10579
10580                },
10581
10582                combobox
10583
10584             ];
10585             
10586             if(this.indicatorpos == 'right'){
10587                 
10588                 cfg.cn = [
10589                     {
10590                        tag: 'label',
10591                        cn : [
10592                            {
10593                                tag : 'span',
10594                                html : this.fieldLabel
10595                            },
10596                            indicator
10597                        ]
10598
10599                     },
10600                     combobox
10601
10602                 ];
10603
10604             }
10605
10606         } else {
10607             
10608 //                Roo.log(" no label && no align");
10609                 cfg = combobox
10610                      
10611                 
10612         }
10613         
10614         var settings=this;
10615         ['xs','sm','md','lg'].map(function(size){
10616             if (settings[size]) {
10617                 cfg.cls += ' col-' + size + '-' + settings[size];
10618             }
10619         });
10620         
10621         return cfg;
10622         
10623     },
10624     
10625     
10626     
10627     // private
10628     onResize : function(w, h){
10629 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10630 //        if(typeof w == 'number'){
10631 //            var x = w - this.trigger.getWidth();
10632 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10633 //            this.trigger.setStyle('left', x+'px');
10634 //        }
10635     },
10636
10637     // private
10638     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10639
10640     // private
10641     getResizeEl : function(){
10642         return this.inputEl();
10643     },
10644
10645     // private
10646     getPositionEl : function(){
10647         return this.inputEl();
10648     },
10649
10650     // private
10651     alignErrorIcon : function(){
10652         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10653     },
10654
10655     // private
10656     initEvents : function(){
10657         
10658         this.createList();
10659         
10660         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10661         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10662         if(!this.multiple && this.showToggleBtn){
10663             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10664             if(this.hideTrigger){
10665                 this.trigger.setDisplayed(false);
10666             }
10667             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10668         }
10669         
10670         if(this.multiple){
10671             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10672         }
10673         
10674         if(this.removable && !this.editable && !this.tickable){
10675             var close = this.closeTriggerEl();
10676             
10677             if(close){
10678                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10679                 close.on('click', this.removeBtnClick, this, close);
10680             }
10681         }
10682         
10683         //this.trigger.addClassOnOver('x-form-trigger-over');
10684         //this.trigger.addClassOnClick('x-form-trigger-click');
10685         
10686         //if(!this.width){
10687         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10688         //}
10689     },
10690     
10691     closeTriggerEl : function()
10692     {
10693         var close = this.el.select('.roo-combo-removable-btn', true).first();
10694         return close ? close : false;
10695     },
10696     
10697     removeBtnClick : function(e, h, el)
10698     {
10699         e.preventDefault();
10700         
10701         if(this.fireEvent("remove", this) !== false){
10702             this.reset();
10703             this.fireEvent("afterremove", this)
10704         }
10705     },
10706     
10707     createList : function()
10708     {
10709         this.list = Roo.get(document.body).createChild({
10710             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10711             cls: 'typeahead typeahead-long dropdown-menu',
10712             style: 'display:none'
10713         });
10714         
10715         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10716         
10717     },
10718
10719     // private
10720     initTrigger : function(){
10721        
10722     },
10723
10724     // private
10725     onDestroy : function(){
10726         if(this.trigger){
10727             this.trigger.removeAllListeners();
10728           //  this.trigger.remove();
10729         }
10730         //if(this.wrap){
10731         //    this.wrap.remove();
10732         //}
10733         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10734     },
10735
10736     // private
10737     onFocus : function(){
10738         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10739         /*
10740         if(!this.mimicing){
10741             this.wrap.addClass('x-trigger-wrap-focus');
10742             this.mimicing = true;
10743             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10744             if(this.monitorTab){
10745                 this.el.on("keydown", this.checkTab, this);
10746             }
10747         }
10748         */
10749     },
10750
10751     // private
10752     checkTab : function(e){
10753         if(e.getKey() == e.TAB){
10754             this.triggerBlur();
10755         }
10756     },
10757
10758     // private
10759     onBlur : function(){
10760         // do nothing
10761     },
10762
10763     // private
10764     mimicBlur : function(e, t){
10765         /*
10766         if(!this.wrap.contains(t) && this.validateBlur()){
10767             this.triggerBlur();
10768         }
10769         */
10770     },
10771
10772     // private
10773     triggerBlur : function(){
10774         this.mimicing = false;
10775         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10776         if(this.monitorTab){
10777             this.el.un("keydown", this.checkTab, this);
10778         }
10779         //this.wrap.removeClass('x-trigger-wrap-focus');
10780         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10781     },
10782
10783     // private
10784     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10785     validateBlur : function(e, t){
10786         return true;
10787     },
10788
10789     // private
10790     onDisable : function(){
10791         this.inputEl().dom.disabled = true;
10792         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10793         //if(this.wrap){
10794         //    this.wrap.addClass('x-item-disabled');
10795         //}
10796     },
10797
10798     // private
10799     onEnable : function(){
10800         this.inputEl().dom.disabled = false;
10801         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10802         //if(this.wrap){
10803         //    this.el.removeClass('x-item-disabled');
10804         //}
10805     },
10806
10807     // private
10808     onShow : function(){
10809         var ae = this.getActionEl();
10810         
10811         if(ae){
10812             ae.dom.style.display = '';
10813             ae.dom.style.visibility = 'visible';
10814         }
10815     },
10816
10817     // private
10818     
10819     onHide : function(){
10820         var ae = this.getActionEl();
10821         ae.dom.style.display = 'none';
10822     },
10823
10824     /**
10825      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10826      * by an implementing function.
10827      * @method
10828      * @param {EventObject} e
10829      */
10830     onTriggerClick : Roo.emptyFn
10831 });
10832  /*
10833  * Based on:
10834  * Ext JS Library 1.1.1
10835  * Copyright(c) 2006-2007, Ext JS, LLC.
10836  *
10837  * Originally Released Under LGPL - original licence link has changed is not relivant.
10838  *
10839  * Fork - LGPL
10840  * <script type="text/javascript">
10841  */
10842
10843
10844 /**
10845  * @class Roo.data.SortTypes
10846  * @singleton
10847  * Defines the default sorting (casting?) comparison functions used when sorting data.
10848  */
10849 Roo.data.SortTypes = {
10850     /**
10851      * Default sort that does nothing
10852      * @param {Mixed} s The value being converted
10853      * @return {Mixed} The comparison value
10854      */
10855     none : function(s){
10856         return s;
10857     },
10858     
10859     /**
10860      * The regular expression used to strip tags
10861      * @type {RegExp}
10862      * @property
10863      */
10864     stripTagsRE : /<\/?[^>]+>/gi,
10865     
10866     /**
10867      * Strips all HTML tags to sort on text only
10868      * @param {Mixed} s The value being converted
10869      * @return {String} The comparison value
10870      */
10871     asText : function(s){
10872         return String(s).replace(this.stripTagsRE, "");
10873     },
10874     
10875     /**
10876      * Strips all HTML tags to sort on text only - Case insensitive
10877      * @param {Mixed} s The value being converted
10878      * @return {String} The comparison value
10879      */
10880     asUCText : function(s){
10881         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10882     },
10883     
10884     /**
10885      * Case insensitive string
10886      * @param {Mixed} s The value being converted
10887      * @return {String} The comparison value
10888      */
10889     asUCString : function(s) {
10890         return String(s).toUpperCase();
10891     },
10892     
10893     /**
10894      * Date sorting
10895      * @param {Mixed} s The value being converted
10896      * @return {Number} The comparison value
10897      */
10898     asDate : function(s) {
10899         if(!s){
10900             return 0;
10901         }
10902         if(s instanceof Date){
10903             return s.getTime();
10904         }
10905         return Date.parse(String(s));
10906     },
10907     
10908     /**
10909      * Float sorting
10910      * @param {Mixed} s The value being converted
10911      * @return {Float} The comparison value
10912      */
10913     asFloat : function(s) {
10914         var val = parseFloat(String(s).replace(/,/g, ""));
10915         if(isNaN(val)) {
10916             val = 0;
10917         }
10918         return val;
10919     },
10920     
10921     /**
10922      * Integer sorting
10923      * @param {Mixed} s The value being converted
10924      * @return {Number} The comparison value
10925      */
10926     asInt : function(s) {
10927         var val = parseInt(String(s).replace(/,/g, ""));
10928         if(isNaN(val)) {
10929             val = 0;
10930         }
10931         return val;
10932     }
10933 };/*
10934  * Based on:
10935  * Ext JS Library 1.1.1
10936  * Copyright(c) 2006-2007, Ext JS, LLC.
10937  *
10938  * Originally Released Under LGPL - original licence link has changed is not relivant.
10939  *
10940  * Fork - LGPL
10941  * <script type="text/javascript">
10942  */
10943
10944 /**
10945 * @class Roo.data.Record
10946  * Instances of this class encapsulate both record <em>definition</em> information, and record
10947  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10948  * to access Records cached in an {@link Roo.data.Store} object.<br>
10949  * <p>
10950  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10951  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10952  * objects.<br>
10953  * <p>
10954  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10955  * @constructor
10956  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10957  * {@link #create}. The parameters are the same.
10958  * @param {Array} data An associative Array of data values keyed by the field name.
10959  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10960  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10961  * not specified an integer id is generated.
10962  */
10963 Roo.data.Record = function(data, id){
10964     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10965     this.data = data;
10966 };
10967
10968 /**
10969  * Generate a constructor for a specific record layout.
10970  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10971  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10972  * Each field definition object may contain the following properties: <ul>
10973  * <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,
10974  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10975  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10976  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10977  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10978  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10979  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10980  * this may be omitted.</p></li>
10981  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10982  * <ul><li>auto (Default, implies no conversion)</li>
10983  * <li>string</li>
10984  * <li>int</li>
10985  * <li>float</li>
10986  * <li>boolean</li>
10987  * <li>date</li></ul></p></li>
10988  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10989  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10990  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10991  * by the Reader into an object that will be stored in the Record. It is passed the
10992  * following parameters:<ul>
10993  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10994  * </ul></p></li>
10995  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10996  * </ul>
10997  * <br>usage:<br><pre><code>
10998 var TopicRecord = Roo.data.Record.create(
10999     {name: 'title', mapping: 'topic_title'},
11000     {name: 'author', mapping: 'username'},
11001     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11002     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11003     {name: 'lastPoster', mapping: 'user2'},
11004     {name: 'excerpt', mapping: 'post_text'}
11005 );
11006
11007 var myNewRecord = new TopicRecord({
11008     title: 'Do my job please',
11009     author: 'noobie',
11010     totalPosts: 1,
11011     lastPost: new Date(),
11012     lastPoster: 'Animal',
11013     excerpt: 'No way dude!'
11014 });
11015 myStore.add(myNewRecord);
11016 </code></pre>
11017  * @method create
11018  * @static
11019  */
11020 Roo.data.Record.create = function(o){
11021     var f = function(){
11022         f.superclass.constructor.apply(this, arguments);
11023     };
11024     Roo.extend(f, Roo.data.Record);
11025     var p = f.prototype;
11026     p.fields = new Roo.util.MixedCollection(false, function(field){
11027         return field.name;
11028     });
11029     for(var i = 0, len = o.length; i < len; i++){
11030         p.fields.add(new Roo.data.Field(o[i]));
11031     }
11032     f.getField = function(name){
11033         return p.fields.get(name);  
11034     };
11035     return f;
11036 };
11037
11038 Roo.data.Record.AUTO_ID = 1000;
11039 Roo.data.Record.EDIT = 'edit';
11040 Roo.data.Record.REJECT = 'reject';
11041 Roo.data.Record.COMMIT = 'commit';
11042
11043 Roo.data.Record.prototype = {
11044     /**
11045      * Readonly flag - true if this record has been modified.
11046      * @type Boolean
11047      */
11048     dirty : false,
11049     editing : false,
11050     error: null,
11051     modified: null,
11052
11053     // private
11054     join : function(store){
11055         this.store = store;
11056     },
11057
11058     /**
11059      * Set the named field to the specified value.
11060      * @param {String} name The name of the field to set.
11061      * @param {Object} value The value to set the field to.
11062      */
11063     set : function(name, value){
11064         if(this.data[name] == value){
11065             return;
11066         }
11067         this.dirty = true;
11068         if(!this.modified){
11069             this.modified = {};
11070         }
11071         if(typeof this.modified[name] == 'undefined'){
11072             this.modified[name] = this.data[name];
11073         }
11074         this.data[name] = value;
11075         if(!this.editing && this.store){
11076             this.store.afterEdit(this);
11077         }       
11078     },
11079
11080     /**
11081      * Get the value of the named field.
11082      * @param {String} name The name of the field to get the value of.
11083      * @return {Object} The value of the field.
11084      */
11085     get : function(name){
11086         return this.data[name]; 
11087     },
11088
11089     // private
11090     beginEdit : function(){
11091         this.editing = true;
11092         this.modified = {}; 
11093     },
11094
11095     // private
11096     cancelEdit : function(){
11097         this.editing = false;
11098         delete this.modified;
11099     },
11100
11101     // private
11102     endEdit : function(){
11103         this.editing = false;
11104         if(this.dirty && this.store){
11105             this.store.afterEdit(this);
11106         }
11107     },
11108
11109     /**
11110      * Usually called by the {@link Roo.data.Store} which owns the Record.
11111      * Rejects all changes made to the Record since either creation, or the last commit operation.
11112      * Modified fields are reverted to their original values.
11113      * <p>
11114      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11115      * of reject operations.
11116      */
11117     reject : function(){
11118         var m = this.modified;
11119         for(var n in m){
11120             if(typeof m[n] != "function"){
11121                 this.data[n] = m[n];
11122             }
11123         }
11124         this.dirty = false;
11125         delete this.modified;
11126         this.editing = false;
11127         if(this.store){
11128             this.store.afterReject(this);
11129         }
11130     },
11131
11132     /**
11133      * Usually called by the {@link Roo.data.Store} which owns the Record.
11134      * Commits all changes made to the Record since either creation, or the last commit operation.
11135      * <p>
11136      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11137      * of commit operations.
11138      */
11139     commit : function(){
11140         this.dirty = false;
11141         delete this.modified;
11142         this.editing = false;
11143         if(this.store){
11144             this.store.afterCommit(this);
11145         }
11146     },
11147
11148     // private
11149     hasError : function(){
11150         return this.error != null;
11151     },
11152
11153     // private
11154     clearError : function(){
11155         this.error = null;
11156     },
11157
11158     /**
11159      * Creates a copy of this record.
11160      * @param {String} id (optional) A new record id if you don't want to use this record's id
11161      * @return {Record}
11162      */
11163     copy : function(newId) {
11164         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11165     }
11166 };/*
11167  * Based on:
11168  * Ext JS Library 1.1.1
11169  * Copyright(c) 2006-2007, Ext JS, LLC.
11170  *
11171  * Originally Released Under LGPL - original licence link has changed is not relivant.
11172  *
11173  * Fork - LGPL
11174  * <script type="text/javascript">
11175  */
11176
11177
11178
11179 /**
11180  * @class Roo.data.Store
11181  * @extends Roo.util.Observable
11182  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11183  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11184  * <p>
11185  * 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
11186  * has no knowledge of the format of the data returned by the Proxy.<br>
11187  * <p>
11188  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11189  * instances from the data object. These records are cached and made available through accessor functions.
11190  * @constructor
11191  * Creates a new Store.
11192  * @param {Object} config A config object containing the objects needed for the Store to access data,
11193  * and read the data into Records.
11194  */
11195 Roo.data.Store = function(config){
11196     this.data = new Roo.util.MixedCollection(false);
11197     this.data.getKey = function(o){
11198         return o.id;
11199     };
11200     this.baseParams = {};
11201     // private
11202     this.paramNames = {
11203         "start" : "start",
11204         "limit" : "limit",
11205         "sort" : "sort",
11206         "dir" : "dir",
11207         "multisort" : "_multisort"
11208     };
11209
11210     if(config && config.data){
11211         this.inlineData = config.data;
11212         delete config.data;
11213     }
11214
11215     Roo.apply(this, config);
11216     
11217     if(this.reader){ // reader passed
11218         this.reader = Roo.factory(this.reader, Roo.data);
11219         this.reader.xmodule = this.xmodule || false;
11220         if(!this.recordType){
11221             this.recordType = this.reader.recordType;
11222         }
11223         if(this.reader.onMetaChange){
11224             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11225         }
11226     }
11227
11228     if(this.recordType){
11229         this.fields = this.recordType.prototype.fields;
11230     }
11231     this.modified = [];
11232
11233     this.addEvents({
11234         /**
11235          * @event datachanged
11236          * Fires when the data cache has changed, and a widget which is using this Store
11237          * as a Record cache should refresh its view.
11238          * @param {Store} this
11239          */
11240         datachanged : true,
11241         /**
11242          * @event metachange
11243          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11244          * @param {Store} this
11245          * @param {Object} meta The JSON metadata
11246          */
11247         metachange : true,
11248         /**
11249          * @event add
11250          * Fires when Records have been added to the Store
11251          * @param {Store} this
11252          * @param {Roo.data.Record[]} records The array of Records added
11253          * @param {Number} index The index at which the record(s) were added
11254          */
11255         add : true,
11256         /**
11257          * @event remove
11258          * Fires when a Record has been removed from the Store
11259          * @param {Store} this
11260          * @param {Roo.data.Record} record The Record that was removed
11261          * @param {Number} index The index at which the record was removed
11262          */
11263         remove : true,
11264         /**
11265          * @event update
11266          * Fires when a Record has been updated
11267          * @param {Store} this
11268          * @param {Roo.data.Record} record The Record that was updated
11269          * @param {String} operation The update operation being performed.  Value may be one of:
11270          * <pre><code>
11271  Roo.data.Record.EDIT
11272  Roo.data.Record.REJECT
11273  Roo.data.Record.COMMIT
11274          * </code></pre>
11275          */
11276         update : true,
11277         /**
11278          * @event clear
11279          * Fires when the data cache has been cleared.
11280          * @param {Store} this
11281          */
11282         clear : true,
11283         /**
11284          * @event beforeload
11285          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11286          * the load action will be canceled.
11287          * @param {Store} this
11288          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11289          */
11290         beforeload : true,
11291         /**
11292          * @event beforeloadadd
11293          * Fires after a new set of Records has been loaded.
11294          * @param {Store} this
11295          * @param {Roo.data.Record[]} records The Records that were loaded
11296          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11297          */
11298         beforeloadadd : true,
11299         /**
11300          * @event load
11301          * Fires after a new set of Records has been loaded, before they are added to the store.
11302          * @param {Store} this
11303          * @param {Roo.data.Record[]} records The Records that were loaded
11304          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11305          * @params {Object} return from reader
11306          */
11307         load : true,
11308         /**
11309          * @event loadexception
11310          * Fires if an exception occurs in the Proxy during loading.
11311          * Called with the signature of the Proxy's "loadexception" event.
11312          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11313          * 
11314          * @param {Proxy} 
11315          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11316          * @param {Object} load options 
11317          * @param {Object} jsonData from your request (normally this contains the Exception)
11318          */
11319         loadexception : true
11320     });
11321     
11322     if(this.proxy){
11323         this.proxy = Roo.factory(this.proxy, Roo.data);
11324         this.proxy.xmodule = this.xmodule || false;
11325         this.relayEvents(this.proxy,  ["loadexception"]);
11326     }
11327     this.sortToggle = {};
11328     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11329
11330     Roo.data.Store.superclass.constructor.call(this);
11331
11332     if(this.inlineData){
11333         this.loadData(this.inlineData);
11334         delete this.inlineData;
11335     }
11336 };
11337
11338 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11339      /**
11340     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11341     * without a remote query - used by combo/forms at present.
11342     */
11343     
11344     /**
11345     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11346     */
11347     /**
11348     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11349     */
11350     /**
11351     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11352     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11353     */
11354     /**
11355     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11356     * on any HTTP request
11357     */
11358     /**
11359     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11360     */
11361     /**
11362     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11363     */
11364     multiSort: false,
11365     /**
11366     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11367     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11368     */
11369     remoteSort : false,
11370
11371     /**
11372     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11373      * loaded or when a record is removed. (defaults to false).
11374     */
11375     pruneModifiedRecords : false,
11376
11377     // private
11378     lastOptions : null,
11379
11380     /**
11381      * Add Records to the Store and fires the add event.
11382      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11383      */
11384     add : function(records){
11385         records = [].concat(records);
11386         for(var i = 0, len = records.length; i < len; i++){
11387             records[i].join(this);
11388         }
11389         var index = this.data.length;
11390         this.data.addAll(records);
11391         this.fireEvent("add", this, records, index);
11392     },
11393
11394     /**
11395      * Remove a Record from the Store and fires the remove event.
11396      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11397      */
11398     remove : function(record){
11399         var index = this.data.indexOf(record);
11400         this.data.removeAt(index);
11401  
11402         if(this.pruneModifiedRecords){
11403             this.modified.remove(record);
11404         }
11405         this.fireEvent("remove", this, record, index);
11406     },
11407
11408     /**
11409      * Remove all Records from the Store and fires the clear event.
11410      */
11411     removeAll : function(){
11412         this.data.clear();
11413         if(this.pruneModifiedRecords){
11414             this.modified = [];
11415         }
11416         this.fireEvent("clear", this);
11417     },
11418
11419     /**
11420      * Inserts Records to the Store at the given index and fires the add event.
11421      * @param {Number} index The start index at which to insert the passed Records.
11422      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11423      */
11424     insert : function(index, records){
11425         records = [].concat(records);
11426         for(var i = 0, len = records.length; i < len; i++){
11427             this.data.insert(index, records[i]);
11428             records[i].join(this);
11429         }
11430         this.fireEvent("add", this, records, index);
11431     },
11432
11433     /**
11434      * Get the index within the cache of the passed Record.
11435      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11436      * @return {Number} The index of the passed Record. Returns -1 if not found.
11437      */
11438     indexOf : function(record){
11439         return this.data.indexOf(record);
11440     },
11441
11442     /**
11443      * Get the index within the cache of the Record with the passed id.
11444      * @param {String} id The id of the Record to find.
11445      * @return {Number} The index of the Record. Returns -1 if not found.
11446      */
11447     indexOfId : function(id){
11448         return this.data.indexOfKey(id);
11449     },
11450
11451     /**
11452      * Get the Record with the specified id.
11453      * @param {String} id The id of the Record to find.
11454      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11455      */
11456     getById : function(id){
11457         return this.data.key(id);
11458     },
11459
11460     /**
11461      * Get the Record at the specified index.
11462      * @param {Number} index The index of the Record to find.
11463      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11464      */
11465     getAt : function(index){
11466         return this.data.itemAt(index);
11467     },
11468
11469     /**
11470      * Returns a range of Records between specified indices.
11471      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11472      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11473      * @return {Roo.data.Record[]} An array of Records
11474      */
11475     getRange : function(start, end){
11476         return this.data.getRange(start, end);
11477     },
11478
11479     // private
11480     storeOptions : function(o){
11481         o = Roo.apply({}, o);
11482         delete o.callback;
11483         delete o.scope;
11484         this.lastOptions = o;
11485     },
11486
11487     /**
11488      * Loads the Record cache from the configured Proxy using the configured Reader.
11489      * <p>
11490      * If using remote paging, then the first load call must specify the <em>start</em>
11491      * and <em>limit</em> properties in the options.params property to establish the initial
11492      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11493      * <p>
11494      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11495      * and this call will return before the new data has been loaded. Perform any post-processing
11496      * in a callback function, or in a "load" event handler.</strong>
11497      * <p>
11498      * @param {Object} options An object containing properties which control loading options:<ul>
11499      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11500      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11501      * passed the following arguments:<ul>
11502      * <li>r : Roo.data.Record[]</li>
11503      * <li>options: Options object from the load call</li>
11504      * <li>success: Boolean success indicator</li></ul></li>
11505      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11506      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11507      * </ul>
11508      */
11509     load : function(options){
11510         options = options || {};
11511         if(this.fireEvent("beforeload", this, options) !== false){
11512             this.storeOptions(options);
11513             var p = Roo.apply(options.params || {}, this.baseParams);
11514             // if meta was not loaded from remote source.. try requesting it.
11515             if (!this.reader.metaFromRemote) {
11516                 p._requestMeta = 1;
11517             }
11518             if(this.sortInfo && this.remoteSort){
11519                 var pn = this.paramNames;
11520                 p[pn["sort"]] = this.sortInfo.field;
11521                 p[pn["dir"]] = this.sortInfo.direction;
11522             }
11523             if (this.multiSort) {
11524                 var pn = this.paramNames;
11525                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11526             }
11527             
11528             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11529         }
11530     },
11531
11532     /**
11533      * Reloads the Record cache from the configured Proxy using the configured Reader and
11534      * the options from the last load operation performed.
11535      * @param {Object} options (optional) An object containing properties which may override the options
11536      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11537      * the most recently used options are reused).
11538      */
11539     reload : function(options){
11540         this.load(Roo.applyIf(options||{}, this.lastOptions));
11541     },
11542
11543     // private
11544     // Called as a callback by the Reader during a load operation.
11545     loadRecords : function(o, options, success){
11546         if(!o || success === false){
11547             if(success !== false){
11548                 this.fireEvent("load", this, [], options, o);
11549             }
11550             if(options.callback){
11551                 options.callback.call(options.scope || this, [], options, false);
11552             }
11553             return;
11554         }
11555         // if data returned failure - throw an exception.
11556         if (o.success === false) {
11557             // show a message if no listener is registered.
11558             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11559                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11560             }
11561             // loadmask wil be hooked into this..
11562             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11563             return;
11564         }
11565         var r = o.records, t = o.totalRecords || r.length;
11566         
11567         this.fireEvent("beforeloadadd", this, r, options, o);
11568         
11569         if(!options || options.add !== true){
11570             if(this.pruneModifiedRecords){
11571                 this.modified = [];
11572             }
11573             for(var i = 0, len = r.length; i < len; i++){
11574                 r[i].join(this);
11575             }
11576             if(this.snapshot){
11577                 this.data = this.snapshot;
11578                 delete this.snapshot;
11579             }
11580             this.data.clear();
11581             this.data.addAll(r);
11582             this.totalLength = t;
11583             this.applySort();
11584             this.fireEvent("datachanged", this);
11585         }else{
11586             this.totalLength = Math.max(t, this.data.length+r.length);
11587             this.add(r);
11588         }
11589         
11590         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11591                 
11592             var e = new Roo.data.Record({});
11593
11594             e.set(this.parent.displayField, this.parent.emptyTitle);
11595             e.set(this.parent.valueField, '');
11596
11597             this.insert(0, e);
11598         }
11599             
11600         this.fireEvent("load", this, r, options, o);
11601         if(options.callback){
11602             options.callback.call(options.scope || this, r, options, true);
11603         }
11604     },
11605
11606
11607     /**
11608      * Loads data from a passed data block. A Reader which understands the format of the data
11609      * must have been configured in the constructor.
11610      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11611      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11612      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11613      */
11614     loadData : function(o, append){
11615         var r = this.reader.readRecords(o);
11616         this.loadRecords(r, {add: append}, true);
11617     },
11618
11619     /**
11620      * Gets the number of cached records.
11621      * <p>
11622      * <em>If using paging, this may not be the total size of the dataset. If the data object
11623      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11624      * the data set size</em>
11625      */
11626     getCount : function(){
11627         return this.data.length || 0;
11628     },
11629
11630     /**
11631      * Gets the total number of records in the dataset as returned by the server.
11632      * <p>
11633      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11634      * the dataset size</em>
11635      */
11636     getTotalCount : function(){
11637         return this.totalLength || 0;
11638     },
11639
11640     /**
11641      * Returns the sort state of the Store as an object with two properties:
11642      * <pre><code>
11643  field {String} The name of the field by which the Records are sorted
11644  direction {String} The sort order, "ASC" or "DESC"
11645      * </code></pre>
11646      */
11647     getSortState : function(){
11648         return this.sortInfo;
11649     },
11650
11651     // private
11652     applySort : function(){
11653         if(this.sortInfo && !this.remoteSort){
11654             var s = this.sortInfo, f = s.field;
11655             var st = this.fields.get(f).sortType;
11656             var fn = function(r1, r2){
11657                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11658                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11659             };
11660             this.data.sort(s.direction, fn);
11661             if(this.snapshot && this.snapshot != this.data){
11662                 this.snapshot.sort(s.direction, fn);
11663             }
11664         }
11665     },
11666
11667     /**
11668      * Sets the default sort column and order to be used by the next load operation.
11669      * @param {String} fieldName The name of the field to sort by.
11670      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11671      */
11672     setDefaultSort : function(field, dir){
11673         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11674     },
11675
11676     /**
11677      * Sort the Records.
11678      * If remote sorting is used, the sort is performed on the server, and the cache is
11679      * reloaded. If local sorting is used, the cache is sorted internally.
11680      * @param {String} fieldName The name of the field to sort by.
11681      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11682      */
11683     sort : function(fieldName, dir){
11684         var f = this.fields.get(fieldName);
11685         if(!dir){
11686             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11687             
11688             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11689                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11690             }else{
11691                 dir = f.sortDir;
11692             }
11693         }
11694         this.sortToggle[f.name] = dir;
11695         this.sortInfo = {field: f.name, direction: dir};
11696         if(!this.remoteSort){
11697             this.applySort();
11698             this.fireEvent("datachanged", this);
11699         }else{
11700             this.load(this.lastOptions);
11701         }
11702     },
11703
11704     /**
11705      * Calls the specified function for each of the Records in the cache.
11706      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11707      * Returning <em>false</em> aborts and exits the iteration.
11708      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11709      */
11710     each : function(fn, scope){
11711         this.data.each(fn, scope);
11712     },
11713
11714     /**
11715      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11716      * (e.g., during paging).
11717      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11718      */
11719     getModifiedRecords : function(){
11720         return this.modified;
11721     },
11722
11723     // private
11724     createFilterFn : function(property, value, anyMatch){
11725         if(!value.exec){ // not a regex
11726             value = String(value);
11727             if(value.length == 0){
11728                 return false;
11729             }
11730             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11731         }
11732         return function(r){
11733             return value.test(r.data[property]);
11734         };
11735     },
11736
11737     /**
11738      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11739      * @param {String} property A field on your records
11740      * @param {Number} start The record index to start at (defaults to 0)
11741      * @param {Number} end The last record index to include (defaults to length - 1)
11742      * @return {Number} The sum
11743      */
11744     sum : function(property, start, end){
11745         var rs = this.data.items, v = 0;
11746         start = start || 0;
11747         end = (end || end === 0) ? end : rs.length-1;
11748
11749         for(var i = start; i <= end; i++){
11750             v += (rs[i].data[property] || 0);
11751         }
11752         return v;
11753     },
11754
11755     /**
11756      * Filter the records by a specified property.
11757      * @param {String} field A field on your records
11758      * @param {String/RegExp} value Either a string that the field
11759      * should start with or a RegExp to test against the field
11760      * @param {Boolean} anyMatch True to match any part not just the beginning
11761      */
11762     filter : function(property, value, anyMatch){
11763         var fn = this.createFilterFn(property, value, anyMatch);
11764         return fn ? this.filterBy(fn) : this.clearFilter();
11765     },
11766
11767     /**
11768      * Filter by a function. The specified function will be called with each
11769      * record in this data source. If the function returns true the record is included,
11770      * otherwise it is filtered.
11771      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11772      * @param {Object} scope (optional) The scope of the function (defaults to this)
11773      */
11774     filterBy : function(fn, scope){
11775         this.snapshot = this.snapshot || this.data;
11776         this.data = this.queryBy(fn, scope||this);
11777         this.fireEvent("datachanged", this);
11778     },
11779
11780     /**
11781      * Query the records by a specified property.
11782      * @param {String} field A field on your records
11783      * @param {String/RegExp} value Either a string that the field
11784      * should start with or a RegExp to test against the field
11785      * @param {Boolean} anyMatch True to match any part not just the beginning
11786      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11787      */
11788     query : function(property, value, anyMatch){
11789         var fn = this.createFilterFn(property, value, anyMatch);
11790         return fn ? this.queryBy(fn) : this.data.clone();
11791     },
11792
11793     /**
11794      * Query by a function. The specified function will be called with each
11795      * record in this data source. If the function returns true the record is included
11796      * in the results.
11797      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11798      * @param {Object} scope (optional) The scope of the function (defaults to this)
11799       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11800      **/
11801     queryBy : function(fn, scope){
11802         var data = this.snapshot || this.data;
11803         return data.filterBy(fn, scope||this);
11804     },
11805
11806     /**
11807      * Collects unique values for a particular dataIndex from this store.
11808      * @param {String} dataIndex The property to collect
11809      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11810      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11811      * @return {Array} An array of the unique values
11812      **/
11813     collect : function(dataIndex, allowNull, bypassFilter){
11814         var d = (bypassFilter === true && this.snapshot) ?
11815                 this.snapshot.items : this.data.items;
11816         var v, sv, r = [], l = {};
11817         for(var i = 0, len = d.length; i < len; i++){
11818             v = d[i].data[dataIndex];
11819             sv = String(v);
11820             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11821                 l[sv] = true;
11822                 r[r.length] = v;
11823             }
11824         }
11825         return r;
11826     },
11827
11828     /**
11829      * Revert to a view of the Record cache with no filtering applied.
11830      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11831      */
11832     clearFilter : function(suppressEvent){
11833         if(this.snapshot && this.snapshot != this.data){
11834             this.data = this.snapshot;
11835             delete this.snapshot;
11836             if(suppressEvent !== true){
11837                 this.fireEvent("datachanged", this);
11838             }
11839         }
11840     },
11841
11842     // private
11843     afterEdit : function(record){
11844         if(this.modified.indexOf(record) == -1){
11845             this.modified.push(record);
11846         }
11847         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11848     },
11849     
11850     // private
11851     afterReject : function(record){
11852         this.modified.remove(record);
11853         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11854     },
11855
11856     // private
11857     afterCommit : function(record){
11858         this.modified.remove(record);
11859         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11860     },
11861
11862     /**
11863      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11864      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11865      */
11866     commitChanges : function(){
11867         var m = this.modified.slice(0);
11868         this.modified = [];
11869         for(var i = 0, len = m.length; i < len; i++){
11870             m[i].commit();
11871         }
11872     },
11873
11874     /**
11875      * Cancel outstanding changes on all changed records.
11876      */
11877     rejectChanges : function(){
11878         var m = this.modified.slice(0);
11879         this.modified = [];
11880         for(var i = 0, len = m.length; i < len; i++){
11881             m[i].reject();
11882         }
11883     },
11884
11885     onMetaChange : function(meta, rtype, o){
11886         this.recordType = rtype;
11887         this.fields = rtype.prototype.fields;
11888         delete this.snapshot;
11889         this.sortInfo = meta.sortInfo || this.sortInfo;
11890         this.modified = [];
11891         this.fireEvent('metachange', this, this.reader.meta);
11892     },
11893     
11894     moveIndex : function(data, type)
11895     {
11896         var index = this.indexOf(data);
11897         
11898         var newIndex = index + type;
11899         
11900         this.remove(data);
11901         
11902         this.insert(newIndex, data);
11903         
11904     }
11905 });/*
11906  * Based on:
11907  * Ext JS Library 1.1.1
11908  * Copyright(c) 2006-2007, Ext JS, LLC.
11909  *
11910  * Originally Released Under LGPL - original licence link has changed is not relivant.
11911  *
11912  * Fork - LGPL
11913  * <script type="text/javascript">
11914  */
11915
11916 /**
11917  * @class Roo.data.SimpleStore
11918  * @extends Roo.data.Store
11919  * Small helper class to make creating Stores from Array data easier.
11920  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11921  * @cfg {Array} fields An array of field definition objects, or field name strings.
11922  * @cfg {Array} data The multi-dimensional array of data
11923  * @constructor
11924  * @param {Object} config
11925  */
11926 Roo.data.SimpleStore = function(config){
11927     Roo.data.SimpleStore.superclass.constructor.call(this, {
11928         isLocal : true,
11929         reader: new Roo.data.ArrayReader({
11930                 id: config.id
11931             },
11932             Roo.data.Record.create(config.fields)
11933         ),
11934         proxy : new Roo.data.MemoryProxy(config.data)
11935     });
11936     this.load();
11937 };
11938 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11939  * Based on:
11940  * Ext JS Library 1.1.1
11941  * Copyright(c) 2006-2007, Ext JS, LLC.
11942  *
11943  * Originally Released Under LGPL - original licence link has changed is not relivant.
11944  *
11945  * Fork - LGPL
11946  * <script type="text/javascript">
11947  */
11948
11949 /**
11950 /**
11951  * @extends Roo.data.Store
11952  * @class Roo.data.JsonStore
11953  * Small helper class to make creating Stores for JSON data easier. <br/>
11954 <pre><code>
11955 var store = new Roo.data.JsonStore({
11956     url: 'get-images.php',
11957     root: 'images',
11958     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11959 });
11960 </code></pre>
11961  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11962  * JsonReader and HttpProxy (unless inline data is provided).</b>
11963  * @cfg {Array} fields An array of field definition objects, or field name strings.
11964  * @constructor
11965  * @param {Object} config
11966  */
11967 Roo.data.JsonStore = function(c){
11968     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11969         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11970         reader: new Roo.data.JsonReader(c, c.fields)
11971     }));
11972 };
11973 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11974  * Based on:
11975  * Ext JS Library 1.1.1
11976  * Copyright(c) 2006-2007, Ext JS, LLC.
11977  *
11978  * Originally Released Under LGPL - original licence link has changed is not relivant.
11979  *
11980  * Fork - LGPL
11981  * <script type="text/javascript">
11982  */
11983
11984  
11985 Roo.data.Field = function(config){
11986     if(typeof config == "string"){
11987         config = {name: config};
11988     }
11989     Roo.apply(this, config);
11990     
11991     if(!this.type){
11992         this.type = "auto";
11993     }
11994     
11995     var st = Roo.data.SortTypes;
11996     // named sortTypes are supported, here we look them up
11997     if(typeof this.sortType == "string"){
11998         this.sortType = st[this.sortType];
11999     }
12000     
12001     // set default sortType for strings and dates
12002     if(!this.sortType){
12003         switch(this.type){
12004             case "string":
12005                 this.sortType = st.asUCString;
12006                 break;
12007             case "date":
12008                 this.sortType = st.asDate;
12009                 break;
12010             default:
12011                 this.sortType = st.none;
12012         }
12013     }
12014
12015     // define once
12016     var stripRe = /[\$,%]/g;
12017
12018     // prebuilt conversion function for this field, instead of
12019     // switching every time we're reading a value
12020     if(!this.convert){
12021         var cv, dateFormat = this.dateFormat;
12022         switch(this.type){
12023             case "":
12024             case "auto":
12025             case undefined:
12026                 cv = function(v){ return v; };
12027                 break;
12028             case "string":
12029                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12030                 break;
12031             case "int":
12032                 cv = function(v){
12033                     return v !== undefined && v !== null && v !== '' ?
12034                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12035                     };
12036                 break;
12037             case "float":
12038                 cv = function(v){
12039                     return v !== undefined && v !== null && v !== '' ?
12040                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12041                     };
12042                 break;
12043             case "bool":
12044             case "boolean":
12045                 cv = function(v){ return v === true || v === "true" || v == 1; };
12046                 break;
12047             case "date":
12048                 cv = function(v){
12049                     if(!v){
12050                         return '';
12051                     }
12052                     if(v instanceof Date){
12053                         return v;
12054                     }
12055                     if(dateFormat){
12056                         if(dateFormat == "timestamp"){
12057                             return new Date(v*1000);
12058                         }
12059                         return Date.parseDate(v, dateFormat);
12060                     }
12061                     var parsed = Date.parse(v);
12062                     return parsed ? new Date(parsed) : null;
12063                 };
12064              break;
12065             
12066         }
12067         this.convert = cv;
12068     }
12069 };
12070
12071 Roo.data.Field.prototype = {
12072     dateFormat: null,
12073     defaultValue: "",
12074     mapping: null,
12075     sortType : null,
12076     sortDir : "ASC"
12077 };/*
12078  * Based on:
12079  * Ext JS Library 1.1.1
12080  * Copyright(c) 2006-2007, Ext JS, LLC.
12081  *
12082  * Originally Released Under LGPL - original licence link has changed is not relivant.
12083  *
12084  * Fork - LGPL
12085  * <script type="text/javascript">
12086  */
12087  
12088 // Base class for reading structured data from a data source.  This class is intended to be
12089 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12090
12091 /**
12092  * @class Roo.data.DataReader
12093  * Base class for reading structured data from a data source.  This class is intended to be
12094  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12095  */
12096
12097 Roo.data.DataReader = function(meta, recordType){
12098     
12099     this.meta = meta;
12100     
12101     this.recordType = recordType instanceof Array ? 
12102         Roo.data.Record.create(recordType) : recordType;
12103 };
12104
12105 Roo.data.DataReader.prototype = {
12106      /**
12107      * Create an empty record
12108      * @param {Object} data (optional) - overlay some values
12109      * @return {Roo.data.Record} record created.
12110      */
12111     newRow :  function(d) {
12112         var da =  {};
12113         this.recordType.prototype.fields.each(function(c) {
12114             switch( c.type) {
12115                 case 'int' : da[c.name] = 0; break;
12116                 case 'date' : da[c.name] = new Date(); break;
12117                 case 'float' : da[c.name] = 0.0; break;
12118                 case 'boolean' : da[c.name] = false; break;
12119                 default : da[c.name] = ""; break;
12120             }
12121             
12122         });
12123         return new this.recordType(Roo.apply(da, d));
12124     }
12125     
12126 };/*
12127  * Based on:
12128  * Ext JS Library 1.1.1
12129  * Copyright(c) 2006-2007, Ext JS, LLC.
12130  *
12131  * Originally Released Under LGPL - original licence link has changed is not relivant.
12132  *
12133  * Fork - LGPL
12134  * <script type="text/javascript">
12135  */
12136
12137 /**
12138  * @class Roo.data.DataProxy
12139  * @extends Roo.data.Observable
12140  * This class is an abstract base class for implementations which provide retrieval of
12141  * unformatted data objects.<br>
12142  * <p>
12143  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12144  * (of the appropriate type which knows how to parse the data object) to provide a block of
12145  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12146  * <p>
12147  * Custom implementations must implement the load method as described in
12148  * {@link Roo.data.HttpProxy#load}.
12149  */
12150 Roo.data.DataProxy = function(){
12151     this.addEvents({
12152         /**
12153          * @event beforeload
12154          * Fires before a network request is made to retrieve a data object.
12155          * @param {Object} This DataProxy object.
12156          * @param {Object} params The params parameter to the load function.
12157          */
12158         beforeload : true,
12159         /**
12160          * @event load
12161          * Fires before the load method's callback is called.
12162          * @param {Object} This DataProxy object.
12163          * @param {Object} o The data object.
12164          * @param {Object} arg The callback argument object passed to the load function.
12165          */
12166         load : true,
12167         /**
12168          * @event loadexception
12169          * Fires if an Exception occurs during data retrieval.
12170          * @param {Object} This DataProxy object.
12171          * @param {Object} o The data object.
12172          * @param {Object} arg The callback argument object passed to the load function.
12173          * @param {Object} e The Exception.
12174          */
12175         loadexception : true
12176     });
12177     Roo.data.DataProxy.superclass.constructor.call(this);
12178 };
12179
12180 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12181
12182     /**
12183      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12184      */
12185 /*
12186  * Based on:
12187  * Ext JS Library 1.1.1
12188  * Copyright(c) 2006-2007, Ext JS, LLC.
12189  *
12190  * Originally Released Under LGPL - original licence link has changed is not relivant.
12191  *
12192  * Fork - LGPL
12193  * <script type="text/javascript">
12194  */
12195 /**
12196  * @class Roo.data.MemoryProxy
12197  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12198  * to the Reader when its load method is called.
12199  * @constructor
12200  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12201  */
12202 Roo.data.MemoryProxy = function(data){
12203     if (data.data) {
12204         data = data.data;
12205     }
12206     Roo.data.MemoryProxy.superclass.constructor.call(this);
12207     this.data = data;
12208 };
12209
12210 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12211     
12212     /**
12213      * Load data from the requested source (in this case an in-memory
12214      * data object passed to the constructor), read the data object into
12215      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12216      * process that block using the passed callback.
12217      * @param {Object} params This parameter is not used by the MemoryProxy class.
12218      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12219      * object into a block of Roo.data.Records.
12220      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12221      * The function must be passed <ul>
12222      * <li>The Record block object</li>
12223      * <li>The "arg" argument from the load function</li>
12224      * <li>A boolean success indicator</li>
12225      * </ul>
12226      * @param {Object} scope The scope in which to call the callback
12227      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12228      */
12229     load : function(params, reader, callback, scope, arg){
12230         params = params || {};
12231         var result;
12232         try {
12233             result = reader.readRecords(this.data);
12234         }catch(e){
12235             this.fireEvent("loadexception", this, arg, null, e);
12236             callback.call(scope, null, arg, false);
12237             return;
12238         }
12239         callback.call(scope, result, arg, true);
12240     },
12241     
12242     // private
12243     update : function(params, records){
12244         
12245     }
12246 });/*
12247  * Based on:
12248  * Ext JS Library 1.1.1
12249  * Copyright(c) 2006-2007, Ext JS, LLC.
12250  *
12251  * Originally Released Under LGPL - original licence link has changed is not relivant.
12252  *
12253  * Fork - LGPL
12254  * <script type="text/javascript">
12255  */
12256 /**
12257  * @class Roo.data.HttpProxy
12258  * @extends Roo.data.DataProxy
12259  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12260  * configured to reference a certain URL.<br><br>
12261  * <p>
12262  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12263  * from which the running page was served.<br><br>
12264  * <p>
12265  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12266  * <p>
12267  * Be aware that to enable the browser to parse an XML document, the server must set
12268  * the Content-Type header in the HTTP response to "text/xml".
12269  * @constructor
12270  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12271  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12272  * will be used to make the request.
12273  */
12274 Roo.data.HttpProxy = function(conn){
12275     Roo.data.HttpProxy.superclass.constructor.call(this);
12276     // is conn a conn config or a real conn?
12277     this.conn = conn;
12278     this.useAjax = !conn || !conn.events;
12279   
12280 };
12281
12282 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12283     // thse are take from connection...
12284     
12285     /**
12286      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12287      */
12288     /**
12289      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12290      * extra parameters to each request made by this object. (defaults to undefined)
12291      */
12292     /**
12293      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12294      *  to each request made by this object. (defaults to undefined)
12295      */
12296     /**
12297      * @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)
12298      */
12299     /**
12300      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12301      */
12302      /**
12303      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12304      * @type Boolean
12305      */
12306   
12307
12308     /**
12309      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12310      * @type Boolean
12311      */
12312     /**
12313      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12314      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12315      * a finer-grained basis than the DataProxy events.
12316      */
12317     getConnection : function(){
12318         return this.useAjax ? Roo.Ajax : this.conn;
12319     },
12320
12321     /**
12322      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12323      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12324      * process that block using the passed callback.
12325      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12326      * for the request to the remote server.
12327      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12328      * object into a block of Roo.data.Records.
12329      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12330      * The function must be passed <ul>
12331      * <li>The Record block object</li>
12332      * <li>The "arg" argument from the load function</li>
12333      * <li>A boolean success indicator</li>
12334      * </ul>
12335      * @param {Object} scope The scope in which to call the callback
12336      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12337      */
12338     load : function(params, reader, callback, scope, arg){
12339         if(this.fireEvent("beforeload", this, params) !== false){
12340             var  o = {
12341                 params : params || {},
12342                 request: {
12343                     callback : callback,
12344                     scope : scope,
12345                     arg : arg
12346                 },
12347                 reader: reader,
12348                 callback : this.loadResponse,
12349                 scope: this
12350             };
12351             if(this.useAjax){
12352                 Roo.applyIf(o, this.conn);
12353                 if(this.activeRequest){
12354                     Roo.Ajax.abort(this.activeRequest);
12355                 }
12356                 this.activeRequest = Roo.Ajax.request(o);
12357             }else{
12358                 this.conn.request(o);
12359             }
12360         }else{
12361             callback.call(scope||this, null, arg, false);
12362         }
12363     },
12364
12365     // private
12366     loadResponse : function(o, success, response){
12367         delete this.activeRequest;
12368         if(!success){
12369             this.fireEvent("loadexception", this, o, response);
12370             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12371             return;
12372         }
12373         var result;
12374         try {
12375             result = o.reader.read(response);
12376         }catch(e){
12377             this.fireEvent("loadexception", this, o, response, e);
12378             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12379             return;
12380         }
12381         
12382         this.fireEvent("load", this, o, o.request.arg);
12383         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12384     },
12385
12386     // private
12387     update : function(dataSet){
12388
12389     },
12390
12391     // private
12392     updateResponse : function(dataSet){
12393
12394     }
12395 });/*
12396  * Based on:
12397  * Ext JS Library 1.1.1
12398  * Copyright(c) 2006-2007, Ext JS, LLC.
12399  *
12400  * Originally Released Under LGPL - original licence link has changed is not relivant.
12401  *
12402  * Fork - LGPL
12403  * <script type="text/javascript">
12404  */
12405
12406 /**
12407  * @class Roo.data.ScriptTagProxy
12408  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12409  * other than the originating domain of the running page.<br><br>
12410  * <p>
12411  * <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
12412  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12413  * <p>
12414  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12415  * source code that is used as the source inside a &lt;script> tag.<br><br>
12416  * <p>
12417  * In order for the browser to process the returned data, the server must wrap the data object
12418  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12419  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12420  * depending on whether the callback name was passed:
12421  * <p>
12422  * <pre><code>
12423 boolean scriptTag = false;
12424 String cb = request.getParameter("callback");
12425 if (cb != null) {
12426     scriptTag = true;
12427     response.setContentType("text/javascript");
12428 } else {
12429     response.setContentType("application/x-json");
12430 }
12431 Writer out = response.getWriter();
12432 if (scriptTag) {
12433     out.write(cb + "(");
12434 }
12435 out.print(dataBlock.toJsonString());
12436 if (scriptTag) {
12437     out.write(");");
12438 }
12439 </pre></code>
12440  *
12441  * @constructor
12442  * @param {Object} config A configuration object.
12443  */
12444 Roo.data.ScriptTagProxy = function(config){
12445     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12446     Roo.apply(this, config);
12447     this.head = document.getElementsByTagName("head")[0];
12448 };
12449
12450 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12451
12452 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12453     /**
12454      * @cfg {String} url The URL from which to request the data object.
12455      */
12456     /**
12457      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12458      */
12459     timeout : 30000,
12460     /**
12461      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12462      * the server the name of the callback function set up by the load call to process the returned data object.
12463      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12464      * javascript output which calls this named function passing the data object as its only parameter.
12465      */
12466     callbackParam : "callback",
12467     /**
12468      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12469      * name to the request.
12470      */
12471     nocache : true,
12472
12473     /**
12474      * Load data from the configured URL, read the data object into
12475      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12476      * process that block using the passed callback.
12477      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12478      * for the request to the remote server.
12479      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12480      * object into a block of Roo.data.Records.
12481      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12482      * The function must be passed <ul>
12483      * <li>The Record block object</li>
12484      * <li>The "arg" argument from the load function</li>
12485      * <li>A boolean success indicator</li>
12486      * </ul>
12487      * @param {Object} scope The scope in which to call the callback
12488      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12489      */
12490     load : function(params, reader, callback, scope, arg){
12491         if(this.fireEvent("beforeload", this, params) !== false){
12492
12493             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12494
12495             var url = this.url;
12496             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12497             if(this.nocache){
12498                 url += "&_dc=" + (new Date().getTime());
12499             }
12500             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12501             var trans = {
12502                 id : transId,
12503                 cb : "stcCallback"+transId,
12504                 scriptId : "stcScript"+transId,
12505                 params : params,
12506                 arg : arg,
12507                 url : url,
12508                 callback : callback,
12509                 scope : scope,
12510                 reader : reader
12511             };
12512             var conn = this;
12513
12514             window[trans.cb] = function(o){
12515                 conn.handleResponse(o, trans);
12516             };
12517
12518             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12519
12520             if(this.autoAbort !== false){
12521                 this.abort();
12522             }
12523
12524             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12525
12526             var script = document.createElement("script");
12527             script.setAttribute("src", url);
12528             script.setAttribute("type", "text/javascript");
12529             script.setAttribute("id", trans.scriptId);
12530             this.head.appendChild(script);
12531
12532             this.trans = trans;
12533         }else{
12534             callback.call(scope||this, null, arg, false);
12535         }
12536     },
12537
12538     // private
12539     isLoading : function(){
12540         return this.trans ? true : false;
12541     },
12542
12543     /**
12544      * Abort the current server request.
12545      */
12546     abort : function(){
12547         if(this.isLoading()){
12548             this.destroyTrans(this.trans);
12549         }
12550     },
12551
12552     // private
12553     destroyTrans : function(trans, isLoaded){
12554         this.head.removeChild(document.getElementById(trans.scriptId));
12555         clearTimeout(trans.timeoutId);
12556         if(isLoaded){
12557             window[trans.cb] = undefined;
12558             try{
12559                 delete window[trans.cb];
12560             }catch(e){}
12561         }else{
12562             // if hasn't been loaded, wait for load to remove it to prevent script error
12563             window[trans.cb] = function(){
12564                 window[trans.cb] = undefined;
12565                 try{
12566                     delete window[trans.cb];
12567                 }catch(e){}
12568             };
12569         }
12570     },
12571
12572     // private
12573     handleResponse : function(o, trans){
12574         this.trans = false;
12575         this.destroyTrans(trans, true);
12576         var result;
12577         try {
12578             result = trans.reader.readRecords(o);
12579         }catch(e){
12580             this.fireEvent("loadexception", this, o, trans.arg, e);
12581             trans.callback.call(trans.scope||window, null, trans.arg, false);
12582             return;
12583         }
12584         this.fireEvent("load", this, o, trans.arg);
12585         trans.callback.call(trans.scope||window, result, trans.arg, true);
12586     },
12587
12588     // private
12589     handleFailure : function(trans){
12590         this.trans = false;
12591         this.destroyTrans(trans, false);
12592         this.fireEvent("loadexception", this, null, trans.arg);
12593         trans.callback.call(trans.scope||window, null, trans.arg, false);
12594     }
12595 });/*
12596  * Based on:
12597  * Ext JS Library 1.1.1
12598  * Copyright(c) 2006-2007, Ext JS, LLC.
12599  *
12600  * Originally Released Under LGPL - original licence link has changed is not relivant.
12601  *
12602  * Fork - LGPL
12603  * <script type="text/javascript">
12604  */
12605
12606 /**
12607  * @class Roo.data.JsonReader
12608  * @extends Roo.data.DataReader
12609  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12610  * based on mappings in a provided Roo.data.Record constructor.
12611  * 
12612  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12613  * in the reply previously. 
12614  * 
12615  * <p>
12616  * Example code:
12617  * <pre><code>
12618 var RecordDef = Roo.data.Record.create([
12619     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12620     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12621 ]);
12622 var myReader = new Roo.data.JsonReader({
12623     totalProperty: "results",    // The property which contains the total dataset size (optional)
12624     root: "rows",                // The property which contains an Array of row objects
12625     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12626 }, RecordDef);
12627 </code></pre>
12628  * <p>
12629  * This would consume a JSON file like this:
12630  * <pre><code>
12631 { 'results': 2, 'rows': [
12632     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12633     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12634 }
12635 </code></pre>
12636  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12637  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12638  * paged from the remote server.
12639  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12640  * @cfg {String} root name of the property which contains the Array of row objects.
12641  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12642  * @cfg {Array} fields Array of field definition objects
12643  * @constructor
12644  * Create a new JsonReader
12645  * @param {Object} meta Metadata configuration options
12646  * @param {Object} recordType Either an Array of field definition objects,
12647  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12648  */
12649 Roo.data.JsonReader = function(meta, recordType){
12650     
12651     meta = meta || {};
12652     // set some defaults:
12653     Roo.applyIf(meta, {
12654         totalProperty: 'total',
12655         successProperty : 'success',
12656         root : 'data',
12657         id : 'id'
12658     });
12659     
12660     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12661 };
12662 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12663     
12664     /**
12665      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12666      * Used by Store query builder to append _requestMeta to params.
12667      * 
12668      */
12669     metaFromRemote : false,
12670     /**
12671      * This method is only used by a DataProxy which has retrieved data from a remote server.
12672      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12673      * @return {Object} data A data block which is used by an Roo.data.Store object as
12674      * a cache of Roo.data.Records.
12675      */
12676     read : function(response){
12677         var json = response.responseText;
12678        
12679         var o = /* eval:var:o */ eval("("+json+")");
12680         if(!o) {
12681             throw {message: "JsonReader.read: Json object not found"};
12682         }
12683         
12684         if(o.metaData){
12685             
12686             delete this.ef;
12687             this.metaFromRemote = true;
12688             this.meta = o.metaData;
12689             this.recordType = Roo.data.Record.create(o.metaData.fields);
12690             this.onMetaChange(this.meta, this.recordType, o);
12691         }
12692         return this.readRecords(o);
12693     },
12694
12695     // private function a store will implement
12696     onMetaChange : function(meta, recordType, o){
12697
12698     },
12699
12700     /**
12701          * @ignore
12702          */
12703     simpleAccess: function(obj, subsc) {
12704         return obj[subsc];
12705     },
12706
12707         /**
12708          * @ignore
12709          */
12710     getJsonAccessor: function(){
12711         var re = /[\[\.]/;
12712         return function(expr) {
12713             try {
12714                 return(re.test(expr))
12715                     ? new Function("obj", "return obj." + expr)
12716                     : function(obj){
12717                         return obj[expr];
12718                     };
12719             } catch(e){}
12720             return Roo.emptyFn;
12721         };
12722     }(),
12723
12724     /**
12725      * Create a data block containing Roo.data.Records from an XML document.
12726      * @param {Object} o An object which contains an Array of row objects in the property specified
12727      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12728      * which contains the total size of the dataset.
12729      * @return {Object} data A data block which is used by an Roo.data.Store object as
12730      * a cache of Roo.data.Records.
12731      */
12732     readRecords : function(o){
12733         /**
12734          * After any data loads, the raw JSON data is available for further custom processing.
12735          * @type Object
12736          */
12737         this.o = o;
12738         var s = this.meta, Record = this.recordType,
12739             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12740
12741 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12742         if (!this.ef) {
12743             if(s.totalProperty) {
12744                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12745                 }
12746                 if(s.successProperty) {
12747                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12748                 }
12749                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12750                 if (s.id) {
12751                         var g = this.getJsonAccessor(s.id);
12752                         this.getId = function(rec) {
12753                                 var r = g(rec);  
12754                                 return (r === undefined || r === "") ? null : r;
12755                         };
12756                 } else {
12757                         this.getId = function(){return null;};
12758                 }
12759             this.ef = [];
12760             for(var jj = 0; jj < fl; jj++){
12761                 f = fi[jj];
12762                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12763                 this.ef[jj] = this.getJsonAccessor(map);
12764             }
12765         }
12766
12767         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12768         if(s.totalProperty){
12769             var vt = parseInt(this.getTotal(o), 10);
12770             if(!isNaN(vt)){
12771                 totalRecords = vt;
12772             }
12773         }
12774         if(s.successProperty){
12775             var vs = this.getSuccess(o);
12776             if(vs === false || vs === 'false'){
12777                 success = false;
12778             }
12779         }
12780         var records = [];
12781         for(var i = 0; i < c; i++){
12782                 var n = root[i];
12783             var values = {};
12784             var id = this.getId(n);
12785             for(var j = 0; j < fl; j++){
12786                 f = fi[j];
12787             var v = this.ef[j](n);
12788             if (!f.convert) {
12789                 Roo.log('missing convert for ' + f.name);
12790                 Roo.log(f);
12791                 continue;
12792             }
12793             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12794             }
12795             var record = new Record(values, id);
12796             record.json = n;
12797             records[i] = record;
12798         }
12799         return {
12800             raw : o,
12801             success : success,
12802             records : records,
12803             totalRecords : totalRecords
12804         };
12805     }
12806 });/*
12807  * Based on:
12808  * Ext JS Library 1.1.1
12809  * Copyright(c) 2006-2007, Ext JS, LLC.
12810  *
12811  * Originally Released Under LGPL - original licence link has changed is not relivant.
12812  *
12813  * Fork - LGPL
12814  * <script type="text/javascript">
12815  */
12816
12817 /**
12818  * @class Roo.data.ArrayReader
12819  * @extends Roo.data.DataReader
12820  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12821  * Each element of that Array represents a row of data fields. The
12822  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12823  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12824  * <p>
12825  * Example code:.
12826  * <pre><code>
12827 var RecordDef = Roo.data.Record.create([
12828     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12829     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12830 ]);
12831 var myReader = new Roo.data.ArrayReader({
12832     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12833 }, RecordDef);
12834 </code></pre>
12835  * <p>
12836  * This would consume an Array like this:
12837  * <pre><code>
12838 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12839   </code></pre>
12840  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12841  * @constructor
12842  * Create a new JsonReader
12843  * @param {Object} meta Metadata configuration options.
12844  * @param {Object} recordType Either an Array of field definition objects
12845  * as specified to {@link Roo.data.Record#create},
12846  * or an {@link Roo.data.Record} object
12847  * created using {@link Roo.data.Record#create}.
12848  */
12849 Roo.data.ArrayReader = function(meta, recordType){
12850     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12851 };
12852
12853 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12854     /**
12855      * Create a data block containing Roo.data.Records from an XML document.
12856      * @param {Object} o An Array of row objects which represents the dataset.
12857      * @return {Object} data A data block which is used by an Roo.data.Store object as
12858      * a cache of Roo.data.Records.
12859      */
12860     readRecords : function(o){
12861         var sid = this.meta ? this.meta.id : null;
12862         var recordType = this.recordType, fields = recordType.prototype.fields;
12863         var records = [];
12864         var root = o;
12865             for(var i = 0; i < root.length; i++){
12866                     var n = root[i];
12867                 var values = {};
12868                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12869                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12870                 var f = fields.items[j];
12871                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12872                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12873                 v = f.convert(v);
12874                 values[f.name] = v;
12875             }
12876                 var record = new recordType(values, id);
12877                 record.json = n;
12878                 records[records.length] = record;
12879             }
12880             return {
12881                 records : records,
12882                 totalRecords : records.length
12883             };
12884     }
12885 });/*
12886  * - LGPL
12887  * * 
12888  */
12889
12890 /**
12891  * @class Roo.bootstrap.ComboBox
12892  * @extends Roo.bootstrap.TriggerField
12893  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12894  * @cfg {Boolean} append (true|false) default false
12895  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12896  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12897  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12898  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12899  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12900  * @cfg {Boolean} animate default true
12901  * @cfg {Boolean} emptyResultText only for touch device
12902  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12903  * @cfg {String} emptyTitle default ''
12904  * @constructor
12905  * Create a new ComboBox.
12906  * @param {Object} config Configuration options
12907  */
12908 Roo.bootstrap.ComboBox = function(config){
12909     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12910     this.addEvents({
12911         /**
12912          * @event expand
12913          * Fires when the dropdown list is expanded
12914         * @param {Roo.bootstrap.ComboBox} combo This combo box
12915         */
12916         'expand' : true,
12917         /**
12918          * @event collapse
12919          * Fires when the dropdown list is collapsed
12920         * @param {Roo.bootstrap.ComboBox} combo This combo box
12921         */
12922         'collapse' : true,
12923         /**
12924          * @event beforeselect
12925          * Fires before a list item is selected. Return false to cancel the selection.
12926         * @param {Roo.bootstrap.ComboBox} combo This combo box
12927         * @param {Roo.data.Record} record The data record returned from the underlying store
12928         * @param {Number} index The index of the selected item in the dropdown list
12929         */
12930         'beforeselect' : true,
12931         /**
12932          * @event select
12933          * Fires when a list item is selected
12934         * @param {Roo.bootstrap.ComboBox} combo This combo box
12935         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12936         * @param {Number} index The index of the selected item in the dropdown list
12937         */
12938         'select' : true,
12939         /**
12940          * @event beforequery
12941          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12942          * The event object passed has these properties:
12943         * @param {Roo.bootstrap.ComboBox} combo This combo box
12944         * @param {String} query The query
12945         * @param {Boolean} forceAll true to force "all" query
12946         * @param {Boolean} cancel true to cancel the query
12947         * @param {Object} e The query event object
12948         */
12949         'beforequery': true,
12950          /**
12951          * @event add
12952          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12953         * @param {Roo.bootstrap.ComboBox} combo This combo box
12954         */
12955         'add' : true,
12956         /**
12957          * @event edit
12958          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12959         * @param {Roo.bootstrap.ComboBox} combo This combo box
12960         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12961         */
12962         'edit' : true,
12963         /**
12964          * @event remove
12965          * Fires when the remove value from the combobox array
12966         * @param {Roo.bootstrap.ComboBox} combo This combo box
12967         */
12968         'remove' : true,
12969         /**
12970          * @event afterremove
12971          * Fires when the remove value from the combobox array
12972         * @param {Roo.bootstrap.ComboBox} combo This combo box
12973         */
12974         'afterremove' : true,
12975         /**
12976          * @event specialfilter
12977          * Fires when specialfilter
12978             * @param {Roo.bootstrap.ComboBox} combo This combo box
12979             */
12980         'specialfilter' : true,
12981         /**
12982          * @event tick
12983          * Fires when tick the element
12984             * @param {Roo.bootstrap.ComboBox} combo This combo box
12985             */
12986         'tick' : true,
12987         /**
12988          * @event touchviewdisplay
12989          * Fires when touch view require special display (default is using displayField)
12990             * @param {Roo.bootstrap.ComboBox} combo This combo box
12991             * @param {Object} cfg set html .
12992             */
12993         'touchviewdisplay' : true
12994         
12995     });
12996     
12997     this.item = [];
12998     this.tickItems = [];
12999     
13000     this.selectedIndex = -1;
13001     if(this.mode == 'local'){
13002         if(config.queryDelay === undefined){
13003             this.queryDelay = 10;
13004         }
13005         if(config.minChars === undefined){
13006             this.minChars = 0;
13007         }
13008     }
13009 };
13010
13011 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13012      
13013     /**
13014      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13015      * rendering into an Roo.Editor, defaults to false)
13016      */
13017     /**
13018      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13019      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13020      */
13021     /**
13022      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13023      */
13024     /**
13025      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13026      * the dropdown list (defaults to undefined, with no header element)
13027      */
13028
13029      /**
13030      * @cfg {String/Roo.Template} tpl The template to use to render the output
13031      */
13032      
13033      /**
13034      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13035      */
13036     listWidth: undefined,
13037     /**
13038      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13039      * mode = 'remote' or 'text' if mode = 'local')
13040      */
13041     displayField: undefined,
13042     
13043     /**
13044      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13045      * mode = 'remote' or 'value' if mode = 'local'). 
13046      * Note: use of a valueField requires the user make a selection
13047      * in order for a value to be mapped.
13048      */
13049     valueField: undefined,
13050     /**
13051      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13052      */
13053     modalTitle : '',
13054     
13055     /**
13056      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13057      * field's data value (defaults to the underlying DOM element's name)
13058      */
13059     hiddenName: undefined,
13060     /**
13061      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13062      */
13063     listClass: '',
13064     /**
13065      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13066      */
13067     selectedClass: 'active',
13068     
13069     /**
13070      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13071      */
13072     shadow:'sides',
13073     /**
13074      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13075      * anchor positions (defaults to 'tl-bl')
13076      */
13077     listAlign: 'tl-bl?',
13078     /**
13079      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13080      */
13081     maxHeight: 300,
13082     /**
13083      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13084      * query specified by the allQuery config option (defaults to 'query')
13085      */
13086     triggerAction: 'query',
13087     /**
13088      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13089      * (defaults to 4, does not apply if editable = false)
13090      */
13091     minChars : 4,
13092     /**
13093      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13094      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13095      */
13096     typeAhead: false,
13097     /**
13098      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13099      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13100      */
13101     queryDelay: 500,
13102     /**
13103      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13104      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13105      */
13106     pageSize: 0,
13107     /**
13108      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13109      * when editable = true (defaults to false)
13110      */
13111     selectOnFocus:false,
13112     /**
13113      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13114      */
13115     queryParam: 'query',
13116     /**
13117      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13118      * when mode = 'remote' (defaults to 'Loading...')
13119      */
13120     loadingText: 'Loading...',
13121     /**
13122      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13123      */
13124     resizable: false,
13125     /**
13126      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13127      */
13128     handleHeight : 8,
13129     /**
13130      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13131      * traditional select (defaults to true)
13132      */
13133     editable: true,
13134     /**
13135      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13136      */
13137     allQuery: '',
13138     /**
13139      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13140      */
13141     mode: 'remote',
13142     /**
13143      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13144      * listWidth has a higher value)
13145      */
13146     minListWidth : 70,
13147     /**
13148      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13149      * allow the user to set arbitrary text into the field (defaults to false)
13150      */
13151     forceSelection:false,
13152     /**
13153      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13154      * if typeAhead = true (defaults to 250)
13155      */
13156     typeAheadDelay : 250,
13157     /**
13158      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13159      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13160      */
13161     valueNotFoundText : undefined,
13162     /**
13163      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13164      */
13165     blockFocus : false,
13166     
13167     /**
13168      * @cfg {Boolean} disableClear Disable showing of clear button.
13169      */
13170     disableClear : false,
13171     /**
13172      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13173      */
13174     alwaysQuery : false,
13175     
13176     /**
13177      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13178      */
13179     multiple : false,
13180     
13181     /**
13182      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13183      */
13184     invalidClass : "has-warning",
13185     
13186     /**
13187      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13188      */
13189     validClass : "has-success",
13190     
13191     /**
13192      * @cfg {Boolean} specialFilter (true|false) special filter default false
13193      */
13194     specialFilter : false,
13195     
13196     /**
13197      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13198      */
13199     mobileTouchView : true,
13200     
13201     /**
13202      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13203      */
13204     useNativeIOS : false,
13205     
13206     /**
13207      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13208      */
13209     mobile_restrict_height : false,
13210     
13211     ios_options : false,
13212     
13213     //private
13214     addicon : false,
13215     editicon: false,
13216     
13217     page: 0,
13218     hasQuery: false,
13219     append: false,
13220     loadNext: false,
13221     autoFocus : true,
13222     tickable : false,
13223     btnPosition : 'right',
13224     triggerList : true,
13225     showToggleBtn : true,
13226     animate : true,
13227     emptyResultText: 'Empty',
13228     triggerText : 'Select',
13229     emptyTitle : '',
13230     
13231     // element that contains real text value.. (when hidden is used..)
13232     
13233     getAutoCreate : function()
13234     {   
13235         var cfg = false;
13236         //render
13237         /*
13238          * Render classic select for iso
13239          */
13240         
13241         if(Roo.isIOS && this.useNativeIOS){
13242             cfg = this.getAutoCreateNativeIOS();
13243             return cfg;
13244         }
13245         
13246         /*
13247          * Touch Devices
13248          */
13249         
13250         if(Roo.isTouch && this.mobileTouchView){
13251             cfg = this.getAutoCreateTouchView();
13252             return cfg;;
13253         }
13254         
13255         /*
13256          *  Normal ComboBox
13257          */
13258         if(!this.tickable){
13259             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13260             return cfg;
13261         }
13262         
13263         /*
13264          *  ComboBox with tickable selections
13265          */
13266              
13267         var align = this.labelAlign || this.parentLabelAlign();
13268         
13269         cfg = {
13270             cls : 'form-group roo-combobox-tickable' //input-group
13271         };
13272         
13273         var btn_text_select = '';
13274         var btn_text_done = '';
13275         var btn_text_cancel = '';
13276         
13277         if (this.btn_text_show) {
13278             btn_text_select = 'Select';
13279             btn_text_done = 'Done';
13280             btn_text_cancel = 'Cancel'; 
13281         }
13282         
13283         var buttons = {
13284             tag : 'div',
13285             cls : 'tickable-buttons',
13286             cn : [
13287                 {
13288                     tag : 'button',
13289                     type : 'button',
13290                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13291                     //html : this.triggerText
13292                     html: btn_text_select
13293                 },
13294                 {
13295                     tag : 'button',
13296                     type : 'button',
13297                     name : 'ok',
13298                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13299                     //html : 'Done'
13300                     html: btn_text_done
13301                 },
13302                 {
13303                     tag : 'button',
13304                     type : 'button',
13305                     name : 'cancel',
13306                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13307                     //html : 'Cancel'
13308                     html: btn_text_cancel
13309                 }
13310             ]
13311         };
13312         
13313         if(this.editable){
13314             buttons.cn.unshift({
13315                 tag: 'input',
13316                 cls: 'roo-select2-search-field-input'
13317             });
13318         }
13319         
13320         var _this = this;
13321         
13322         Roo.each(buttons.cn, function(c){
13323             if (_this.size) {
13324                 c.cls += ' btn-' + _this.size;
13325             }
13326
13327             if (_this.disabled) {
13328                 c.disabled = true;
13329             }
13330         });
13331         
13332         var box = {
13333             tag: 'div',
13334             cn: [
13335                 {
13336                     tag: 'input',
13337                     type : 'hidden',
13338                     cls: 'form-hidden-field'
13339                 },
13340                 {
13341                     tag: 'ul',
13342                     cls: 'roo-select2-choices',
13343                     cn:[
13344                         {
13345                             tag: 'li',
13346                             cls: 'roo-select2-search-field',
13347                             cn: [
13348                                 buttons
13349                             ]
13350                         }
13351                     ]
13352                 }
13353             ]
13354         };
13355         
13356         var combobox = {
13357             cls: 'roo-select2-container input-group roo-select2-container-multi',
13358             cn: [
13359                 
13360                 box
13361 //                {
13362 //                    tag: 'ul',
13363 //                    cls: 'typeahead typeahead-long dropdown-menu',
13364 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13365 //                }
13366             ]
13367         };
13368         
13369         if(this.hasFeedback && !this.allowBlank){
13370             
13371             var feedback = {
13372                 tag: 'span',
13373                 cls: 'glyphicon form-control-feedback'
13374             };
13375
13376             combobox.cn.push(feedback);
13377         }
13378         
13379         var indicator = {
13380             tag : 'i',
13381             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13382             tooltip : 'This field is required'
13383         };
13384         if (Roo.bootstrap.version == 4) {
13385             indicator = {
13386                 tag : 'i',
13387                 style : 'display:none'
13388             };
13389         }
13390         if (align ==='left' && this.fieldLabel.length) {
13391             
13392             cfg.cls += ' roo-form-group-label-left row';
13393             
13394             cfg.cn = [
13395                 indicator,
13396                 {
13397                     tag: 'label',
13398                     'for' :  id,
13399                     cls : 'control-label col-form-label',
13400                     html : this.fieldLabel
13401
13402                 },
13403                 {
13404                     cls : "", 
13405                     cn: [
13406                         combobox
13407                     ]
13408                 }
13409
13410             ];
13411             
13412             var labelCfg = cfg.cn[1];
13413             var contentCfg = cfg.cn[2];
13414             
13415
13416             if(this.indicatorpos == 'right'){
13417                 
13418                 cfg.cn = [
13419                     {
13420                         tag: 'label',
13421                         'for' :  id,
13422                         cls : 'control-label col-form-label',
13423                         cn : [
13424                             {
13425                                 tag : 'span',
13426                                 html : this.fieldLabel
13427                             },
13428                             indicator
13429                         ]
13430                     },
13431                     {
13432                         cls : "",
13433                         cn: [
13434                             combobox
13435                         ]
13436                     }
13437
13438                 ];
13439                 
13440                 
13441                 
13442                 labelCfg = cfg.cn[0];
13443                 contentCfg = cfg.cn[1];
13444             
13445             }
13446             
13447             if(this.labelWidth > 12){
13448                 labelCfg.style = "width: " + this.labelWidth + 'px';
13449             }
13450             
13451             if(this.labelWidth < 13 && this.labelmd == 0){
13452                 this.labelmd = this.labelWidth;
13453             }
13454             
13455             if(this.labellg > 0){
13456                 labelCfg.cls += ' col-lg-' + this.labellg;
13457                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13458             }
13459             
13460             if(this.labelmd > 0){
13461                 labelCfg.cls += ' col-md-' + this.labelmd;
13462                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13463             }
13464             
13465             if(this.labelsm > 0){
13466                 labelCfg.cls += ' col-sm-' + this.labelsm;
13467                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13468             }
13469             
13470             if(this.labelxs > 0){
13471                 labelCfg.cls += ' col-xs-' + this.labelxs;
13472                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13473             }
13474                 
13475                 
13476         } else if ( this.fieldLabel.length) {
13477 //                Roo.log(" label");
13478                  cfg.cn = [
13479                    indicator,
13480                     {
13481                         tag: 'label',
13482                         //cls : 'input-group-addon',
13483                         html : this.fieldLabel
13484                     },
13485                     combobox
13486                 ];
13487                 
13488                 if(this.indicatorpos == 'right'){
13489                     cfg.cn = [
13490                         {
13491                             tag: 'label',
13492                             //cls : 'input-group-addon',
13493                             html : this.fieldLabel
13494                         },
13495                         indicator,
13496                         combobox
13497                     ];
13498                     
13499                 }
13500
13501         } else {
13502             
13503 //                Roo.log(" no label && no align");
13504                 cfg = combobox
13505                      
13506                 
13507         }
13508          
13509         var settings=this;
13510         ['xs','sm','md','lg'].map(function(size){
13511             if (settings[size]) {
13512                 cfg.cls += ' col-' + size + '-' + settings[size];
13513             }
13514         });
13515         
13516         return cfg;
13517         
13518     },
13519     
13520     _initEventsCalled : false,
13521     
13522     // private
13523     initEvents: function()
13524     {   
13525         if (this._initEventsCalled) { // as we call render... prevent looping...
13526             return;
13527         }
13528         this._initEventsCalled = true;
13529         
13530         if (!this.store) {
13531             throw "can not find store for combo";
13532         }
13533         
13534         this.indicator = this.indicatorEl();
13535         
13536         this.store = Roo.factory(this.store, Roo.data);
13537         this.store.parent = this;
13538         
13539         // if we are building from html. then this element is so complex, that we can not really
13540         // use the rendered HTML.
13541         // so we have to trash and replace the previous code.
13542         if (Roo.XComponent.build_from_html) {
13543             // remove this element....
13544             var e = this.el.dom, k=0;
13545             while (e ) { e = e.previousSibling;  ++k;}
13546
13547             this.el.remove();
13548             
13549             this.el=false;
13550             this.rendered = false;
13551             
13552             this.render(this.parent().getChildContainer(true), k);
13553         }
13554         
13555         if(Roo.isIOS && this.useNativeIOS){
13556             this.initIOSView();
13557             return;
13558         }
13559         
13560         /*
13561          * Touch Devices
13562          */
13563         
13564         if(Roo.isTouch && this.mobileTouchView){
13565             this.initTouchView();
13566             return;
13567         }
13568         
13569         if(this.tickable){
13570             this.initTickableEvents();
13571             return;
13572         }
13573         
13574         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13575         
13576         if(this.hiddenName){
13577             
13578             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13579             
13580             this.hiddenField.dom.value =
13581                 this.hiddenValue !== undefined ? this.hiddenValue :
13582                 this.value !== undefined ? this.value : '';
13583
13584             // prevent input submission
13585             this.el.dom.removeAttribute('name');
13586             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13587              
13588              
13589         }
13590         //if(Roo.isGecko){
13591         //    this.el.dom.setAttribute('autocomplete', 'off');
13592         //}
13593         
13594         var cls = 'x-combo-list';
13595         
13596         //this.list = new Roo.Layer({
13597         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13598         //});
13599         
13600         var _this = this;
13601         
13602         (function(){
13603             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13604             _this.list.setWidth(lw);
13605         }).defer(100);
13606         
13607         this.list.on('mouseover', this.onViewOver, this);
13608         this.list.on('mousemove', this.onViewMove, this);
13609         this.list.on('scroll', this.onViewScroll, this);
13610         
13611         /*
13612         this.list.swallowEvent('mousewheel');
13613         this.assetHeight = 0;
13614
13615         if(this.title){
13616             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13617             this.assetHeight += this.header.getHeight();
13618         }
13619
13620         this.innerList = this.list.createChild({cls:cls+'-inner'});
13621         this.innerList.on('mouseover', this.onViewOver, this);
13622         this.innerList.on('mousemove', this.onViewMove, this);
13623         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13624         
13625         if(this.allowBlank && !this.pageSize && !this.disableClear){
13626             this.footer = this.list.createChild({cls:cls+'-ft'});
13627             this.pageTb = new Roo.Toolbar(this.footer);
13628            
13629         }
13630         if(this.pageSize){
13631             this.footer = this.list.createChild({cls:cls+'-ft'});
13632             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13633                     {pageSize: this.pageSize});
13634             
13635         }
13636         
13637         if (this.pageTb && this.allowBlank && !this.disableClear) {
13638             var _this = this;
13639             this.pageTb.add(new Roo.Toolbar.Fill(), {
13640                 cls: 'x-btn-icon x-btn-clear',
13641                 text: '&#160;',
13642                 handler: function()
13643                 {
13644                     _this.collapse();
13645                     _this.clearValue();
13646                     _this.onSelect(false, -1);
13647                 }
13648             });
13649         }
13650         if (this.footer) {
13651             this.assetHeight += this.footer.getHeight();
13652         }
13653         */
13654             
13655         if(!this.tpl){
13656             this.tpl = Roo.bootstrap.version == 4 ?
13657                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13658                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13659         }
13660
13661         this.view = new Roo.View(this.list, this.tpl, {
13662             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13663         });
13664         //this.view.wrapEl.setDisplayed(false);
13665         this.view.on('click', this.onViewClick, this);
13666         
13667         
13668         this.store.on('beforeload', this.onBeforeLoad, this);
13669         this.store.on('load', this.onLoad, this);
13670         this.store.on('loadexception', this.onLoadException, this);
13671         /*
13672         if(this.resizable){
13673             this.resizer = new Roo.Resizable(this.list,  {
13674                pinned:true, handles:'se'
13675             });
13676             this.resizer.on('resize', function(r, w, h){
13677                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13678                 this.listWidth = w;
13679                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13680                 this.restrictHeight();
13681             }, this);
13682             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13683         }
13684         */
13685         if(!this.editable){
13686             this.editable = true;
13687             this.setEditable(false);
13688         }
13689         
13690         /*
13691         
13692         if (typeof(this.events.add.listeners) != 'undefined') {
13693             
13694             this.addicon = this.wrap.createChild(
13695                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13696        
13697             this.addicon.on('click', function(e) {
13698                 this.fireEvent('add', this);
13699             }, this);
13700         }
13701         if (typeof(this.events.edit.listeners) != 'undefined') {
13702             
13703             this.editicon = this.wrap.createChild(
13704                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13705             if (this.addicon) {
13706                 this.editicon.setStyle('margin-left', '40px');
13707             }
13708             this.editicon.on('click', function(e) {
13709                 
13710                 // we fire even  if inothing is selected..
13711                 this.fireEvent('edit', this, this.lastData );
13712                 
13713             }, this);
13714         }
13715         */
13716         
13717         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13718             "up" : function(e){
13719                 this.inKeyMode = true;
13720                 this.selectPrev();
13721             },
13722
13723             "down" : function(e){
13724                 if(!this.isExpanded()){
13725                     this.onTriggerClick();
13726                 }else{
13727                     this.inKeyMode = true;
13728                     this.selectNext();
13729                 }
13730             },
13731
13732             "enter" : function(e){
13733 //                this.onViewClick();
13734                 //return true;
13735                 this.collapse();
13736                 
13737                 if(this.fireEvent("specialkey", this, e)){
13738                     this.onViewClick(false);
13739                 }
13740                 
13741                 return true;
13742             },
13743
13744             "esc" : function(e){
13745                 this.collapse();
13746             },
13747
13748             "tab" : function(e){
13749                 this.collapse();
13750                 
13751                 if(this.fireEvent("specialkey", this, e)){
13752                     this.onViewClick(false);
13753                 }
13754                 
13755                 return true;
13756             },
13757
13758             scope : this,
13759
13760             doRelay : function(foo, bar, hname){
13761                 if(hname == 'down' || this.scope.isExpanded()){
13762                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13763                 }
13764                 return true;
13765             },
13766
13767             forceKeyDown: true
13768         });
13769         
13770         
13771         this.queryDelay = Math.max(this.queryDelay || 10,
13772                 this.mode == 'local' ? 10 : 250);
13773         
13774         
13775         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13776         
13777         if(this.typeAhead){
13778             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13779         }
13780         if(this.editable !== false){
13781             this.inputEl().on("keyup", this.onKeyUp, this);
13782         }
13783         if(this.forceSelection){
13784             this.inputEl().on('blur', this.doForce, this);
13785         }
13786         
13787         if(this.multiple){
13788             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13789             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13790         }
13791     },
13792     
13793     initTickableEvents: function()
13794     {   
13795         this.createList();
13796         
13797         if(this.hiddenName){
13798             
13799             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13800             
13801             this.hiddenField.dom.value =
13802                 this.hiddenValue !== undefined ? this.hiddenValue :
13803                 this.value !== undefined ? this.value : '';
13804
13805             // prevent input submission
13806             this.el.dom.removeAttribute('name');
13807             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13808              
13809              
13810         }
13811         
13812 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13813         
13814         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13815         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13816         if(this.triggerList){
13817             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13818         }
13819          
13820         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13821         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13822         
13823         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13824         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13825         
13826         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13827         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13828         
13829         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13830         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13831         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13832         
13833         this.okBtn.hide();
13834         this.cancelBtn.hide();
13835         
13836         var _this = this;
13837         
13838         (function(){
13839             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13840             _this.list.setWidth(lw);
13841         }).defer(100);
13842         
13843         this.list.on('mouseover', this.onViewOver, this);
13844         this.list.on('mousemove', this.onViewMove, this);
13845         
13846         this.list.on('scroll', this.onViewScroll, this);
13847         
13848         if(!this.tpl){
13849             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13850                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13851         }
13852
13853         this.view = new Roo.View(this.list, this.tpl, {
13854             singleSelect:true,
13855             tickable:true,
13856             parent:this,
13857             store: this.store,
13858             selectedClass: this.selectedClass
13859         });
13860         
13861         //this.view.wrapEl.setDisplayed(false);
13862         this.view.on('click', this.onViewClick, this);
13863         
13864         
13865         
13866         this.store.on('beforeload', this.onBeforeLoad, this);
13867         this.store.on('load', this.onLoad, this);
13868         this.store.on('loadexception', this.onLoadException, this);
13869         
13870         if(this.editable){
13871             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13872                 "up" : function(e){
13873                     this.inKeyMode = true;
13874                     this.selectPrev();
13875                 },
13876
13877                 "down" : function(e){
13878                     this.inKeyMode = true;
13879                     this.selectNext();
13880                 },
13881
13882                 "enter" : function(e){
13883                     if(this.fireEvent("specialkey", this, e)){
13884                         this.onViewClick(false);
13885                     }
13886                     
13887                     return true;
13888                 },
13889
13890                 "esc" : function(e){
13891                     this.onTickableFooterButtonClick(e, false, false);
13892                 },
13893
13894                 "tab" : function(e){
13895                     this.fireEvent("specialkey", this, e);
13896                     
13897                     this.onTickableFooterButtonClick(e, false, false);
13898                     
13899                     return true;
13900                 },
13901
13902                 scope : this,
13903
13904                 doRelay : function(e, fn, key){
13905                     if(this.scope.isExpanded()){
13906                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13907                     }
13908                     return true;
13909                 },
13910
13911                 forceKeyDown: true
13912             });
13913         }
13914         
13915         this.queryDelay = Math.max(this.queryDelay || 10,
13916                 this.mode == 'local' ? 10 : 250);
13917         
13918         
13919         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13920         
13921         if(this.typeAhead){
13922             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13923         }
13924         
13925         if(this.editable !== false){
13926             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13927         }
13928         
13929         this.indicator = this.indicatorEl();
13930         
13931         if(this.indicator){
13932             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13933             this.indicator.hide();
13934         }
13935         
13936     },
13937
13938     onDestroy : function(){
13939         if(this.view){
13940             this.view.setStore(null);
13941             this.view.el.removeAllListeners();
13942             this.view.el.remove();
13943             this.view.purgeListeners();
13944         }
13945         if(this.list){
13946             this.list.dom.innerHTML  = '';
13947         }
13948         
13949         if(this.store){
13950             this.store.un('beforeload', this.onBeforeLoad, this);
13951             this.store.un('load', this.onLoad, this);
13952             this.store.un('loadexception', this.onLoadException, this);
13953         }
13954         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13955     },
13956
13957     // private
13958     fireKey : function(e){
13959         if(e.isNavKeyPress() && !this.list.isVisible()){
13960             this.fireEvent("specialkey", this, e);
13961         }
13962     },
13963
13964     // private
13965     onResize: function(w, h){
13966 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13967 //        
13968 //        if(typeof w != 'number'){
13969 //            // we do not handle it!?!?
13970 //            return;
13971 //        }
13972 //        var tw = this.trigger.getWidth();
13973 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13974 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13975 //        var x = w - tw;
13976 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13977 //            
13978 //        //this.trigger.setStyle('left', x+'px');
13979 //        
13980 //        if(this.list && this.listWidth === undefined){
13981 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13982 //            this.list.setWidth(lw);
13983 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13984 //        }
13985         
13986     
13987         
13988     },
13989
13990     /**
13991      * Allow or prevent the user from directly editing the field text.  If false is passed,
13992      * the user will only be able to select from the items defined in the dropdown list.  This method
13993      * is the runtime equivalent of setting the 'editable' config option at config time.
13994      * @param {Boolean} value True to allow the user to directly edit the field text
13995      */
13996     setEditable : function(value){
13997         if(value == this.editable){
13998             return;
13999         }
14000         this.editable = value;
14001         if(!value){
14002             this.inputEl().dom.setAttribute('readOnly', true);
14003             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14004             this.inputEl().addClass('x-combo-noedit');
14005         }else{
14006             this.inputEl().dom.setAttribute('readOnly', false);
14007             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14008             this.inputEl().removeClass('x-combo-noedit');
14009         }
14010     },
14011
14012     // private
14013     
14014     onBeforeLoad : function(combo,opts){
14015         if(!this.hasFocus){
14016             return;
14017         }
14018          if (!opts.add) {
14019             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14020          }
14021         this.restrictHeight();
14022         this.selectedIndex = -1;
14023     },
14024
14025     // private
14026     onLoad : function(){
14027         
14028         this.hasQuery = false;
14029         
14030         if(!this.hasFocus){
14031             return;
14032         }
14033         
14034         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14035             this.loading.hide();
14036         }
14037         
14038         if(this.store.getCount() > 0){
14039             
14040             this.expand();
14041             this.restrictHeight();
14042             if(this.lastQuery == this.allQuery){
14043                 if(this.editable && !this.tickable){
14044                     this.inputEl().dom.select();
14045                 }
14046                 
14047                 if(
14048                     !this.selectByValue(this.value, true) &&
14049                     this.autoFocus && 
14050                     (
14051                         !this.store.lastOptions ||
14052                         typeof(this.store.lastOptions.add) == 'undefined' || 
14053                         this.store.lastOptions.add != true
14054                     )
14055                 ){
14056                     this.select(0, true);
14057                 }
14058             }else{
14059                 if(this.autoFocus){
14060                     this.selectNext();
14061                 }
14062                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14063                     this.taTask.delay(this.typeAheadDelay);
14064                 }
14065             }
14066         }else{
14067             this.onEmptyResults();
14068         }
14069         
14070         //this.el.focus();
14071     },
14072     // private
14073     onLoadException : function()
14074     {
14075         this.hasQuery = false;
14076         
14077         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14078             this.loading.hide();
14079         }
14080         
14081         if(this.tickable && this.editable){
14082             return;
14083         }
14084         
14085         this.collapse();
14086         // only causes errors at present
14087         //Roo.log(this.store.reader.jsonData);
14088         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14089             // fixme
14090             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14091         //}
14092         
14093         
14094     },
14095     // private
14096     onTypeAhead : function(){
14097         if(this.store.getCount() > 0){
14098             var r = this.store.getAt(0);
14099             var newValue = r.data[this.displayField];
14100             var len = newValue.length;
14101             var selStart = this.getRawValue().length;
14102             
14103             if(selStart != len){
14104                 this.setRawValue(newValue);
14105                 this.selectText(selStart, newValue.length);
14106             }
14107         }
14108     },
14109
14110     // private
14111     onSelect : function(record, index){
14112         
14113         if(this.fireEvent('beforeselect', this, record, index) !== false){
14114         
14115             this.setFromData(index > -1 ? record.data : false);
14116             
14117             this.collapse();
14118             this.fireEvent('select', this, record, index);
14119         }
14120     },
14121
14122     /**
14123      * Returns the currently selected field value or empty string if no value is set.
14124      * @return {String} value The selected value
14125      */
14126     getValue : function()
14127     {
14128         if(Roo.isIOS && this.useNativeIOS){
14129             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14130         }
14131         
14132         if(this.multiple){
14133             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14134         }
14135         
14136         if(this.valueField){
14137             return typeof this.value != 'undefined' ? this.value : '';
14138         }else{
14139             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14140         }
14141     },
14142     
14143     getRawValue : function()
14144     {
14145         if(Roo.isIOS && this.useNativeIOS){
14146             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14147         }
14148         
14149         var v = this.inputEl().getValue();
14150         
14151         return v;
14152     },
14153
14154     /**
14155      * Clears any text/value currently set in the field
14156      */
14157     clearValue : function(){
14158         
14159         if(this.hiddenField){
14160             this.hiddenField.dom.value = '';
14161         }
14162         this.value = '';
14163         this.setRawValue('');
14164         this.lastSelectionText = '';
14165         this.lastData = false;
14166         
14167         var close = this.closeTriggerEl();
14168         
14169         if(close){
14170             close.hide();
14171         }
14172         
14173         this.validate();
14174         
14175     },
14176
14177     /**
14178      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14179      * will be displayed in the field.  If the value does not match the data value of an existing item,
14180      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14181      * Otherwise the field will be blank (although the value will still be set).
14182      * @param {String} value The value to match
14183      */
14184     setValue : function(v)
14185     {
14186         if(Roo.isIOS && this.useNativeIOS){
14187             this.setIOSValue(v);
14188             return;
14189         }
14190         
14191         if(this.multiple){
14192             this.syncValue();
14193             return;
14194         }
14195         
14196         var text = v;
14197         if(this.valueField){
14198             var r = this.findRecord(this.valueField, v);
14199             if(r){
14200                 text = r.data[this.displayField];
14201             }else if(this.valueNotFoundText !== undefined){
14202                 text = this.valueNotFoundText;
14203             }
14204         }
14205         this.lastSelectionText = text;
14206         if(this.hiddenField){
14207             this.hiddenField.dom.value = v;
14208         }
14209         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14210         this.value = v;
14211         
14212         var close = this.closeTriggerEl();
14213         
14214         if(close){
14215             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14216         }
14217         
14218         this.validate();
14219     },
14220     /**
14221      * @property {Object} the last set data for the element
14222      */
14223     
14224     lastData : false,
14225     /**
14226      * Sets the value of the field based on a object which is related to the record format for the store.
14227      * @param {Object} value the value to set as. or false on reset?
14228      */
14229     setFromData : function(o){
14230         
14231         if(this.multiple){
14232             this.addItem(o);
14233             return;
14234         }
14235             
14236         var dv = ''; // display value
14237         var vv = ''; // value value..
14238         this.lastData = o;
14239         if (this.displayField) {
14240             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14241         } else {
14242             // this is an error condition!!!
14243             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14244         }
14245         
14246         if(this.valueField){
14247             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14248         }
14249         
14250         var close = this.closeTriggerEl();
14251         
14252         if(close){
14253             if(dv.length || vv * 1 > 0){
14254                 close.show() ;
14255                 this.blockFocus=true;
14256             } else {
14257                 close.hide();
14258             }             
14259         }
14260         
14261         if(this.hiddenField){
14262             this.hiddenField.dom.value = vv;
14263             
14264             this.lastSelectionText = dv;
14265             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14266             this.value = vv;
14267             return;
14268         }
14269         // no hidden field.. - we store the value in 'value', but still display
14270         // display field!!!!
14271         this.lastSelectionText = dv;
14272         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14273         this.value = vv;
14274         
14275         
14276         
14277     },
14278     // private
14279     reset : function(){
14280         // overridden so that last data is reset..
14281         
14282         if(this.multiple){
14283             this.clearItem();
14284             return;
14285         }
14286         
14287         this.setValue(this.originalValue);
14288         //this.clearInvalid();
14289         this.lastData = false;
14290         if (this.view) {
14291             this.view.clearSelections();
14292         }
14293         
14294         this.validate();
14295     },
14296     // private
14297     findRecord : function(prop, value){
14298         var record;
14299         if(this.store.getCount() > 0){
14300             this.store.each(function(r){
14301                 if(r.data[prop] == value){
14302                     record = r;
14303                     return false;
14304                 }
14305                 return true;
14306             });
14307         }
14308         return record;
14309     },
14310     
14311     getName: function()
14312     {
14313         // returns hidden if it's set..
14314         if (!this.rendered) {return ''};
14315         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14316         
14317     },
14318     // private
14319     onViewMove : function(e, t){
14320         this.inKeyMode = false;
14321     },
14322
14323     // private
14324     onViewOver : function(e, t){
14325         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14326             return;
14327         }
14328         var item = this.view.findItemFromChild(t);
14329         
14330         if(item){
14331             var index = this.view.indexOf(item);
14332             this.select(index, false);
14333         }
14334     },
14335
14336     // private
14337     onViewClick : function(view, doFocus, el, e)
14338     {
14339         var index = this.view.getSelectedIndexes()[0];
14340         
14341         var r = this.store.getAt(index);
14342         
14343         if(this.tickable){
14344             
14345             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14346                 return;
14347             }
14348             
14349             var rm = false;
14350             var _this = this;
14351             
14352             Roo.each(this.tickItems, function(v,k){
14353                 
14354                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14355                     Roo.log(v);
14356                     _this.tickItems.splice(k, 1);
14357                     
14358                     if(typeof(e) == 'undefined' && view == false){
14359                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14360                     }
14361                     
14362                     rm = true;
14363                     return;
14364                 }
14365             });
14366             
14367             if(rm){
14368                 return;
14369             }
14370             
14371             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14372                 this.tickItems.push(r.data);
14373             }
14374             
14375             if(typeof(e) == 'undefined' && view == false){
14376                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14377             }
14378                     
14379             return;
14380         }
14381         
14382         if(r){
14383             this.onSelect(r, index);
14384         }
14385         if(doFocus !== false && !this.blockFocus){
14386             this.inputEl().focus();
14387         }
14388     },
14389
14390     // private
14391     restrictHeight : function(){
14392         //this.innerList.dom.style.height = '';
14393         //var inner = this.innerList.dom;
14394         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14395         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14396         //this.list.beginUpdate();
14397         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14398         this.list.alignTo(this.inputEl(), this.listAlign);
14399         this.list.alignTo(this.inputEl(), this.listAlign);
14400         //this.list.endUpdate();
14401     },
14402
14403     // private
14404     onEmptyResults : function(){
14405         
14406         if(this.tickable && this.editable){
14407             this.hasFocus = false;
14408             this.restrictHeight();
14409             return;
14410         }
14411         
14412         this.collapse();
14413     },
14414
14415     /**
14416      * Returns true if the dropdown list is expanded, else false.
14417      */
14418     isExpanded : function(){
14419         return this.list.isVisible();
14420     },
14421
14422     /**
14423      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14424      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14425      * @param {String} value The data value of the item to select
14426      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14427      * selected item if it is not currently in view (defaults to true)
14428      * @return {Boolean} True if the value matched an item in the list, else false
14429      */
14430     selectByValue : function(v, scrollIntoView){
14431         if(v !== undefined && v !== null){
14432             var r = this.findRecord(this.valueField || this.displayField, v);
14433             if(r){
14434                 this.select(this.store.indexOf(r), scrollIntoView);
14435                 return true;
14436             }
14437         }
14438         return false;
14439     },
14440
14441     /**
14442      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14443      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14444      * @param {Number} index The zero-based index of the list item to select
14445      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14446      * selected item if it is not currently in view (defaults to true)
14447      */
14448     select : function(index, scrollIntoView){
14449         this.selectedIndex = index;
14450         this.view.select(index);
14451         if(scrollIntoView !== false){
14452             var el = this.view.getNode(index);
14453             /*
14454              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14455              */
14456             if(el){
14457                 this.list.scrollChildIntoView(el, false);
14458             }
14459         }
14460     },
14461
14462     // private
14463     selectNext : function(){
14464         var ct = this.store.getCount();
14465         if(ct > 0){
14466             if(this.selectedIndex == -1){
14467                 this.select(0);
14468             }else if(this.selectedIndex < ct-1){
14469                 this.select(this.selectedIndex+1);
14470             }
14471         }
14472     },
14473
14474     // private
14475     selectPrev : function(){
14476         var ct = this.store.getCount();
14477         if(ct > 0){
14478             if(this.selectedIndex == -1){
14479                 this.select(0);
14480             }else if(this.selectedIndex != 0){
14481                 this.select(this.selectedIndex-1);
14482             }
14483         }
14484     },
14485
14486     // private
14487     onKeyUp : function(e){
14488         if(this.editable !== false && !e.isSpecialKey()){
14489             this.lastKey = e.getKey();
14490             this.dqTask.delay(this.queryDelay);
14491         }
14492     },
14493
14494     // private
14495     validateBlur : function(){
14496         return !this.list || !this.list.isVisible();   
14497     },
14498
14499     // private
14500     initQuery : function(){
14501         
14502         var v = this.getRawValue();
14503         
14504         if(this.tickable && this.editable){
14505             v = this.tickableInputEl().getValue();
14506         }
14507         
14508         this.doQuery(v);
14509     },
14510
14511     // private
14512     doForce : function(){
14513         if(this.inputEl().dom.value.length > 0){
14514             this.inputEl().dom.value =
14515                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14516              
14517         }
14518     },
14519
14520     /**
14521      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14522      * query allowing the query action to be canceled if needed.
14523      * @param {String} query The SQL query to execute
14524      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14525      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14526      * saved in the current store (defaults to false)
14527      */
14528     doQuery : function(q, forceAll){
14529         
14530         if(q === undefined || q === null){
14531             q = '';
14532         }
14533         var qe = {
14534             query: q,
14535             forceAll: forceAll,
14536             combo: this,
14537             cancel:false
14538         };
14539         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14540             return false;
14541         }
14542         q = qe.query;
14543         
14544         forceAll = qe.forceAll;
14545         if(forceAll === true || (q.length >= this.minChars)){
14546             
14547             this.hasQuery = true;
14548             
14549             if(this.lastQuery != q || this.alwaysQuery){
14550                 this.lastQuery = q;
14551                 if(this.mode == 'local'){
14552                     this.selectedIndex = -1;
14553                     if(forceAll){
14554                         this.store.clearFilter();
14555                     }else{
14556                         
14557                         if(this.specialFilter){
14558                             this.fireEvent('specialfilter', this);
14559                             this.onLoad();
14560                             return;
14561                         }
14562                         
14563                         this.store.filter(this.displayField, q);
14564                     }
14565                     
14566                     this.store.fireEvent("datachanged", this.store);
14567                     
14568                     this.onLoad();
14569                     
14570                     
14571                 }else{
14572                     
14573                     this.store.baseParams[this.queryParam] = q;
14574                     
14575                     var options = {params : this.getParams(q)};
14576                     
14577                     if(this.loadNext){
14578                         options.add = true;
14579                         options.params.start = this.page * this.pageSize;
14580                     }
14581                     
14582                     this.store.load(options);
14583                     
14584                     /*
14585                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14586                      *  we should expand the list on onLoad
14587                      *  so command out it
14588                      */
14589 //                    this.expand();
14590                 }
14591             }else{
14592                 this.selectedIndex = -1;
14593                 this.onLoad();   
14594             }
14595         }
14596         
14597         this.loadNext = false;
14598     },
14599     
14600     // private
14601     getParams : function(q){
14602         var p = {};
14603         //p[this.queryParam] = q;
14604         
14605         if(this.pageSize){
14606             p.start = 0;
14607             p.limit = this.pageSize;
14608         }
14609         return p;
14610     },
14611
14612     /**
14613      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14614      */
14615     collapse : function(){
14616         if(!this.isExpanded()){
14617             return;
14618         }
14619         
14620         this.list.hide();
14621         
14622         this.hasFocus = false;
14623         
14624         if(this.tickable){
14625             this.okBtn.hide();
14626             this.cancelBtn.hide();
14627             this.trigger.show();
14628             
14629             if(this.editable){
14630                 this.tickableInputEl().dom.value = '';
14631                 this.tickableInputEl().blur();
14632             }
14633             
14634         }
14635         
14636         Roo.get(document).un('mousedown', this.collapseIf, this);
14637         Roo.get(document).un('mousewheel', this.collapseIf, this);
14638         if (!this.editable) {
14639             Roo.get(document).un('keydown', this.listKeyPress, this);
14640         }
14641         this.fireEvent('collapse', this);
14642         
14643         this.validate();
14644     },
14645
14646     // private
14647     collapseIf : function(e){
14648         var in_combo  = e.within(this.el);
14649         var in_list =  e.within(this.list);
14650         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14651         
14652         if (in_combo || in_list || is_list) {
14653             //e.stopPropagation();
14654             return;
14655         }
14656         
14657         if(this.tickable){
14658             this.onTickableFooterButtonClick(e, false, false);
14659         }
14660
14661         this.collapse();
14662         
14663     },
14664
14665     /**
14666      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14667      */
14668     expand : function(){
14669        
14670         if(this.isExpanded() || !this.hasFocus){
14671             return;
14672         }
14673         
14674         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14675         this.list.setWidth(lw);
14676         
14677         Roo.log('expand');
14678         
14679         this.list.show();
14680         
14681         this.restrictHeight();
14682         
14683         if(this.tickable){
14684             
14685             this.tickItems = Roo.apply([], this.item);
14686             
14687             this.okBtn.show();
14688             this.cancelBtn.show();
14689             this.trigger.hide();
14690             
14691             if(this.editable){
14692                 this.tickableInputEl().focus();
14693             }
14694             
14695         }
14696         
14697         Roo.get(document).on('mousedown', this.collapseIf, this);
14698         Roo.get(document).on('mousewheel', this.collapseIf, this);
14699         if (!this.editable) {
14700             Roo.get(document).on('keydown', this.listKeyPress, this);
14701         }
14702         
14703         this.fireEvent('expand', this);
14704     },
14705
14706     // private
14707     // Implements the default empty TriggerField.onTriggerClick function
14708     onTriggerClick : function(e)
14709     {
14710         Roo.log('trigger click');
14711         
14712         if(this.disabled || !this.triggerList){
14713             return;
14714         }
14715         
14716         this.page = 0;
14717         this.loadNext = false;
14718         
14719         if(this.isExpanded()){
14720             this.collapse();
14721             if (!this.blockFocus) {
14722                 this.inputEl().focus();
14723             }
14724             
14725         }else {
14726             this.hasFocus = true;
14727             if(this.triggerAction == 'all') {
14728                 this.doQuery(this.allQuery, true);
14729             } else {
14730                 this.doQuery(this.getRawValue());
14731             }
14732             if (!this.blockFocus) {
14733                 this.inputEl().focus();
14734             }
14735         }
14736     },
14737     
14738     onTickableTriggerClick : function(e)
14739     {
14740         if(this.disabled){
14741             return;
14742         }
14743         
14744         this.page = 0;
14745         this.loadNext = false;
14746         this.hasFocus = true;
14747         
14748         if(this.triggerAction == 'all') {
14749             this.doQuery(this.allQuery, true);
14750         } else {
14751             this.doQuery(this.getRawValue());
14752         }
14753     },
14754     
14755     onSearchFieldClick : function(e)
14756     {
14757         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14758             this.onTickableFooterButtonClick(e, false, false);
14759             return;
14760         }
14761         
14762         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14763             return;
14764         }
14765         
14766         this.page = 0;
14767         this.loadNext = false;
14768         this.hasFocus = true;
14769         
14770         if(this.triggerAction == 'all') {
14771             this.doQuery(this.allQuery, true);
14772         } else {
14773             this.doQuery(this.getRawValue());
14774         }
14775     },
14776     
14777     listKeyPress : function(e)
14778     {
14779         //Roo.log('listkeypress');
14780         // scroll to first matching element based on key pres..
14781         if (e.isSpecialKey()) {
14782             return false;
14783         }
14784         var k = String.fromCharCode(e.getKey()).toUpperCase();
14785         //Roo.log(k);
14786         var match  = false;
14787         var csel = this.view.getSelectedNodes();
14788         var cselitem = false;
14789         if (csel.length) {
14790             var ix = this.view.indexOf(csel[0]);
14791             cselitem  = this.store.getAt(ix);
14792             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14793                 cselitem = false;
14794             }
14795             
14796         }
14797         
14798         this.store.each(function(v) { 
14799             if (cselitem) {
14800                 // start at existing selection.
14801                 if (cselitem.id == v.id) {
14802                     cselitem = false;
14803                 }
14804                 return true;
14805             }
14806                 
14807             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14808                 match = this.store.indexOf(v);
14809                 return false;
14810             }
14811             return true;
14812         }, this);
14813         
14814         if (match === false) {
14815             return true; // no more action?
14816         }
14817         // scroll to?
14818         this.view.select(match);
14819         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14820         sn.scrollIntoView(sn.dom.parentNode, false);
14821     },
14822     
14823     onViewScroll : function(e, t){
14824         
14825         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){
14826             return;
14827         }
14828         
14829         this.hasQuery = true;
14830         
14831         this.loading = this.list.select('.loading', true).first();
14832         
14833         if(this.loading === null){
14834             this.list.createChild({
14835                 tag: 'div',
14836                 cls: 'loading roo-select2-more-results roo-select2-active',
14837                 html: 'Loading more results...'
14838             });
14839             
14840             this.loading = this.list.select('.loading', true).first();
14841             
14842             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14843             
14844             this.loading.hide();
14845         }
14846         
14847         this.loading.show();
14848         
14849         var _combo = this;
14850         
14851         this.page++;
14852         this.loadNext = true;
14853         
14854         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14855         
14856         return;
14857     },
14858     
14859     addItem : function(o)
14860     {   
14861         var dv = ''; // display value
14862         
14863         if (this.displayField) {
14864             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14865         } else {
14866             // this is an error condition!!!
14867             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14868         }
14869         
14870         if(!dv.length){
14871             return;
14872         }
14873         
14874         var choice = this.choices.createChild({
14875             tag: 'li',
14876             cls: 'roo-select2-search-choice',
14877             cn: [
14878                 {
14879                     tag: 'div',
14880                     html: dv
14881                 },
14882                 {
14883                     tag: 'a',
14884                     href: '#',
14885                     cls: 'roo-select2-search-choice-close fa fa-times',
14886                     tabindex: '-1'
14887                 }
14888             ]
14889             
14890         }, this.searchField);
14891         
14892         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14893         
14894         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14895         
14896         this.item.push(o);
14897         
14898         this.lastData = o;
14899         
14900         this.syncValue();
14901         
14902         this.inputEl().dom.value = '';
14903         
14904         this.validate();
14905     },
14906     
14907     onRemoveItem : function(e, _self, o)
14908     {
14909         e.preventDefault();
14910         
14911         this.lastItem = Roo.apply([], this.item);
14912         
14913         var index = this.item.indexOf(o.data) * 1;
14914         
14915         if( index < 0){
14916             Roo.log('not this item?!');
14917             return;
14918         }
14919         
14920         this.item.splice(index, 1);
14921         o.item.remove();
14922         
14923         this.syncValue();
14924         
14925         this.fireEvent('remove', this, e);
14926         
14927         this.validate();
14928         
14929     },
14930     
14931     syncValue : function()
14932     {
14933         if(!this.item.length){
14934             this.clearValue();
14935             return;
14936         }
14937             
14938         var value = [];
14939         var _this = this;
14940         Roo.each(this.item, function(i){
14941             if(_this.valueField){
14942                 value.push(i[_this.valueField]);
14943                 return;
14944             }
14945
14946             value.push(i);
14947         });
14948
14949         this.value = value.join(',');
14950
14951         if(this.hiddenField){
14952             this.hiddenField.dom.value = this.value;
14953         }
14954         
14955         this.store.fireEvent("datachanged", this.store);
14956         
14957         this.validate();
14958     },
14959     
14960     clearItem : function()
14961     {
14962         if(!this.multiple){
14963             return;
14964         }
14965         
14966         this.item = [];
14967         
14968         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14969            c.remove();
14970         });
14971         
14972         this.syncValue();
14973         
14974         this.validate();
14975         
14976         if(this.tickable && !Roo.isTouch){
14977             this.view.refresh();
14978         }
14979     },
14980     
14981     inputEl: function ()
14982     {
14983         if(Roo.isIOS && this.useNativeIOS){
14984             return this.el.select('select.roo-ios-select', true).first();
14985         }
14986         
14987         if(Roo.isTouch && this.mobileTouchView){
14988             return this.el.select('input.form-control',true).first();
14989         }
14990         
14991         if(this.tickable){
14992             return this.searchField;
14993         }
14994         
14995         return this.el.select('input.form-control',true).first();
14996     },
14997     
14998     onTickableFooterButtonClick : function(e, btn, el)
14999     {
15000         e.preventDefault();
15001         
15002         this.lastItem = Roo.apply([], this.item);
15003         
15004         if(btn && btn.name == 'cancel'){
15005             this.tickItems = Roo.apply([], this.item);
15006             this.collapse();
15007             return;
15008         }
15009         
15010         this.clearItem();
15011         
15012         var _this = this;
15013         
15014         Roo.each(this.tickItems, function(o){
15015             _this.addItem(o);
15016         });
15017         
15018         this.collapse();
15019         
15020     },
15021     
15022     validate : function()
15023     {
15024         if(this.getVisibilityEl().hasClass('hidden')){
15025             return true;
15026         }
15027         
15028         var v = this.getRawValue();
15029         
15030         if(this.multiple){
15031             v = this.getValue();
15032         }
15033         
15034         if(this.disabled || this.allowBlank || v.length){
15035             this.markValid();
15036             return true;
15037         }
15038         
15039         this.markInvalid();
15040         return false;
15041     },
15042     
15043     tickableInputEl : function()
15044     {
15045         if(!this.tickable || !this.editable){
15046             return this.inputEl();
15047         }
15048         
15049         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15050     },
15051     
15052     
15053     getAutoCreateTouchView : function()
15054     {
15055         var id = Roo.id();
15056         
15057         var cfg = {
15058             cls: 'form-group' //input-group
15059         };
15060         
15061         var input =  {
15062             tag: 'input',
15063             id : id,
15064             type : this.inputType,
15065             cls : 'form-control x-combo-noedit',
15066             autocomplete: 'new-password',
15067             placeholder : this.placeholder || '',
15068             readonly : true
15069         };
15070         
15071         if (this.name) {
15072             input.name = this.name;
15073         }
15074         
15075         if (this.size) {
15076             input.cls += ' input-' + this.size;
15077         }
15078         
15079         if (this.disabled) {
15080             input.disabled = true;
15081         }
15082         
15083         var inputblock = {
15084             cls : '',
15085             cn : [
15086                 input
15087             ]
15088         };
15089         
15090         if(this.before){
15091             inputblock.cls += ' input-group';
15092             
15093             inputblock.cn.unshift({
15094                 tag :'span',
15095                 cls : 'input-group-addon input-group-prepend input-group-text',
15096                 html : this.before
15097             });
15098         }
15099         
15100         if(this.removable && !this.multiple){
15101             inputblock.cls += ' roo-removable';
15102             
15103             inputblock.cn.push({
15104                 tag: 'button',
15105                 html : 'x',
15106                 cls : 'roo-combo-removable-btn close'
15107             });
15108         }
15109
15110         if(this.hasFeedback && !this.allowBlank){
15111             
15112             inputblock.cls += ' has-feedback';
15113             
15114             inputblock.cn.push({
15115                 tag: 'span',
15116                 cls: 'glyphicon form-control-feedback'
15117             });
15118             
15119         }
15120         
15121         if (this.after) {
15122             
15123             inputblock.cls += (this.before) ? '' : ' input-group';
15124             
15125             inputblock.cn.push({
15126                 tag :'span',
15127                 cls : 'input-group-addon input-group-append input-group-text',
15128                 html : this.after
15129             });
15130         }
15131
15132         
15133         var ibwrap = inputblock;
15134         
15135         if(this.multiple){
15136             ibwrap = {
15137                 tag: 'ul',
15138                 cls: 'roo-select2-choices',
15139                 cn:[
15140                     {
15141                         tag: 'li',
15142                         cls: 'roo-select2-search-field',
15143                         cn: [
15144
15145                             inputblock
15146                         ]
15147                     }
15148                 ]
15149             };
15150         
15151             
15152         }
15153         
15154         var combobox = {
15155             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15156             cn: [
15157                 {
15158                     tag: 'input',
15159                     type : 'hidden',
15160                     cls: 'form-hidden-field'
15161                 },
15162                 ibwrap
15163             ]
15164         };
15165         
15166         if(!this.multiple && this.showToggleBtn){
15167             
15168             var caret = {
15169                         tag: 'span',
15170                         cls: 'caret'
15171             };
15172             
15173             if (this.caret != false) {
15174                 caret = {
15175                      tag: 'i',
15176                      cls: 'fa fa-' + this.caret
15177                 };
15178                 
15179             }
15180             
15181             combobox.cn.push({
15182                 tag :'span',
15183                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15184                 cn : [
15185                     caret,
15186                     {
15187                         tag: 'span',
15188                         cls: 'combobox-clear',
15189                         cn  : [
15190                             {
15191                                 tag : 'i',
15192                                 cls: 'icon-remove'
15193                             }
15194                         ]
15195                     }
15196                 ]
15197
15198             })
15199         }
15200         
15201         if(this.multiple){
15202             combobox.cls += ' roo-select2-container-multi';
15203         }
15204         
15205         var align = this.labelAlign || this.parentLabelAlign();
15206         
15207         if (align ==='left' && this.fieldLabel.length) {
15208
15209             cfg.cn = [
15210                 {
15211                    tag : 'i',
15212                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15213                    tooltip : 'This field is required'
15214                 },
15215                 {
15216                     tag: 'label',
15217                     cls : 'control-label col-form-label',
15218                     html : this.fieldLabel
15219
15220                 },
15221                 {
15222                     cls : '', 
15223                     cn: [
15224                         combobox
15225                     ]
15226                 }
15227             ];
15228             
15229             var labelCfg = cfg.cn[1];
15230             var contentCfg = cfg.cn[2];
15231             
15232
15233             if(this.indicatorpos == 'right'){
15234                 cfg.cn = [
15235                     {
15236                         tag: 'label',
15237                         'for' :  id,
15238                         cls : 'control-label col-form-label',
15239                         cn : [
15240                             {
15241                                 tag : 'span',
15242                                 html : this.fieldLabel
15243                             },
15244                             {
15245                                 tag : 'i',
15246                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15247                                 tooltip : 'This field is required'
15248                             }
15249                         ]
15250                     },
15251                     {
15252                         cls : "",
15253                         cn: [
15254                             combobox
15255                         ]
15256                     }
15257
15258                 ];
15259                 
15260                 labelCfg = cfg.cn[0];
15261                 contentCfg = cfg.cn[1];
15262             }
15263             
15264            
15265             
15266             if(this.labelWidth > 12){
15267                 labelCfg.style = "width: " + this.labelWidth + 'px';
15268             }
15269             
15270             if(this.labelWidth < 13 && this.labelmd == 0){
15271                 this.labelmd = this.labelWidth;
15272             }
15273             
15274             if(this.labellg > 0){
15275                 labelCfg.cls += ' col-lg-' + this.labellg;
15276                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15277             }
15278             
15279             if(this.labelmd > 0){
15280                 labelCfg.cls += ' col-md-' + this.labelmd;
15281                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15282             }
15283             
15284             if(this.labelsm > 0){
15285                 labelCfg.cls += ' col-sm-' + this.labelsm;
15286                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15287             }
15288             
15289             if(this.labelxs > 0){
15290                 labelCfg.cls += ' col-xs-' + this.labelxs;
15291                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15292             }
15293                 
15294                 
15295         } else if ( this.fieldLabel.length) {
15296             cfg.cn = [
15297                 {
15298                    tag : 'i',
15299                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15300                    tooltip : 'This field is required'
15301                 },
15302                 {
15303                     tag: 'label',
15304                     cls : 'control-label',
15305                     html : this.fieldLabel
15306
15307                 },
15308                 {
15309                     cls : '', 
15310                     cn: [
15311                         combobox
15312                     ]
15313                 }
15314             ];
15315             
15316             if(this.indicatorpos == 'right'){
15317                 cfg.cn = [
15318                     {
15319                         tag: 'label',
15320                         cls : 'control-label',
15321                         html : this.fieldLabel,
15322                         cn : [
15323                             {
15324                                tag : 'i',
15325                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15326                                tooltip : 'This field is required'
15327                             }
15328                         ]
15329                     },
15330                     {
15331                         cls : '', 
15332                         cn: [
15333                             combobox
15334                         ]
15335                     }
15336                 ];
15337             }
15338         } else {
15339             cfg.cn = combobox;    
15340         }
15341         
15342         
15343         var settings = this;
15344         
15345         ['xs','sm','md','lg'].map(function(size){
15346             if (settings[size]) {
15347                 cfg.cls += ' col-' + size + '-' + settings[size];
15348             }
15349         });
15350         
15351         return cfg;
15352     },
15353     
15354     initTouchView : function()
15355     {
15356         this.renderTouchView();
15357         
15358         this.touchViewEl.on('scroll', function(){
15359             this.el.dom.scrollTop = 0;
15360         }, this);
15361         
15362         this.originalValue = this.getValue();
15363         
15364         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15365         
15366         this.inputEl().on("click", this.showTouchView, this);
15367         if (this.triggerEl) {
15368             this.triggerEl.on("click", this.showTouchView, this);
15369         }
15370         
15371         
15372         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15373         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15374         
15375         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15376         
15377         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15378         this.store.on('load', this.onTouchViewLoad, this);
15379         this.store.on('loadexception', this.onTouchViewLoadException, this);
15380         
15381         if(this.hiddenName){
15382             
15383             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15384             
15385             this.hiddenField.dom.value =
15386                 this.hiddenValue !== undefined ? this.hiddenValue :
15387                 this.value !== undefined ? this.value : '';
15388         
15389             this.el.dom.removeAttribute('name');
15390             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15391         }
15392         
15393         if(this.multiple){
15394             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15395             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15396         }
15397         
15398         if(this.removable && !this.multiple){
15399             var close = this.closeTriggerEl();
15400             if(close){
15401                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15402                 close.on('click', this.removeBtnClick, this, close);
15403             }
15404         }
15405         /*
15406          * fix the bug in Safari iOS8
15407          */
15408         this.inputEl().on("focus", function(e){
15409             document.activeElement.blur();
15410         }, this);
15411         
15412         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15413         
15414         return;
15415         
15416         
15417     },
15418     
15419     renderTouchView : function()
15420     {
15421         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15422         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15423         
15424         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15425         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15426         
15427         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15428         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15429         this.touchViewBodyEl.setStyle('overflow', 'auto');
15430         
15431         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15432         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15433         
15434         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15435         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15436         
15437     },
15438     
15439     showTouchView : function()
15440     {
15441         if(this.disabled){
15442             return;
15443         }
15444         
15445         this.touchViewHeaderEl.hide();
15446
15447         if(this.modalTitle.length){
15448             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15449             this.touchViewHeaderEl.show();
15450         }
15451
15452         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15453         this.touchViewEl.show();
15454
15455         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15456         
15457         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15458         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15459
15460         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15461
15462         if(this.modalTitle.length){
15463             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15464         }
15465         
15466         this.touchViewBodyEl.setHeight(bodyHeight);
15467
15468         if(this.animate){
15469             var _this = this;
15470             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15471         }else{
15472             this.touchViewEl.addClass('in');
15473         }
15474         
15475         if(this._touchViewMask){
15476             Roo.get(document.body).addClass("x-body-masked");
15477             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15478             this._touchViewMask.setStyle('z-index', 10000);
15479             this._touchViewMask.addClass('show');
15480         }
15481         
15482         this.doTouchViewQuery();
15483         
15484     },
15485     
15486     hideTouchView : function()
15487     {
15488         this.touchViewEl.removeClass('in');
15489
15490         if(this.animate){
15491             var _this = this;
15492             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15493         }else{
15494             this.touchViewEl.setStyle('display', 'none');
15495         }
15496         
15497         if(this._touchViewMask){
15498             this._touchViewMask.removeClass('show');
15499             Roo.get(document.body).removeClass("x-body-masked");
15500         }
15501     },
15502     
15503     setTouchViewValue : function()
15504     {
15505         if(this.multiple){
15506             this.clearItem();
15507         
15508             var _this = this;
15509
15510             Roo.each(this.tickItems, function(o){
15511                 this.addItem(o);
15512             }, this);
15513         }
15514         
15515         this.hideTouchView();
15516     },
15517     
15518     doTouchViewQuery : function()
15519     {
15520         var qe = {
15521             query: '',
15522             forceAll: true,
15523             combo: this,
15524             cancel:false
15525         };
15526         
15527         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15528             return false;
15529         }
15530         
15531         if(!this.alwaysQuery || this.mode == 'local'){
15532             this.onTouchViewLoad();
15533             return;
15534         }
15535         
15536         this.store.load();
15537     },
15538     
15539     onTouchViewBeforeLoad : function(combo,opts)
15540     {
15541         return;
15542     },
15543
15544     // private
15545     onTouchViewLoad : function()
15546     {
15547         if(this.store.getCount() < 1){
15548             this.onTouchViewEmptyResults();
15549             return;
15550         }
15551         
15552         this.clearTouchView();
15553         
15554         var rawValue = this.getRawValue();
15555         
15556         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15557         
15558         this.tickItems = [];
15559         
15560         this.store.data.each(function(d, rowIndex){
15561             var row = this.touchViewListGroup.createChild(template);
15562             
15563             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15564                 row.addClass(d.data.cls);
15565             }
15566             
15567             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15568                 var cfg = {
15569                     data : d.data,
15570                     html : d.data[this.displayField]
15571                 };
15572                 
15573                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15574                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15575                 }
15576             }
15577             row.removeClass('selected');
15578             if(!this.multiple && this.valueField &&
15579                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15580             {
15581                 // radio buttons..
15582                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15583                 row.addClass('selected');
15584             }
15585             
15586             if(this.multiple && this.valueField &&
15587                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15588             {
15589                 
15590                 // checkboxes...
15591                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15592                 this.tickItems.push(d.data);
15593             }
15594             
15595             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15596             
15597         }, this);
15598         
15599         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15600         
15601         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15602
15603         if(this.modalTitle.length){
15604             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15605         }
15606
15607         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15608         
15609         if(this.mobile_restrict_height && listHeight < bodyHeight){
15610             this.touchViewBodyEl.setHeight(listHeight);
15611         }
15612         
15613         var _this = this;
15614         
15615         if(firstChecked && listHeight > bodyHeight){
15616             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15617         }
15618         
15619     },
15620     
15621     onTouchViewLoadException : function()
15622     {
15623         this.hideTouchView();
15624     },
15625     
15626     onTouchViewEmptyResults : function()
15627     {
15628         this.clearTouchView();
15629         
15630         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15631         
15632         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15633         
15634     },
15635     
15636     clearTouchView : function()
15637     {
15638         this.touchViewListGroup.dom.innerHTML = '';
15639     },
15640     
15641     onTouchViewClick : function(e, el, o)
15642     {
15643         e.preventDefault();
15644         
15645         var row = o.row;
15646         var rowIndex = o.rowIndex;
15647         
15648         var r = this.store.getAt(rowIndex);
15649         
15650         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15651             
15652             if(!this.multiple){
15653                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15654                     c.dom.removeAttribute('checked');
15655                 }, this);
15656
15657                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15658
15659                 this.setFromData(r.data);
15660
15661                 var close = this.closeTriggerEl();
15662
15663                 if(close){
15664                     close.show();
15665                 }
15666
15667                 this.hideTouchView();
15668
15669                 this.fireEvent('select', this, r, rowIndex);
15670
15671                 return;
15672             }
15673
15674             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15675                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15676                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15677                 return;
15678             }
15679
15680             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15681             this.addItem(r.data);
15682             this.tickItems.push(r.data);
15683         }
15684     },
15685     
15686     getAutoCreateNativeIOS : function()
15687     {
15688         var cfg = {
15689             cls: 'form-group' //input-group,
15690         };
15691         
15692         var combobox =  {
15693             tag: 'select',
15694             cls : 'roo-ios-select'
15695         };
15696         
15697         if (this.name) {
15698             combobox.name = this.name;
15699         }
15700         
15701         if (this.disabled) {
15702             combobox.disabled = true;
15703         }
15704         
15705         var settings = this;
15706         
15707         ['xs','sm','md','lg'].map(function(size){
15708             if (settings[size]) {
15709                 cfg.cls += ' col-' + size + '-' + settings[size];
15710             }
15711         });
15712         
15713         cfg.cn = combobox;
15714         
15715         return cfg;
15716         
15717     },
15718     
15719     initIOSView : function()
15720     {
15721         this.store.on('load', this.onIOSViewLoad, this);
15722         
15723         return;
15724     },
15725     
15726     onIOSViewLoad : function()
15727     {
15728         if(this.store.getCount() < 1){
15729             return;
15730         }
15731         
15732         this.clearIOSView();
15733         
15734         if(this.allowBlank) {
15735             
15736             var default_text = '-- SELECT --';
15737             
15738             if(this.placeholder.length){
15739                 default_text = this.placeholder;
15740             }
15741             
15742             if(this.emptyTitle.length){
15743                 default_text += ' - ' + this.emptyTitle + ' -';
15744             }
15745             
15746             var opt = this.inputEl().createChild({
15747                 tag: 'option',
15748                 value : 0,
15749                 html : default_text
15750             });
15751             
15752             var o = {};
15753             o[this.valueField] = 0;
15754             o[this.displayField] = default_text;
15755             
15756             this.ios_options.push({
15757                 data : o,
15758                 el : opt
15759             });
15760             
15761         }
15762         
15763         this.store.data.each(function(d, rowIndex){
15764             
15765             var html = '';
15766             
15767             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15768                 html = d.data[this.displayField];
15769             }
15770             
15771             var value = '';
15772             
15773             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15774                 value = d.data[this.valueField];
15775             }
15776             
15777             var option = {
15778                 tag: 'option',
15779                 value : value,
15780                 html : html
15781             };
15782             
15783             if(this.value == d.data[this.valueField]){
15784                 option['selected'] = true;
15785             }
15786             
15787             var opt = this.inputEl().createChild(option);
15788             
15789             this.ios_options.push({
15790                 data : d.data,
15791                 el : opt
15792             });
15793             
15794         }, this);
15795         
15796         this.inputEl().on('change', function(){
15797            this.fireEvent('select', this);
15798         }, this);
15799         
15800     },
15801     
15802     clearIOSView: function()
15803     {
15804         this.inputEl().dom.innerHTML = '';
15805         
15806         this.ios_options = [];
15807     },
15808     
15809     setIOSValue: function(v)
15810     {
15811         this.value = v;
15812         
15813         if(!this.ios_options){
15814             return;
15815         }
15816         
15817         Roo.each(this.ios_options, function(opts){
15818            
15819            opts.el.dom.removeAttribute('selected');
15820            
15821            if(opts.data[this.valueField] != v){
15822                return;
15823            }
15824            
15825            opts.el.dom.setAttribute('selected', true);
15826            
15827         }, this);
15828     }
15829
15830     /** 
15831     * @cfg {Boolean} grow 
15832     * @hide 
15833     */
15834     /** 
15835     * @cfg {Number} growMin 
15836     * @hide 
15837     */
15838     /** 
15839     * @cfg {Number} growMax 
15840     * @hide 
15841     */
15842     /**
15843      * @hide
15844      * @method autoSize
15845      */
15846 });
15847
15848 Roo.apply(Roo.bootstrap.ComboBox,  {
15849     
15850     header : {
15851         tag: 'div',
15852         cls: 'modal-header',
15853         cn: [
15854             {
15855                 tag: 'h4',
15856                 cls: 'modal-title'
15857             }
15858         ]
15859     },
15860     
15861     body : {
15862         tag: 'div',
15863         cls: 'modal-body',
15864         cn: [
15865             {
15866                 tag: 'ul',
15867                 cls: 'list-group'
15868             }
15869         ]
15870     },
15871     
15872     listItemRadio : {
15873         tag: 'li',
15874         cls: 'list-group-item',
15875         cn: [
15876             {
15877                 tag: 'span',
15878                 cls: 'roo-combobox-list-group-item-value'
15879             },
15880             {
15881                 tag: 'div',
15882                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15883                 cn: [
15884                     {
15885                         tag: 'input',
15886                         type: 'radio'
15887                     },
15888                     {
15889                         tag: 'label'
15890                     }
15891                 ]
15892             }
15893         ]
15894     },
15895     
15896     listItemCheckbox : {
15897         tag: 'li',
15898         cls: 'list-group-item',
15899         cn: [
15900             {
15901                 tag: 'span',
15902                 cls: 'roo-combobox-list-group-item-value'
15903             },
15904             {
15905                 tag: 'div',
15906                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15907                 cn: [
15908                     {
15909                         tag: 'input',
15910                         type: 'checkbox'
15911                     },
15912                     {
15913                         tag: 'label'
15914                     }
15915                 ]
15916             }
15917         ]
15918     },
15919     
15920     emptyResult : {
15921         tag: 'div',
15922         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15923     },
15924     
15925     footer : {
15926         tag: 'div',
15927         cls: 'modal-footer',
15928         cn: [
15929             {
15930                 tag: 'div',
15931                 cls: 'row',
15932                 cn: [
15933                     {
15934                         tag: 'div',
15935                         cls: 'col-xs-6 text-left',
15936                         cn: {
15937                             tag: 'button',
15938                             cls: 'btn btn-danger roo-touch-view-cancel',
15939                             html: 'Cancel'
15940                         }
15941                     },
15942                     {
15943                         tag: 'div',
15944                         cls: 'col-xs-6 text-right',
15945                         cn: {
15946                             tag: 'button',
15947                             cls: 'btn btn-success roo-touch-view-ok',
15948                             html: 'OK'
15949                         }
15950                     }
15951                 ]
15952             }
15953         ]
15954         
15955     }
15956 });
15957
15958 Roo.apply(Roo.bootstrap.ComboBox,  {
15959     
15960     touchViewTemplate : {
15961         tag: 'div',
15962         cls: 'modal fade roo-combobox-touch-view',
15963         cn: [
15964             {
15965                 tag: 'div',
15966                 cls: 'modal-dialog',
15967                 style : 'position:fixed', // we have to fix position....
15968                 cn: [
15969                     {
15970                         tag: 'div',
15971                         cls: 'modal-content',
15972                         cn: [
15973                             Roo.bootstrap.ComboBox.header,
15974                             Roo.bootstrap.ComboBox.body,
15975                             Roo.bootstrap.ComboBox.footer
15976                         ]
15977                     }
15978                 ]
15979             }
15980         ]
15981     }
15982 });/*
15983  * Based on:
15984  * Ext JS Library 1.1.1
15985  * Copyright(c) 2006-2007, Ext JS, LLC.
15986  *
15987  * Originally Released Under LGPL - original licence link has changed is not relivant.
15988  *
15989  * Fork - LGPL
15990  * <script type="text/javascript">
15991  */
15992
15993 /**
15994  * @class Roo.View
15995  * @extends Roo.util.Observable
15996  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15997  * This class also supports single and multi selection modes. <br>
15998  * Create a data model bound view:
15999  <pre><code>
16000  var store = new Roo.data.Store(...);
16001
16002  var view = new Roo.View({
16003     el : "my-element",
16004     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16005  
16006     singleSelect: true,
16007     selectedClass: "ydataview-selected",
16008     store: store
16009  });
16010
16011  // listen for node click?
16012  view.on("click", function(vw, index, node, e){
16013  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16014  });
16015
16016  // load XML data
16017  dataModel.load("foobar.xml");
16018  </code></pre>
16019  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16020  * <br><br>
16021  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16022  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16023  * 
16024  * Note: old style constructor is still suported (container, template, config)
16025  * 
16026  * @constructor
16027  * Create a new View
16028  * @param {Object} config The config object
16029  * 
16030  */
16031 Roo.View = function(config, depreciated_tpl, depreciated_config){
16032     
16033     this.parent = false;
16034     
16035     if (typeof(depreciated_tpl) == 'undefined') {
16036         // new way.. - universal constructor.
16037         Roo.apply(this, config);
16038         this.el  = Roo.get(this.el);
16039     } else {
16040         // old format..
16041         this.el  = Roo.get(config);
16042         this.tpl = depreciated_tpl;
16043         Roo.apply(this, depreciated_config);
16044     }
16045     this.wrapEl  = this.el.wrap().wrap();
16046     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16047     
16048     
16049     if(typeof(this.tpl) == "string"){
16050         this.tpl = new Roo.Template(this.tpl);
16051     } else {
16052         // support xtype ctors..
16053         this.tpl = new Roo.factory(this.tpl, Roo);
16054     }
16055     
16056     
16057     this.tpl.compile();
16058     
16059     /** @private */
16060     this.addEvents({
16061         /**
16062          * @event beforeclick
16063          * Fires before a click is processed. Returns false to cancel the default action.
16064          * @param {Roo.View} this
16065          * @param {Number} index The index of the target node
16066          * @param {HTMLElement} node The target node
16067          * @param {Roo.EventObject} e The raw event object
16068          */
16069             "beforeclick" : true,
16070         /**
16071          * @event click
16072          * Fires when a template node is clicked.
16073          * @param {Roo.View} this
16074          * @param {Number} index The index of the target node
16075          * @param {HTMLElement} node The target node
16076          * @param {Roo.EventObject} e The raw event object
16077          */
16078             "click" : true,
16079         /**
16080          * @event dblclick
16081          * Fires when a template node is double clicked.
16082          * @param {Roo.View} this
16083          * @param {Number} index The index of the target node
16084          * @param {HTMLElement} node The target node
16085          * @param {Roo.EventObject} e The raw event object
16086          */
16087             "dblclick" : true,
16088         /**
16089          * @event contextmenu
16090          * Fires when a template node is right clicked.
16091          * @param {Roo.View} this
16092          * @param {Number} index The index of the target node
16093          * @param {HTMLElement} node The target node
16094          * @param {Roo.EventObject} e The raw event object
16095          */
16096             "contextmenu" : true,
16097         /**
16098          * @event selectionchange
16099          * Fires when the selected nodes change.
16100          * @param {Roo.View} this
16101          * @param {Array} selections Array of the selected nodes
16102          */
16103             "selectionchange" : true,
16104     
16105         /**
16106          * @event beforeselect
16107          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16108          * @param {Roo.View} this
16109          * @param {HTMLElement} node The node to be selected
16110          * @param {Array} selections Array of currently selected nodes
16111          */
16112             "beforeselect" : true,
16113         /**
16114          * @event preparedata
16115          * Fires on every row to render, to allow you to change the data.
16116          * @param {Roo.View} this
16117          * @param {Object} data to be rendered (change this)
16118          */
16119           "preparedata" : true
16120           
16121           
16122         });
16123
16124
16125
16126     this.el.on({
16127         "click": this.onClick,
16128         "dblclick": this.onDblClick,
16129         "contextmenu": this.onContextMenu,
16130         scope:this
16131     });
16132
16133     this.selections = [];
16134     this.nodes = [];
16135     this.cmp = new Roo.CompositeElementLite([]);
16136     if(this.store){
16137         this.store = Roo.factory(this.store, Roo.data);
16138         this.setStore(this.store, true);
16139     }
16140     
16141     if ( this.footer && this.footer.xtype) {
16142            
16143          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16144         
16145         this.footer.dataSource = this.store;
16146         this.footer.container = fctr;
16147         this.footer = Roo.factory(this.footer, Roo);
16148         fctr.insertFirst(this.el);
16149         
16150         // this is a bit insane - as the paging toolbar seems to detach the el..
16151 //        dom.parentNode.parentNode.parentNode
16152          // they get detached?
16153     }
16154     
16155     
16156     Roo.View.superclass.constructor.call(this);
16157     
16158     
16159 };
16160
16161 Roo.extend(Roo.View, Roo.util.Observable, {
16162     
16163      /**
16164      * @cfg {Roo.data.Store} store Data store to load data from.
16165      */
16166     store : false,
16167     
16168     /**
16169      * @cfg {String|Roo.Element} el The container element.
16170      */
16171     el : '',
16172     
16173     /**
16174      * @cfg {String|Roo.Template} tpl The template used by this View 
16175      */
16176     tpl : false,
16177     /**
16178      * @cfg {String} dataName the named area of the template to use as the data area
16179      *                          Works with domtemplates roo-name="name"
16180      */
16181     dataName: false,
16182     /**
16183      * @cfg {String} selectedClass The css class to add to selected nodes
16184      */
16185     selectedClass : "x-view-selected",
16186      /**
16187      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16188      */
16189     emptyText : "",
16190     
16191     /**
16192      * @cfg {String} text to display on mask (default Loading)
16193      */
16194     mask : false,
16195     /**
16196      * @cfg {Boolean} multiSelect Allow multiple selection
16197      */
16198     multiSelect : false,
16199     /**
16200      * @cfg {Boolean} singleSelect Allow single selection
16201      */
16202     singleSelect:  false,
16203     
16204     /**
16205      * @cfg {Boolean} toggleSelect - selecting 
16206      */
16207     toggleSelect : false,
16208     
16209     /**
16210      * @cfg {Boolean} tickable - selecting 
16211      */
16212     tickable : false,
16213     
16214     /**
16215      * Returns the element this view is bound to.
16216      * @return {Roo.Element}
16217      */
16218     getEl : function(){
16219         return this.wrapEl;
16220     },
16221     
16222     
16223
16224     /**
16225      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16226      */
16227     refresh : function(){
16228         //Roo.log('refresh');
16229         var t = this.tpl;
16230         
16231         // if we are using something like 'domtemplate', then
16232         // the what gets used is:
16233         // t.applySubtemplate(NAME, data, wrapping data..)
16234         // the outer template then get' applied with
16235         //     the store 'extra data'
16236         // and the body get's added to the
16237         //      roo-name="data" node?
16238         //      <span class='roo-tpl-{name}'></span> ?????
16239         
16240         
16241         
16242         this.clearSelections();
16243         this.el.update("");
16244         var html = [];
16245         var records = this.store.getRange();
16246         if(records.length < 1) {
16247             
16248             // is this valid??  = should it render a template??
16249             
16250             this.el.update(this.emptyText);
16251             return;
16252         }
16253         var el = this.el;
16254         if (this.dataName) {
16255             this.el.update(t.apply(this.store.meta)); //????
16256             el = this.el.child('.roo-tpl-' + this.dataName);
16257         }
16258         
16259         for(var i = 0, len = records.length; i < len; i++){
16260             var data = this.prepareData(records[i].data, i, records[i]);
16261             this.fireEvent("preparedata", this, data, i, records[i]);
16262             
16263             var d = Roo.apply({}, data);
16264             
16265             if(this.tickable){
16266                 Roo.apply(d, {'roo-id' : Roo.id()});
16267                 
16268                 var _this = this;
16269             
16270                 Roo.each(this.parent.item, function(item){
16271                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16272                         return;
16273                     }
16274                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16275                 });
16276             }
16277             
16278             html[html.length] = Roo.util.Format.trim(
16279                 this.dataName ?
16280                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16281                     t.apply(d)
16282             );
16283         }
16284         
16285         
16286         
16287         el.update(html.join(""));
16288         this.nodes = el.dom.childNodes;
16289         this.updateIndexes(0);
16290     },
16291     
16292
16293     /**
16294      * Function to override to reformat the data that is sent to
16295      * the template for each node.
16296      * DEPRICATED - use the preparedata event handler.
16297      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16298      * a JSON object for an UpdateManager bound view).
16299      */
16300     prepareData : function(data, index, record)
16301     {
16302         this.fireEvent("preparedata", this, data, index, record);
16303         return data;
16304     },
16305
16306     onUpdate : function(ds, record){
16307         // Roo.log('on update');   
16308         this.clearSelections();
16309         var index = this.store.indexOf(record);
16310         var n = this.nodes[index];
16311         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16312         n.parentNode.removeChild(n);
16313         this.updateIndexes(index, index);
16314     },
16315
16316     
16317     
16318 // --------- FIXME     
16319     onAdd : function(ds, records, index)
16320     {
16321         //Roo.log(['on Add', ds, records, index] );        
16322         this.clearSelections();
16323         if(this.nodes.length == 0){
16324             this.refresh();
16325             return;
16326         }
16327         var n = this.nodes[index];
16328         for(var i = 0, len = records.length; i < len; i++){
16329             var d = this.prepareData(records[i].data, i, records[i]);
16330             if(n){
16331                 this.tpl.insertBefore(n, d);
16332             }else{
16333                 
16334                 this.tpl.append(this.el, d);
16335             }
16336         }
16337         this.updateIndexes(index);
16338     },
16339
16340     onRemove : function(ds, record, index){
16341        // Roo.log('onRemove');
16342         this.clearSelections();
16343         var el = this.dataName  ?
16344             this.el.child('.roo-tpl-' + this.dataName) :
16345             this.el; 
16346         
16347         el.dom.removeChild(this.nodes[index]);
16348         this.updateIndexes(index);
16349     },
16350
16351     /**
16352      * Refresh an individual node.
16353      * @param {Number} index
16354      */
16355     refreshNode : function(index){
16356         this.onUpdate(this.store, this.store.getAt(index));
16357     },
16358
16359     updateIndexes : function(startIndex, endIndex){
16360         var ns = this.nodes;
16361         startIndex = startIndex || 0;
16362         endIndex = endIndex || ns.length - 1;
16363         for(var i = startIndex; i <= endIndex; i++){
16364             ns[i].nodeIndex = i;
16365         }
16366     },
16367
16368     /**
16369      * Changes the data store this view uses and refresh the view.
16370      * @param {Store} store
16371      */
16372     setStore : function(store, initial){
16373         if(!initial && this.store){
16374             this.store.un("datachanged", this.refresh);
16375             this.store.un("add", this.onAdd);
16376             this.store.un("remove", this.onRemove);
16377             this.store.un("update", this.onUpdate);
16378             this.store.un("clear", this.refresh);
16379             this.store.un("beforeload", this.onBeforeLoad);
16380             this.store.un("load", this.onLoad);
16381             this.store.un("loadexception", this.onLoad);
16382         }
16383         if(store){
16384           
16385             store.on("datachanged", this.refresh, this);
16386             store.on("add", this.onAdd, this);
16387             store.on("remove", this.onRemove, this);
16388             store.on("update", this.onUpdate, this);
16389             store.on("clear", this.refresh, this);
16390             store.on("beforeload", this.onBeforeLoad, this);
16391             store.on("load", this.onLoad, this);
16392             store.on("loadexception", this.onLoad, this);
16393         }
16394         
16395         if(store){
16396             this.refresh();
16397         }
16398     },
16399     /**
16400      * onbeforeLoad - masks the loading area.
16401      *
16402      */
16403     onBeforeLoad : function(store,opts)
16404     {
16405          //Roo.log('onBeforeLoad');   
16406         if (!opts.add) {
16407             this.el.update("");
16408         }
16409         this.el.mask(this.mask ? this.mask : "Loading" ); 
16410     },
16411     onLoad : function ()
16412     {
16413         this.el.unmask();
16414     },
16415     
16416
16417     /**
16418      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16419      * @param {HTMLElement} node
16420      * @return {HTMLElement} The template node
16421      */
16422     findItemFromChild : function(node){
16423         var el = this.dataName  ?
16424             this.el.child('.roo-tpl-' + this.dataName,true) :
16425             this.el.dom; 
16426         
16427         if(!node || node.parentNode == el){
16428                     return node;
16429             }
16430             var p = node.parentNode;
16431             while(p && p != el){
16432             if(p.parentNode == el){
16433                 return p;
16434             }
16435             p = p.parentNode;
16436         }
16437             return null;
16438     },
16439
16440     /** @ignore */
16441     onClick : function(e){
16442         var item = this.findItemFromChild(e.getTarget());
16443         if(item){
16444             var index = this.indexOf(item);
16445             if(this.onItemClick(item, index, e) !== false){
16446                 this.fireEvent("click", this, index, item, e);
16447             }
16448         }else{
16449             this.clearSelections();
16450         }
16451     },
16452
16453     /** @ignore */
16454     onContextMenu : function(e){
16455         var item = this.findItemFromChild(e.getTarget());
16456         if(item){
16457             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16458         }
16459     },
16460
16461     /** @ignore */
16462     onDblClick : function(e){
16463         var item = this.findItemFromChild(e.getTarget());
16464         if(item){
16465             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16466         }
16467     },
16468
16469     onItemClick : function(item, index, e)
16470     {
16471         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16472             return false;
16473         }
16474         if (this.toggleSelect) {
16475             var m = this.isSelected(item) ? 'unselect' : 'select';
16476             //Roo.log(m);
16477             var _t = this;
16478             _t[m](item, true, false);
16479             return true;
16480         }
16481         if(this.multiSelect || this.singleSelect){
16482             if(this.multiSelect && e.shiftKey && this.lastSelection){
16483                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16484             }else{
16485                 this.select(item, this.multiSelect && e.ctrlKey);
16486                 this.lastSelection = item;
16487             }
16488             
16489             if(!this.tickable){
16490                 e.preventDefault();
16491             }
16492             
16493         }
16494         return true;
16495     },
16496
16497     /**
16498      * Get the number of selected nodes.
16499      * @return {Number}
16500      */
16501     getSelectionCount : function(){
16502         return this.selections.length;
16503     },
16504
16505     /**
16506      * Get the currently selected nodes.
16507      * @return {Array} An array of HTMLElements
16508      */
16509     getSelectedNodes : function(){
16510         return this.selections;
16511     },
16512
16513     /**
16514      * Get the indexes of the selected nodes.
16515      * @return {Array}
16516      */
16517     getSelectedIndexes : function(){
16518         var indexes = [], s = this.selections;
16519         for(var i = 0, len = s.length; i < len; i++){
16520             indexes.push(s[i].nodeIndex);
16521         }
16522         return indexes;
16523     },
16524
16525     /**
16526      * Clear all selections
16527      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16528      */
16529     clearSelections : function(suppressEvent){
16530         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16531             this.cmp.elements = this.selections;
16532             this.cmp.removeClass(this.selectedClass);
16533             this.selections = [];
16534             if(!suppressEvent){
16535                 this.fireEvent("selectionchange", this, this.selections);
16536             }
16537         }
16538     },
16539
16540     /**
16541      * Returns true if the passed node is selected
16542      * @param {HTMLElement/Number} node The node or node index
16543      * @return {Boolean}
16544      */
16545     isSelected : function(node){
16546         var s = this.selections;
16547         if(s.length < 1){
16548             return false;
16549         }
16550         node = this.getNode(node);
16551         return s.indexOf(node) !== -1;
16552     },
16553
16554     /**
16555      * Selects nodes.
16556      * @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
16557      * @param {Boolean} keepExisting (optional) true to keep existing selections
16558      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16559      */
16560     select : function(nodeInfo, keepExisting, suppressEvent){
16561         if(nodeInfo instanceof Array){
16562             if(!keepExisting){
16563                 this.clearSelections(true);
16564             }
16565             for(var i = 0, len = nodeInfo.length; i < len; i++){
16566                 this.select(nodeInfo[i], true, true);
16567             }
16568             return;
16569         } 
16570         var node = this.getNode(nodeInfo);
16571         if(!node || this.isSelected(node)){
16572             return; // already selected.
16573         }
16574         if(!keepExisting){
16575             this.clearSelections(true);
16576         }
16577         
16578         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16579             Roo.fly(node).addClass(this.selectedClass);
16580             this.selections.push(node);
16581             if(!suppressEvent){
16582                 this.fireEvent("selectionchange", this, this.selections);
16583             }
16584         }
16585         
16586         
16587     },
16588       /**
16589      * Unselects nodes.
16590      * @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
16591      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16592      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16593      */
16594     unselect : function(nodeInfo, keepExisting, suppressEvent)
16595     {
16596         if(nodeInfo instanceof Array){
16597             Roo.each(this.selections, function(s) {
16598                 this.unselect(s, nodeInfo);
16599             }, this);
16600             return;
16601         }
16602         var node = this.getNode(nodeInfo);
16603         if(!node || !this.isSelected(node)){
16604             //Roo.log("not selected");
16605             return; // not selected.
16606         }
16607         // fireevent???
16608         var ns = [];
16609         Roo.each(this.selections, function(s) {
16610             if (s == node ) {
16611                 Roo.fly(node).removeClass(this.selectedClass);
16612
16613                 return;
16614             }
16615             ns.push(s);
16616         },this);
16617         
16618         this.selections= ns;
16619         this.fireEvent("selectionchange", this, this.selections);
16620     },
16621
16622     /**
16623      * Gets a template node.
16624      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16625      * @return {HTMLElement} The node or null if it wasn't found
16626      */
16627     getNode : function(nodeInfo){
16628         if(typeof nodeInfo == "string"){
16629             return document.getElementById(nodeInfo);
16630         }else if(typeof nodeInfo == "number"){
16631             return this.nodes[nodeInfo];
16632         }
16633         return nodeInfo;
16634     },
16635
16636     /**
16637      * Gets a range template nodes.
16638      * @param {Number} startIndex
16639      * @param {Number} endIndex
16640      * @return {Array} An array of nodes
16641      */
16642     getNodes : function(start, end){
16643         var ns = this.nodes;
16644         start = start || 0;
16645         end = typeof end == "undefined" ? ns.length - 1 : end;
16646         var nodes = [];
16647         if(start <= end){
16648             for(var i = start; i <= end; i++){
16649                 nodes.push(ns[i]);
16650             }
16651         } else{
16652             for(var i = start; i >= end; i--){
16653                 nodes.push(ns[i]);
16654             }
16655         }
16656         return nodes;
16657     },
16658
16659     /**
16660      * Finds the index of the passed node
16661      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16662      * @return {Number} The index of the node or -1
16663      */
16664     indexOf : function(node){
16665         node = this.getNode(node);
16666         if(typeof node.nodeIndex == "number"){
16667             return node.nodeIndex;
16668         }
16669         var ns = this.nodes;
16670         for(var i = 0, len = ns.length; i < len; i++){
16671             if(ns[i] == node){
16672                 return i;
16673             }
16674         }
16675         return -1;
16676     }
16677 });
16678 /*
16679  * - LGPL
16680  *
16681  * based on jquery fullcalendar
16682  * 
16683  */
16684
16685 Roo.bootstrap = Roo.bootstrap || {};
16686 /**
16687  * @class Roo.bootstrap.Calendar
16688  * @extends Roo.bootstrap.Component
16689  * Bootstrap Calendar class
16690  * @cfg {Boolean} loadMask (true|false) default false
16691  * @cfg {Object} header generate the user specific header of the calendar, default false
16692
16693  * @constructor
16694  * Create a new Container
16695  * @param {Object} config The config object
16696  */
16697
16698
16699
16700 Roo.bootstrap.Calendar = function(config){
16701     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16702      this.addEvents({
16703         /**
16704              * @event select
16705              * Fires when a date is selected
16706              * @param {DatePicker} this
16707              * @param {Date} date The selected date
16708              */
16709         'select': true,
16710         /**
16711              * @event monthchange
16712              * Fires when the displayed month changes 
16713              * @param {DatePicker} this
16714              * @param {Date} date The selected month
16715              */
16716         'monthchange': true,
16717         /**
16718              * @event evententer
16719              * Fires when mouse over an event
16720              * @param {Calendar} this
16721              * @param {event} Event
16722              */
16723         'evententer': true,
16724         /**
16725              * @event eventleave
16726              * Fires when the mouse leaves an
16727              * @param {Calendar} this
16728              * @param {event}
16729              */
16730         'eventleave': true,
16731         /**
16732              * @event eventclick
16733              * Fires when the mouse click an
16734              * @param {Calendar} this
16735              * @param {event}
16736              */
16737         'eventclick': true
16738         
16739     });
16740
16741 };
16742
16743 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16744     
16745      /**
16746      * @cfg {Number} startDay
16747      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16748      */
16749     startDay : 0,
16750     
16751     loadMask : false,
16752     
16753     header : false,
16754       
16755     getAutoCreate : function(){
16756         
16757         
16758         var fc_button = function(name, corner, style, content ) {
16759             return Roo.apply({},{
16760                 tag : 'span',
16761                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16762                          (corner.length ?
16763                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16764                             ''
16765                         ),
16766                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16767                 unselectable: 'on'
16768             });
16769         };
16770         
16771         var header = {};
16772         
16773         if(!this.header){
16774             header = {
16775                 tag : 'table',
16776                 cls : 'fc-header',
16777                 style : 'width:100%',
16778                 cn : [
16779                     {
16780                         tag: 'tr',
16781                         cn : [
16782                             {
16783                                 tag : 'td',
16784                                 cls : 'fc-header-left',
16785                                 cn : [
16786                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16787                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16788                                     { tag: 'span', cls: 'fc-header-space' },
16789                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16790
16791
16792                                 ]
16793                             },
16794
16795                             {
16796                                 tag : 'td',
16797                                 cls : 'fc-header-center',
16798                                 cn : [
16799                                     {
16800                                         tag: 'span',
16801                                         cls: 'fc-header-title',
16802                                         cn : {
16803                                             tag: 'H2',
16804                                             html : 'month / year'
16805                                         }
16806                                     }
16807
16808                                 ]
16809                             },
16810                             {
16811                                 tag : 'td',
16812                                 cls : 'fc-header-right',
16813                                 cn : [
16814                               /*      fc_button('month', 'left', '', 'month' ),
16815                                     fc_button('week', '', '', 'week' ),
16816                                     fc_button('day', 'right', '', 'day' )
16817                                 */    
16818
16819                                 ]
16820                             }
16821
16822                         ]
16823                     }
16824                 ]
16825             };
16826         }
16827         
16828         header = this.header;
16829         
16830        
16831         var cal_heads = function() {
16832             var ret = [];
16833             // fixme - handle this.
16834             
16835             for (var i =0; i < Date.dayNames.length; i++) {
16836                 var d = Date.dayNames[i];
16837                 ret.push({
16838                     tag: 'th',
16839                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16840                     html : d.substring(0,3)
16841                 });
16842                 
16843             }
16844             ret[0].cls += ' fc-first';
16845             ret[6].cls += ' fc-last';
16846             return ret;
16847         };
16848         var cal_cell = function(n) {
16849             return  {
16850                 tag: 'td',
16851                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16852                 cn : [
16853                     {
16854                         cn : [
16855                             {
16856                                 cls: 'fc-day-number',
16857                                 html: 'D'
16858                             },
16859                             {
16860                                 cls: 'fc-day-content',
16861                              
16862                                 cn : [
16863                                      {
16864                                         style: 'position: relative;' // height: 17px;
16865                                     }
16866                                 ]
16867                             }
16868                             
16869                             
16870                         ]
16871                     }
16872                 ]
16873                 
16874             }
16875         };
16876         var cal_rows = function() {
16877             
16878             var ret = [];
16879             for (var r = 0; r < 6; r++) {
16880                 var row= {
16881                     tag : 'tr',
16882                     cls : 'fc-week',
16883                     cn : []
16884                 };
16885                 
16886                 for (var i =0; i < Date.dayNames.length; i++) {
16887                     var d = Date.dayNames[i];
16888                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16889
16890                 }
16891                 row.cn[0].cls+=' fc-first';
16892                 row.cn[0].cn[0].style = 'min-height:90px';
16893                 row.cn[6].cls+=' fc-last';
16894                 ret.push(row);
16895                 
16896             }
16897             ret[0].cls += ' fc-first';
16898             ret[4].cls += ' fc-prev-last';
16899             ret[5].cls += ' fc-last';
16900             return ret;
16901             
16902         };
16903         
16904         var cal_table = {
16905             tag: 'table',
16906             cls: 'fc-border-separate',
16907             style : 'width:100%',
16908             cellspacing  : 0,
16909             cn : [
16910                 { 
16911                     tag: 'thead',
16912                     cn : [
16913                         { 
16914                             tag: 'tr',
16915                             cls : 'fc-first fc-last',
16916                             cn : cal_heads()
16917                         }
16918                     ]
16919                 },
16920                 { 
16921                     tag: 'tbody',
16922                     cn : cal_rows()
16923                 }
16924                   
16925             ]
16926         };
16927          
16928          var cfg = {
16929             cls : 'fc fc-ltr',
16930             cn : [
16931                 header,
16932                 {
16933                     cls : 'fc-content',
16934                     style : "position: relative;",
16935                     cn : [
16936                         {
16937                             cls : 'fc-view fc-view-month fc-grid',
16938                             style : 'position: relative',
16939                             unselectable : 'on',
16940                             cn : [
16941                                 {
16942                                     cls : 'fc-event-container',
16943                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16944                                 },
16945                                 cal_table
16946                             ]
16947                         }
16948                     ]
16949     
16950                 }
16951            ] 
16952             
16953         };
16954         
16955          
16956         
16957         return cfg;
16958     },
16959     
16960     
16961     initEvents : function()
16962     {
16963         if(!this.store){
16964             throw "can not find store for calendar";
16965         }
16966         
16967         var mark = {
16968             tag: "div",
16969             cls:"x-dlg-mask",
16970             style: "text-align:center",
16971             cn: [
16972                 {
16973                     tag: "div",
16974                     style: "background-color:white;width:50%;margin:250 auto",
16975                     cn: [
16976                         {
16977                             tag: "img",
16978                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16979                         },
16980                         {
16981                             tag: "span",
16982                             html: "Loading"
16983                         }
16984                         
16985                     ]
16986                 }
16987             ]
16988         };
16989         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16990         
16991         var size = this.el.select('.fc-content', true).first().getSize();
16992         this.maskEl.setSize(size.width, size.height);
16993         this.maskEl.enableDisplayMode("block");
16994         if(!this.loadMask){
16995             this.maskEl.hide();
16996         }
16997         
16998         this.store = Roo.factory(this.store, Roo.data);
16999         this.store.on('load', this.onLoad, this);
17000         this.store.on('beforeload', this.onBeforeLoad, this);
17001         
17002         this.resize();
17003         
17004         this.cells = this.el.select('.fc-day',true);
17005         //Roo.log(this.cells);
17006         this.textNodes = this.el.query('.fc-day-number');
17007         this.cells.addClassOnOver('fc-state-hover');
17008         
17009         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17010         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17011         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17012         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17013         
17014         this.on('monthchange', this.onMonthChange, this);
17015         
17016         this.update(new Date().clearTime());
17017     },
17018     
17019     resize : function() {
17020         var sz  = this.el.getSize();
17021         
17022         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17023         this.el.select('.fc-day-content div',true).setHeight(34);
17024     },
17025     
17026     
17027     // private
17028     showPrevMonth : function(e){
17029         this.update(this.activeDate.add("mo", -1));
17030     },
17031     showToday : function(e){
17032         this.update(new Date().clearTime());
17033     },
17034     // private
17035     showNextMonth : function(e){
17036         this.update(this.activeDate.add("mo", 1));
17037     },
17038
17039     // private
17040     showPrevYear : function(){
17041         this.update(this.activeDate.add("y", -1));
17042     },
17043
17044     // private
17045     showNextYear : function(){
17046         this.update(this.activeDate.add("y", 1));
17047     },
17048
17049     
17050    // private
17051     update : function(date)
17052     {
17053         var vd = this.activeDate;
17054         this.activeDate = date;
17055 //        if(vd && this.el){
17056 //            var t = date.getTime();
17057 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17058 //                Roo.log('using add remove');
17059 //                
17060 //                this.fireEvent('monthchange', this, date);
17061 //                
17062 //                this.cells.removeClass("fc-state-highlight");
17063 //                this.cells.each(function(c){
17064 //                   if(c.dateValue == t){
17065 //                       c.addClass("fc-state-highlight");
17066 //                       setTimeout(function(){
17067 //                            try{c.dom.firstChild.focus();}catch(e){}
17068 //                       }, 50);
17069 //                       return false;
17070 //                   }
17071 //                   return true;
17072 //                });
17073 //                return;
17074 //            }
17075 //        }
17076         
17077         var days = date.getDaysInMonth();
17078         
17079         var firstOfMonth = date.getFirstDateOfMonth();
17080         var startingPos = firstOfMonth.getDay()-this.startDay;
17081         
17082         if(startingPos < this.startDay){
17083             startingPos += 7;
17084         }
17085         
17086         var pm = date.add(Date.MONTH, -1);
17087         var prevStart = pm.getDaysInMonth()-startingPos;
17088 //        
17089         this.cells = this.el.select('.fc-day',true);
17090         this.textNodes = this.el.query('.fc-day-number');
17091         this.cells.addClassOnOver('fc-state-hover');
17092         
17093         var cells = this.cells.elements;
17094         var textEls = this.textNodes;
17095         
17096         Roo.each(cells, function(cell){
17097             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17098         });
17099         
17100         days += startingPos;
17101
17102         // convert everything to numbers so it's fast
17103         var day = 86400000;
17104         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17105         //Roo.log(d);
17106         //Roo.log(pm);
17107         //Roo.log(prevStart);
17108         
17109         var today = new Date().clearTime().getTime();
17110         var sel = date.clearTime().getTime();
17111         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17112         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17113         var ddMatch = this.disabledDatesRE;
17114         var ddText = this.disabledDatesText;
17115         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17116         var ddaysText = this.disabledDaysText;
17117         var format = this.format;
17118         
17119         var setCellClass = function(cal, cell){
17120             cell.row = 0;
17121             cell.events = [];
17122             cell.more = [];
17123             //Roo.log('set Cell Class');
17124             cell.title = "";
17125             var t = d.getTime();
17126             
17127             //Roo.log(d);
17128             
17129             cell.dateValue = t;
17130             if(t == today){
17131                 cell.className += " fc-today";
17132                 cell.className += " fc-state-highlight";
17133                 cell.title = cal.todayText;
17134             }
17135             if(t == sel){
17136                 // disable highlight in other month..
17137                 //cell.className += " fc-state-highlight";
17138                 
17139             }
17140             // disabling
17141             if(t < min) {
17142                 cell.className = " fc-state-disabled";
17143                 cell.title = cal.minText;
17144                 return;
17145             }
17146             if(t > max) {
17147                 cell.className = " fc-state-disabled";
17148                 cell.title = cal.maxText;
17149                 return;
17150             }
17151             if(ddays){
17152                 if(ddays.indexOf(d.getDay()) != -1){
17153                     cell.title = ddaysText;
17154                     cell.className = " fc-state-disabled";
17155                 }
17156             }
17157             if(ddMatch && format){
17158                 var fvalue = d.dateFormat(format);
17159                 if(ddMatch.test(fvalue)){
17160                     cell.title = ddText.replace("%0", fvalue);
17161                     cell.className = " fc-state-disabled";
17162                 }
17163             }
17164             
17165             if (!cell.initialClassName) {
17166                 cell.initialClassName = cell.dom.className;
17167             }
17168             
17169             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17170         };
17171
17172         var i = 0;
17173         
17174         for(; i < startingPos; i++) {
17175             textEls[i].innerHTML = (++prevStart);
17176             d.setDate(d.getDate()+1);
17177             
17178             cells[i].className = "fc-past fc-other-month";
17179             setCellClass(this, cells[i]);
17180         }
17181         
17182         var intDay = 0;
17183         
17184         for(; i < days; i++){
17185             intDay = i - startingPos + 1;
17186             textEls[i].innerHTML = (intDay);
17187             d.setDate(d.getDate()+1);
17188             
17189             cells[i].className = ''; // "x-date-active";
17190             setCellClass(this, cells[i]);
17191         }
17192         var extraDays = 0;
17193         
17194         for(; i < 42; i++) {
17195             textEls[i].innerHTML = (++extraDays);
17196             d.setDate(d.getDate()+1);
17197             
17198             cells[i].className = "fc-future fc-other-month";
17199             setCellClass(this, cells[i]);
17200         }
17201         
17202         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17203         
17204         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17205         
17206         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17207         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17208         
17209         if(totalRows != 6){
17210             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17211             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17212         }
17213         
17214         this.fireEvent('monthchange', this, date);
17215         
17216         
17217         /*
17218         if(!this.internalRender){
17219             var main = this.el.dom.firstChild;
17220             var w = main.offsetWidth;
17221             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17222             Roo.fly(main).setWidth(w);
17223             this.internalRender = true;
17224             // opera does not respect the auto grow header center column
17225             // then, after it gets a width opera refuses to recalculate
17226             // without a second pass
17227             if(Roo.isOpera && !this.secondPass){
17228                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17229                 this.secondPass = true;
17230                 this.update.defer(10, this, [date]);
17231             }
17232         }
17233         */
17234         
17235     },
17236     
17237     findCell : function(dt) {
17238         dt = dt.clearTime().getTime();
17239         var ret = false;
17240         this.cells.each(function(c){
17241             //Roo.log("check " +c.dateValue + '?=' + dt);
17242             if(c.dateValue == dt){
17243                 ret = c;
17244                 return false;
17245             }
17246             return true;
17247         });
17248         
17249         return ret;
17250     },
17251     
17252     findCells : function(ev) {
17253         var s = ev.start.clone().clearTime().getTime();
17254        // Roo.log(s);
17255         var e= ev.end.clone().clearTime().getTime();
17256        // Roo.log(e);
17257         var ret = [];
17258         this.cells.each(function(c){
17259              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17260             
17261             if(c.dateValue > e){
17262                 return ;
17263             }
17264             if(c.dateValue < s){
17265                 return ;
17266             }
17267             ret.push(c);
17268         });
17269         
17270         return ret;    
17271     },
17272     
17273 //    findBestRow: function(cells)
17274 //    {
17275 //        var ret = 0;
17276 //        
17277 //        for (var i =0 ; i < cells.length;i++) {
17278 //            ret  = Math.max(cells[i].rows || 0,ret);
17279 //        }
17280 //        return ret;
17281 //        
17282 //    },
17283     
17284     
17285     addItem : function(ev)
17286     {
17287         // look for vertical location slot in
17288         var cells = this.findCells(ev);
17289         
17290 //        ev.row = this.findBestRow(cells);
17291         
17292         // work out the location.
17293         
17294         var crow = false;
17295         var rows = [];
17296         for(var i =0; i < cells.length; i++) {
17297             
17298             cells[i].row = cells[0].row;
17299             
17300             if(i == 0){
17301                 cells[i].row = cells[i].row + 1;
17302             }
17303             
17304             if (!crow) {
17305                 crow = {
17306                     start : cells[i],
17307                     end :  cells[i]
17308                 };
17309                 continue;
17310             }
17311             if (crow.start.getY() == cells[i].getY()) {
17312                 // on same row.
17313                 crow.end = cells[i];
17314                 continue;
17315             }
17316             // different row.
17317             rows.push(crow);
17318             crow = {
17319                 start: cells[i],
17320                 end : cells[i]
17321             };
17322             
17323         }
17324         
17325         rows.push(crow);
17326         ev.els = [];
17327         ev.rows = rows;
17328         ev.cells = cells;
17329         
17330         cells[0].events.push(ev);
17331         
17332         this.calevents.push(ev);
17333     },
17334     
17335     clearEvents: function() {
17336         
17337         if(!this.calevents){
17338             return;
17339         }
17340         
17341         Roo.each(this.cells.elements, function(c){
17342             c.row = 0;
17343             c.events = [];
17344             c.more = [];
17345         });
17346         
17347         Roo.each(this.calevents, function(e) {
17348             Roo.each(e.els, function(el) {
17349                 el.un('mouseenter' ,this.onEventEnter, this);
17350                 el.un('mouseleave' ,this.onEventLeave, this);
17351                 el.remove();
17352             },this);
17353         },this);
17354         
17355         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17356             e.remove();
17357         });
17358         
17359     },
17360     
17361     renderEvents: function()
17362     {   
17363         var _this = this;
17364         
17365         this.cells.each(function(c) {
17366             
17367             if(c.row < 5){
17368                 return;
17369             }
17370             
17371             var ev = c.events;
17372             
17373             var r = 4;
17374             if(c.row != c.events.length){
17375                 r = 4 - (4 - (c.row - c.events.length));
17376             }
17377             
17378             c.events = ev.slice(0, r);
17379             c.more = ev.slice(r);
17380             
17381             if(c.more.length && c.more.length == 1){
17382                 c.events.push(c.more.pop());
17383             }
17384             
17385             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17386             
17387         });
17388             
17389         this.cells.each(function(c) {
17390             
17391             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17392             
17393             
17394             for (var e = 0; e < c.events.length; e++){
17395                 var ev = c.events[e];
17396                 var rows = ev.rows;
17397                 
17398                 for(var i = 0; i < rows.length; i++) {
17399                 
17400                     // how many rows should it span..
17401
17402                     var  cfg = {
17403                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17404                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17405
17406                         unselectable : "on",
17407                         cn : [
17408                             {
17409                                 cls: 'fc-event-inner',
17410                                 cn : [
17411     //                                {
17412     //                                  tag:'span',
17413     //                                  cls: 'fc-event-time',
17414     //                                  html : cells.length > 1 ? '' : ev.time
17415     //                                },
17416                                     {
17417                                       tag:'span',
17418                                       cls: 'fc-event-title',
17419                                       html : String.format('{0}', ev.title)
17420                                     }
17421
17422
17423                                 ]
17424                             },
17425                             {
17426                                 cls: 'ui-resizable-handle ui-resizable-e',
17427                                 html : '&nbsp;&nbsp;&nbsp'
17428                             }
17429
17430                         ]
17431                     };
17432
17433                     if (i == 0) {
17434                         cfg.cls += ' fc-event-start';
17435                     }
17436                     if ((i+1) == rows.length) {
17437                         cfg.cls += ' fc-event-end';
17438                     }
17439
17440                     var ctr = _this.el.select('.fc-event-container',true).first();
17441                     var cg = ctr.createChild(cfg);
17442
17443                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17444                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17445
17446                     var r = (c.more.length) ? 1 : 0;
17447                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17448                     cg.setWidth(ebox.right - sbox.x -2);
17449
17450                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17451                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17452                     cg.on('click', _this.onEventClick, _this, ev);
17453
17454                     ev.els.push(cg);
17455                     
17456                 }
17457                 
17458             }
17459             
17460             
17461             if(c.more.length){
17462                 var  cfg = {
17463                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17464                     style : 'position: absolute',
17465                     unselectable : "on",
17466                     cn : [
17467                         {
17468                             cls: 'fc-event-inner',
17469                             cn : [
17470                                 {
17471                                   tag:'span',
17472                                   cls: 'fc-event-title',
17473                                   html : 'More'
17474                                 }
17475
17476
17477                             ]
17478                         },
17479                         {
17480                             cls: 'ui-resizable-handle ui-resizable-e',
17481                             html : '&nbsp;&nbsp;&nbsp'
17482                         }
17483
17484                     ]
17485                 };
17486
17487                 var ctr = _this.el.select('.fc-event-container',true).first();
17488                 var cg = ctr.createChild(cfg);
17489
17490                 var sbox = c.select('.fc-day-content',true).first().getBox();
17491                 var ebox = c.select('.fc-day-content',true).first().getBox();
17492                 //Roo.log(cg);
17493                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17494                 cg.setWidth(ebox.right - sbox.x -2);
17495
17496                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17497                 
17498             }
17499             
17500         });
17501         
17502         
17503         
17504     },
17505     
17506     onEventEnter: function (e, el,event,d) {
17507         this.fireEvent('evententer', this, el, event);
17508     },
17509     
17510     onEventLeave: function (e, el,event,d) {
17511         this.fireEvent('eventleave', this, el, event);
17512     },
17513     
17514     onEventClick: function (e, el,event,d) {
17515         this.fireEvent('eventclick', this, el, event);
17516     },
17517     
17518     onMonthChange: function () {
17519         this.store.load();
17520     },
17521     
17522     onMoreEventClick: function(e, el, more)
17523     {
17524         var _this = this;
17525         
17526         this.calpopover.placement = 'right';
17527         this.calpopover.setTitle('More');
17528         
17529         this.calpopover.setContent('');
17530         
17531         var ctr = this.calpopover.el.select('.popover-content', true).first();
17532         
17533         Roo.each(more, function(m){
17534             var cfg = {
17535                 cls : 'fc-event-hori fc-event-draggable',
17536                 html : m.title
17537             };
17538             var cg = ctr.createChild(cfg);
17539             
17540             cg.on('click', _this.onEventClick, _this, m);
17541         });
17542         
17543         this.calpopover.show(el);
17544         
17545         
17546     },
17547     
17548     onLoad: function () 
17549     {   
17550         this.calevents = [];
17551         var cal = this;
17552         
17553         if(this.store.getCount() > 0){
17554             this.store.data.each(function(d){
17555                cal.addItem({
17556                     id : d.data.id,
17557                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17558                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17559                     time : d.data.start_time,
17560                     title : d.data.title,
17561                     description : d.data.description,
17562                     venue : d.data.venue
17563                 });
17564             });
17565         }
17566         
17567         this.renderEvents();
17568         
17569         if(this.calevents.length && this.loadMask){
17570             this.maskEl.hide();
17571         }
17572     },
17573     
17574     onBeforeLoad: function()
17575     {
17576         this.clearEvents();
17577         if(this.loadMask){
17578             this.maskEl.show();
17579         }
17580     }
17581 });
17582
17583  
17584  /*
17585  * - LGPL
17586  *
17587  * element
17588  * 
17589  */
17590
17591 /**
17592  * @class Roo.bootstrap.Popover
17593  * @extends Roo.bootstrap.Component
17594  * Bootstrap Popover class
17595  * @cfg {String} html contents of the popover   (or false to use children..)
17596  * @cfg {String} title of popover (or false to hide)
17597  * @cfg {String} placement how it is placed
17598  * @cfg {String} trigger click || hover (or false to trigger manually)
17599  * @cfg {String} over what (parent or false to trigger manually.)
17600  * @cfg {Number} delay - delay before showing
17601  
17602  * @constructor
17603  * Create a new Popover
17604  * @param {Object} config The config object
17605  */
17606
17607 Roo.bootstrap.Popover = function(config){
17608     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17609     
17610     this.addEvents({
17611         // raw events
17612          /**
17613          * @event show
17614          * After the popover show
17615          * 
17616          * @param {Roo.bootstrap.Popover} this
17617          */
17618         "show" : true,
17619         /**
17620          * @event hide
17621          * After the popover hide
17622          * 
17623          * @param {Roo.bootstrap.Popover} this
17624          */
17625         "hide" : true
17626     });
17627 };
17628
17629 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17630     
17631     title: 'Fill in a title',
17632     html: false,
17633     
17634     placement : 'right',
17635     trigger : 'hover', // hover
17636     
17637     delay : 0,
17638     
17639     over: 'parent',
17640     
17641     can_build_overlaid : false,
17642     
17643     getChildContainer : function()
17644     {
17645         return this.el.select('.popover-content',true).first();
17646     },
17647     
17648     getAutoCreate : function(){
17649          
17650         var cfg = {
17651            cls : 'popover roo-dynamic',
17652            style: 'display:block',
17653            cn : [
17654                 {
17655                     cls : 'arrow'
17656                 },
17657                 {
17658                     cls : 'popover-inner',
17659                     cn : [
17660                         {
17661                             tag: 'h3',
17662                             cls: 'popover-title popover-header',
17663                             html : this.title
17664                         },
17665                         {
17666                             cls : 'popover-content popover-body',
17667                             html : this.html
17668                         }
17669                     ]
17670                     
17671                 }
17672            ]
17673         };
17674         
17675         return cfg;
17676     },
17677     setTitle: function(str)
17678     {
17679         this.title = str;
17680         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17681     },
17682     setContent: function(str)
17683     {
17684         this.html = str;
17685         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17686     },
17687     // as it get's added to the bottom of the page.
17688     onRender : function(ct, position)
17689     {
17690         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17691         if(!this.el){
17692             var cfg = Roo.apply({},  this.getAutoCreate());
17693             cfg.id = Roo.id();
17694             
17695             if (this.cls) {
17696                 cfg.cls += ' ' + this.cls;
17697             }
17698             if (this.style) {
17699                 cfg.style = this.style;
17700             }
17701             //Roo.log("adding to ");
17702             this.el = Roo.get(document.body).createChild(cfg, position);
17703 //            Roo.log(this.el);
17704         }
17705         this.initEvents();
17706     },
17707     
17708     initEvents : function()
17709     {
17710         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17711         this.el.enableDisplayMode('block');
17712         this.el.hide();
17713         if (this.over === false) {
17714             return; 
17715         }
17716         if (this.triggers === false) {
17717             return;
17718         }
17719         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17720         var triggers = this.trigger ? this.trigger.split(' ') : [];
17721         Roo.each(triggers, function(trigger) {
17722         
17723             if (trigger == 'click') {
17724                 on_el.on('click', this.toggle, this);
17725             } else if (trigger != 'manual') {
17726                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17727                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17728       
17729                 on_el.on(eventIn  ,this.enter, this);
17730                 on_el.on(eventOut, this.leave, this);
17731             }
17732         }, this);
17733         
17734     },
17735     
17736     
17737     // private
17738     timeout : null,
17739     hoverState : null,
17740     
17741     toggle : function () {
17742         this.hoverState == 'in' ? this.leave() : this.enter();
17743     },
17744     
17745     enter : function () {
17746         
17747         clearTimeout(this.timeout);
17748     
17749         this.hoverState = 'in';
17750     
17751         if (!this.delay || !this.delay.show) {
17752             this.show();
17753             return;
17754         }
17755         var _t = this;
17756         this.timeout = setTimeout(function () {
17757             if (_t.hoverState == 'in') {
17758                 _t.show();
17759             }
17760         }, this.delay.show)
17761     },
17762     
17763     leave : function() {
17764         clearTimeout(this.timeout);
17765     
17766         this.hoverState = 'out';
17767     
17768         if (!this.delay || !this.delay.hide) {
17769             this.hide();
17770             return;
17771         }
17772         var _t = this;
17773         this.timeout = setTimeout(function () {
17774             if (_t.hoverState == 'out') {
17775                 _t.hide();
17776             }
17777         }, this.delay.hide)
17778     },
17779     
17780     show : function (on_el)
17781     {
17782         if (!on_el) {
17783             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17784         }
17785         
17786         // set content.
17787         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17788         if (this.html !== false) {
17789             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17790         }
17791         this.el.removeClass([
17792             'fade','top','bottom', 'left', 'right','in',
17793             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17794         ]);
17795         if (!this.title.length) {
17796             this.el.select('.popover-title',true).hide();
17797         }
17798         
17799         var placement = typeof this.placement == 'function' ?
17800             this.placement.call(this, this.el, on_el) :
17801             this.placement;
17802             
17803         var autoToken = /\s?auto?\s?/i;
17804         var autoPlace = autoToken.test(placement);
17805         if (autoPlace) {
17806             placement = placement.replace(autoToken, '') || 'top';
17807         }
17808         
17809         //this.el.detach()
17810         //this.el.setXY([0,0]);
17811         this.el.show();
17812         this.el.dom.style.display='block';
17813         this.el.addClass(placement);
17814         
17815         //this.el.appendTo(on_el);
17816         
17817         var p = this.getPosition();
17818         var box = this.el.getBox();
17819         
17820         if (autoPlace) {
17821             // fixme..
17822         }
17823         var align = Roo.bootstrap.Popover.alignment[placement];
17824         
17825 //        Roo.log(align);
17826         this.el.alignTo(on_el, align[0],align[1]);
17827         //var arrow = this.el.select('.arrow',true).first();
17828         //arrow.set(align[2], 
17829         
17830         this.el.addClass('in');
17831         
17832         
17833         if (this.el.hasClass('fade')) {
17834             // fade it?
17835         }
17836         
17837         this.hoverState = 'in';
17838         
17839         this.fireEvent('show', this);
17840         
17841     },
17842     hide : function()
17843     {
17844         this.el.setXY([0,0]);
17845         this.el.removeClass('in');
17846         this.el.hide();
17847         this.hoverState = null;
17848         
17849         this.fireEvent('hide', this);
17850     }
17851     
17852 });
17853
17854 Roo.bootstrap.Popover.alignment = {
17855     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17856     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17857     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17858     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17859 };
17860
17861  /*
17862  * - LGPL
17863  *
17864  * Progress
17865  * 
17866  */
17867
17868 /**
17869  * @class Roo.bootstrap.Progress
17870  * @extends Roo.bootstrap.Component
17871  * Bootstrap Progress class
17872  * @cfg {Boolean} striped striped of the progress bar
17873  * @cfg {Boolean} active animated of the progress bar
17874  * 
17875  * 
17876  * @constructor
17877  * Create a new Progress
17878  * @param {Object} config The config object
17879  */
17880
17881 Roo.bootstrap.Progress = function(config){
17882     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17883 };
17884
17885 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17886     
17887     striped : false,
17888     active: false,
17889     
17890     getAutoCreate : function(){
17891         var cfg = {
17892             tag: 'div',
17893             cls: 'progress'
17894         };
17895         
17896         
17897         if(this.striped){
17898             cfg.cls += ' progress-striped';
17899         }
17900       
17901         if(this.active){
17902             cfg.cls += ' active';
17903         }
17904         
17905         
17906         return cfg;
17907     }
17908    
17909 });
17910
17911  
17912
17913  /*
17914  * - LGPL
17915  *
17916  * ProgressBar
17917  * 
17918  */
17919
17920 /**
17921  * @class Roo.bootstrap.ProgressBar
17922  * @extends Roo.bootstrap.Component
17923  * Bootstrap ProgressBar class
17924  * @cfg {Number} aria_valuenow aria-value now
17925  * @cfg {Number} aria_valuemin aria-value min
17926  * @cfg {Number} aria_valuemax aria-value max
17927  * @cfg {String} label label for the progress bar
17928  * @cfg {String} panel (success | info | warning | danger )
17929  * @cfg {String} role role of the progress bar
17930  * @cfg {String} sr_only text
17931  * 
17932  * 
17933  * @constructor
17934  * Create a new ProgressBar
17935  * @param {Object} config The config object
17936  */
17937
17938 Roo.bootstrap.ProgressBar = function(config){
17939     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17940 };
17941
17942 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17943     
17944     aria_valuenow : 0,
17945     aria_valuemin : 0,
17946     aria_valuemax : 100,
17947     label : false,
17948     panel : false,
17949     role : false,
17950     sr_only: false,
17951     
17952     getAutoCreate : function()
17953     {
17954         
17955         var cfg = {
17956             tag: 'div',
17957             cls: 'progress-bar',
17958             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17959         };
17960         
17961         if(this.sr_only){
17962             cfg.cn = {
17963                 tag: 'span',
17964                 cls: 'sr-only',
17965                 html: this.sr_only
17966             }
17967         }
17968         
17969         if(this.role){
17970             cfg.role = this.role;
17971         }
17972         
17973         if(this.aria_valuenow){
17974             cfg['aria-valuenow'] = this.aria_valuenow;
17975         }
17976         
17977         if(this.aria_valuemin){
17978             cfg['aria-valuemin'] = this.aria_valuemin;
17979         }
17980         
17981         if(this.aria_valuemax){
17982             cfg['aria-valuemax'] = this.aria_valuemax;
17983         }
17984         
17985         if(this.label && !this.sr_only){
17986             cfg.html = this.label;
17987         }
17988         
17989         if(this.panel){
17990             cfg.cls += ' progress-bar-' + this.panel;
17991         }
17992         
17993         return cfg;
17994     },
17995     
17996     update : function(aria_valuenow)
17997     {
17998         this.aria_valuenow = aria_valuenow;
17999         
18000         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18001     }
18002    
18003 });
18004
18005  
18006
18007  /*
18008  * - LGPL
18009  *
18010  * column
18011  * 
18012  */
18013
18014 /**
18015  * @class Roo.bootstrap.TabGroup
18016  * @extends Roo.bootstrap.Column
18017  * Bootstrap Column class
18018  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18019  * @cfg {Boolean} carousel true to make the group behave like a carousel
18020  * @cfg {Boolean} bullets show bullets for the panels
18021  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18022  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18023  * @cfg {Boolean} showarrow (true|false) show arrow default true
18024  * 
18025  * @constructor
18026  * Create a new TabGroup
18027  * @param {Object} config The config object
18028  */
18029
18030 Roo.bootstrap.TabGroup = function(config){
18031     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18032     if (!this.navId) {
18033         this.navId = Roo.id();
18034     }
18035     this.tabs = [];
18036     Roo.bootstrap.TabGroup.register(this);
18037     
18038 };
18039
18040 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18041     
18042     carousel : false,
18043     transition : false,
18044     bullets : 0,
18045     timer : 0,
18046     autoslide : false,
18047     slideFn : false,
18048     slideOnTouch : false,
18049     showarrow : true,
18050     
18051     getAutoCreate : function()
18052     {
18053         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18054         
18055         cfg.cls += ' tab-content';
18056         
18057         if (this.carousel) {
18058             cfg.cls += ' carousel slide';
18059             
18060             cfg.cn = [{
18061                cls : 'carousel-inner',
18062                cn : []
18063             }];
18064         
18065             if(this.bullets  && !Roo.isTouch){
18066                 
18067                 var bullets = {
18068                     cls : 'carousel-bullets',
18069                     cn : []
18070                 };
18071                
18072                 if(this.bullets_cls){
18073                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18074                 }
18075                 
18076                 bullets.cn.push({
18077                     cls : 'clear'
18078                 });
18079                 
18080                 cfg.cn[0].cn.push(bullets);
18081             }
18082             
18083             if(this.showarrow){
18084                 cfg.cn[0].cn.push({
18085                     tag : 'div',
18086                     class : 'carousel-arrow',
18087                     cn : [
18088                         {
18089                             tag : 'div',
18090                             class : 'carousel-prev',
18091                             cn : [
18092                                 {
18093                                     tag : 'i',
18094                                     class : 'fa fa-chevron-left'
18095                                 }
18096                             ]
18097                         },
18098                         {
18099                             tag : 'div',
18100                             class : 'carousel-next',
18101                             cn : [
18102                                 {
18103                                     tag : 'i',
18104                                     class : 'fa fa-chevron-right'
18105                                 }
18106                             ]
18107                         }
18108                     ]
18109                 });
18110             }
18111             
18112         }
18113         
18114         return cfg;
18115     },
18116     
18117     initEvents:  function()
18118     {
18119 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18120 //            this.el.on("touchstart", this.onTouchStart, this);
18121 //        }
18122         
18123         if(this.autoslide){
18124             var _this = this;
18125             
18126             this.slideFn = window.setInterval(function() {
18127                 _this.showPanelNext();
18128             }, this.timer);
18129         }
18130         
18131         if(this.showarrow){
18132             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18133             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18134         }
18135         
18136         
18137     },
18138     
18139 //    onTouchStart : function(e, el, o)
18140 //    {
18141 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18142 //            return;
18143 //        }
18144 //        
18145 //        this.showPanelNext();
18146 //    },
18147     
18148     
18149     getChildContainer : function()
18150     {
18151         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18152     },
18153     
18154     /**
18155     * register a Navigation item
18156     * @param {Roo.bootstrap.NavItem} the navitem to add
18157     */
18158     register : function(item)
18159     {
18160         this.tabs.push( item);
18161         item.navId = this.navId; // not really needed..
18162         this.addBullet();
18163     
18164     },
18165     
18166     getActivePanel : function()
18167     {
18168         var r = false;
18169         Roo.each(this.tabs, function(t) {
18170             if (t.active) {
18171                 r = t;
18172                 return false;
18173             }
18174             return null;
18175         });
18176         return r;
18177         
18178     },
18179     getPanelByName : function(n)
18180     {
18181         var r = false;
18182         Roo.each(this.tabs, function(t) {
18183             if (t.tabId == n) {
18184                 r = t;
18185                 return false;
18186             }
18187             return null;
18188         });
18189         return r;
18190     },
18191     indexOfPanel : function(p)
18192     {
18193         var r = false;
18194         Roo.each(this.tabs, function(t,i) {
18195             if (t.tabId == p.tabId) {
18196                 r = i;
18197                 return false;
18198             }
18199             return null;
18200         });
18201         return r;
18202     },
18203     /**
18204      * show a specific panel
18205      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18206      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18207      */
18208     showPanel : function (pan)
18209     {
18210         if(this.transition || typeof(pan) == 'undefined'){
18211             Roo.log("waiting for the transitionend");
18212             return;
18213         }
18214         
18215         if (typeof(pan) == 'number') {
18216             pan = this.tabs[pan];
18217         }
18218         
18219         if (typeof(pan) == 'string') {
18220             pan = this.getPanelByName(pan);
18221         }
18222         
18223         var cur = this.getActivePanel();
18224         
18225         if(!pan || !cur){
18226             Roo.log('pan or acitve pan is undefined');
18227             return false;
18228         }
18229         
18230         if (pan.tabId == this.getActivePanel().tabId) {
18231             return true;
18232         }
18233         
18234         if (false === cur.fireEvent('beforedeactivate')) {
18235             return false;
18236         }
18237         
18238         if(this.bullets > 0 && !Roo.isTouch){
18239             this.setActiveBullet(this.indexOfPanel(pan));
18240         }
18241         
18242         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18243             
18244             this.transition = true;
18245             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18246             var lr = dir == 'next' ? 'left' : 'right';
18247             pan.el.addClass(dir); // or prev
18248             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18249             cur.el.addClass(lr); // or right
18250             pan.el.addClass(lr);
18251             
18252             var _this = this;
18253             cur.el.on('transitionend', function() {
18254                 Roo.log("trans end?");
18255                 
18256                 pan.el.removeClass([lr,dir]);
18257                 pan.setActive(true);
18258                 
18259                 cur.el.removeClass([lr]);
18260                 cur.setActive(false);
18261                 
18262                 _this.transition = false;
18263                 
18264             }, this, { single:  true } );
18265             
18266             return true;
18267         }
18268         
18269         cur.setActive(false);
18270         pan.setActive(true);
18271         
18272         return true;
18273         
18274     },
18275     showPanelNext : function()
18276     {
18277         var i = this.indexOfPanel(this.getActivePanel());
18278         
18279         if (i >= this.tabs.length - 1 && !this.autoslide) {
18280             return;
18281         }
18282         
18283         if (i >= this.tabs.length - 1 && this.autoslide) {
18284             i = -1;
18285         }
18286         
18287         this.showPanel(this.tabs[i+1]);
18288     },
18289     
18290     showPanelPrev : function()
18291     {
18292         var i = this.indexOfPanel(this.getActivePanel());
18293         
18294         if (i  < 1 && !this.autoslide) {
18295             return;
18296         }
18297         
18298         if (i < 1 && this.autoslide) {
18299             i = this.tabs.length;
18300         }
18301         
18302         this.showPanel(this.tabs[i-1]);
18303     },
18304     
18305     
18306     addBullet: function()
18307     {
18308         if(!this.bullets || Roo.isTouch){
18309             return;
18310         }
18311         var ctr = this.el.select('.carousel-bullets',true).first();
18312         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18313         var bullet = ctr.createChild({
18314             cls : 'bullet bullet-' + i
18315         },ctr.dom.lastChild);
18316         
18317         
18318         var _this = this;
18319         
18320         bullet.on('click', (function(e, el, o, ii, t){
18321
18322             e.preventDefault();
18323
18324             this.showPanel(ii);
18325
18326             if(this.autoslide && this.slideFn){
18327                 clearInterval(this.slideFn);
18328                 this.slideFn = window.setInterval(function() {
18329                     _this.showPanelNext();
18330                 }, this.timer);
18331             }
18332
18333         }).createDelegate(this, [i, bullet], true));
18334                 
18335         
18336     },
18337      
18338     setActiveBullet : function(i)
18339     {
18340         if(Roo.isTouch){
18341             return;
18342         }
18343         
18344         Roo.each(this.el.select('.bullet', true).elements, function(el){
18345             el.removeClass('selected');
18346         });
18347
18348         var bullet = this.el.select('.bullet-' + i, true).first();
18349         
18350         if(!bullet){
18351             return;
18352         }
18353         
18354         bullet.addClass('selected');
18355     }
18356     
18357     
18358   
18359 });
18360
18361  
18362
18363  
18364  
18365 Roo.apply(Roo.bootstrap.TabGroup, {
18366     
18367     groups: {},
18368      /**
18369     * register a Navigation Group
18370     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18371     */
18372     register : function(navgrp)
18373     {
18374         this.groups[navgrp.navId] = navgrp;
18375         
18376     },
18377     /**
18378     * fetch a Navigation Group based on the navigation ID
18379     * if one does not exist , it will get created.
18380     * @param {string} the navgroup to add
18381     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18382     */
18383     get: function(navId) {
18384         if (typeof(this.groups[navId]) == 'undefined') {
18385             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18386         }
18387         return this.groups[navId] ;
18388     }
18389     
18390     
18391     
18392 });
18393
18394  /*
18395  * - LGPL
18396  *
18397  * TabPanel
18398  * 
18399  */
18400
18401 /**
18402  * @class Roo.bootstrap.TabPanel
18403  * @extends Roo.bootstrap.Component
18404  * Bootstrap TabPanel class
18405  * @cfg {Boolean} active panel active
18406  * @cfg {String} html panel content
18407  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18408  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18409  * @cfg {String} href click to link..
18410  * 
18411  * 
18412  * @constructor
18413  * Create a new TabPanel
18414  * @param {Object} config The config object
18415  */
18416
18417 Roo.bootstrap.TabPanel = function(config){
18418     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18419     this.addEvents({
18420         /**
18421              * @event changed
18422              * Fires when the active status changes
18423              * @param {Roo.bootstrap.TabPanel} this
18424              * @param {Boolean} state the new state
18425             
18426          */
18427         'changed': true,
18428         /**
18429              * @event beforedeactivate
18430              * Fires before a tab is de-activated - can be used to do validation on a form.
18431              * @param {Roo.bootstrap.TabPanel} this
18432              * @return {Boolean} false if there is an error
18433             
18434          */
18435         'beforedeactivate': true
18436      });
18437     
18438     this.tabId = this.tabId || Roo.id();
18439   
18440 };
18441
18442 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18443     
18444     active: false,
18445     html: false,
18446     tabId: false,
18447     navId : false,
18448     href : '',
18449     
18450     getAutoCreate : function(){
18451         var cfg = {
18452             tag: 'div',
18453             // item is needed for carousel - not sure if it has any effect otherwise
18454             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18455             html: this.html || ''
18456         };
18457         
18458         if(this.active){
18459             cfg.cls += ' active';
18460         }
18461         
18462         if(this.tabId){
18463             cfg.tabId = this.tabId;
18464         }
18465         
18466         
18467         return cfg;
18468     },
18469     
18470     initEvents:  function()
18471     {
18472         var p = this.parent();
18473         
18474         this.navId = this.navId || p.navId;
18475         
18476         if (typeof(this.navId) != 'undefined') {
18477             // not really needed.. but just in case.. parent should be a NavGroup.
18478             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18479             
18480             tg.register(this);
18481             
18482             var i = tg.tabs.length - 1;
18483             
18484             if(this.active && tg.bullets > 0 && i < tg.bullets){
18485                 tg.setActiveBullet(i);
18486             }
18487         }
18488         
18489         this.el.on('click', this.onClick, this);
18490         
18491         if(Roo.isTouch){
18492             this.el.on("touchstart", this.onTouchStart, this);
18493             this.el.on("touchmove", this.onTouchMove, this);
18494             this.el.on("touchend", this.onTouchEnd, this);
18495         }
18496         
18497     },
18498     
18499     onRender : function(ct, position)
18500     {
18501         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18502     },
18503     
18504     setActive : function(state)
18505     {
18506         Roo.log("panel - set active " + this.tabId + "=" + state);
18507         
18508         this.active = state;
18509         if (!state) {
18510             this.el.removeClass('active');
18511             
18512         } else  if (!this.el.hasClass('active')) {
18513             this.el.addClass('active');
18514         }
18515         
18516         this.fireEvent('changed', this, state);
18517     },
18518     
18519     onClick : function(e)
18520     {
18521         e.preventDefault();
18522         
18523         if(!this.href.length){
18524             return;
18525         }
18526         
18527         window.location.href = this.href;
18528     },
18529     
18530     startX : 0,
18531     startY : 0,
18532     endX : 0,
18533     endY : 0,
18534     swiping : false,
18535     
18536     onTouchStart : function(e)
18537     {
18538         this.swiping = false;
18539         
18540         this.startX = e.browserEvent.touches[0].clientX;
18541         this.startY = e.browserEvent.touches[0].clientY;
18542     },
18543     
18544     onTouchMove : function(e)
18545     {
18546         this.swiping = true;
18547         
18548         this.endX = e.browserEvent.touches[0].clientX;
18549         this.endY = e.browserEvent.touches[0].clientY;
18550     },
18551     
18552     onTouchEnd : function(e)
18553     {
18554         if(!this.swiping){
18555             this.onClick(e);
18556             return;
18557         }
18558         
18559         var tabGroup = this.parent();
18560         
18561         if(this.endX > this.startX){ // swiping right
18562             tabGroup.showPanelPrev();
18563             return;
18564         }
18565         
18566         if(this.startX > this.endX){ // swiping left
18567             tabGroup.showPanelNext();
18568             return;
18569         }
18570     }
18571     
18572     
18573 });
18574  
18575
18576  
18577
18578  /*
18579  * - LGPL
18580  *
18581  * DateField
18582  * 
18583  */
18584
18585 /**
18586  * @class Roo.bootstrap.DateField
18587  * @extends Roo.bootstrap.Input
18588  * Bootstrap DateField class
18589  * @cfg {Number} weekStart default 0
18590  * @cfg {String} viewMode default empty, (months|years)
18591  * @cfg {String} minViewMode default empty, (months|years)
18592  * @cfg {Number} startDate default -Infinity
18593  * @cfg {Number} endDate default Infinity
18594  * @cfg {Boolean} todayHighlight default false
18595  * @cfg {Boolean} todayBtn default false
18596  * @cfg {Boolean} calendarWeeks default false
18597  * @cfg {Object} daysOfWeekDisabled default empty
18598  * @cfg {Boolean} singleMode default false (true | false)
18599  * 
18600  * @cfg {Boolean} keyboardNavigation default true
18601  * @cfg {String} language default en
18602  * 
18603  * @constructor
18604  * Create a new DateField
18605  * @param {Object} config The config object
18606  */
18607
18608 Roo.bootstrap.DateField = function(config){
18609     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18610      this.addEvents({
18611             /**
18612              * @event show
18613              * Fires when this field show.
18614              * @param {Roo.bootstrap.DateField} this
18615              * @param {Mixed} date The date value
18616              */
18617             show : true,
18618             /**
18619              * @event show
18620              * Fires when this field hide.
18621              * @param {Roo.bootstrap.DateField} this
18622              * @param {Mixed} date The date value
18623              */
18624             hide : true,
18625             /**
18626              * @event select
18627              * Fires when select a date.
18628              * @param {Roo.bootstrap.DateField} this
18629              * @param {Mixed} date The date value
18630              */
18631             select : true,
18632             /**
18633              * @event beforeselect
18634              * Fires when before select a date.
18635              * @param {Roo.bootstrap.DateField} this
18636              * @param {Mixed} date The date value
18637              */
18638             beforeselect : true
18639         });
18640 };
18641
18642 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18643     
18644     /**
18645      * @cfg {String} format
18646      * The default date format string which can be overriden for localization support.  The format must be
18647      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18648      */
18649     format : "m/d/y",
18650     /**
18651      * @cfg {String} altFormats
18652      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18653      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18654      */
18655     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18656     
18657     weekStart : 0,
18658     
18659     viewMode : '',
18660     
18661     minViewMode : '',
18662     
18663     todayHighlight : false,
18664     
18665     todayBtn: false,
18666     
18667     language: 'en',
18668     
18669     keyboardNavigation: true,
18670     
18671     calendarWeeks: false,
18672     
18673     startDate: -Infinity,
18674     
18675     endDate: Infinity,
18676     
18677     daysOfWeekDisabled: [],
18678     
18679     _events: [],
18680     
18681     singleMode : false,
18682     
18683     UTCDate: function()
18684     {
18685         return new Date(Date.UTC.apply(Date, arguments));
18686     },
18687     
18688     UTCToday: function()
18689     {
18690         var today = new Date();
18691         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18692     },
18693     
18694     getDate: function() {
18695             var d = this.getUTCDate();
18696             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18697     },
18698     
18699     getUTCDate: function() {
18700             return this.date;
18701     },
18702     
18703     setDate: function(d) {
18704             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18705     },
18706     
18707     setUTCDate: function(d) {
18708             this.date = d;
18709             this.setValue(this.formatDate(this.date));
18710     },
18711         
18712     onRender: function(ct, position)
18713     {
18714         
18715         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18716         
18717         this.language = this.language || 'en';
18718         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18719         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18720         
18721         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18722         this.format = this.format || 'm/d/y';
18723         this.isInline = false;
18724         this.isInput = true;
18725         this.component = this.el.select('.add-on', true).first() || false;
18726         this.component = (this.component && this.component.length === 0) ? false : this.component;
18727         this.hasInput = this.component && this.inputEl().length;
18728         
18729         if (typeof(this.minViewMode === 'string')) {
18730             switch (this.minViewMode) {
18731                 case 'months':
18732                     this.minViewMode = 1;
18733                     break;
18734                 case 'years':
18735                     this.minViewMode = 2;
18736                     break;
18737                 default:
18738                     this.minViewMode = 0;
18739                     break;
18740             }
18741         }
18742         
18743         if (typeof(this.viewMode === 'string')) {
18744             switch (this.viewMode) {
18745                 case 'months':
18746                     this.viewMode = 1;
18747                     break;
18748                 case 'years':
18749                     this.viewMode = 2;
18750                     break;
18751                 default:
18752                     this.viewMode = 0;
18753                     break;
18754             }
18755         }
18756                 
18757         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18758         
18759 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18760         
18761         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18762         
18763         this.picker().on('mousedown', this.onMousedown, this);
18764         this.picker().on('click', this.onClick, this);
18765         
18766         this.picker().addClass('datepicker-dropdown');
18767         
18768         this.startViewMode = this.viewMode;
18769         
18770         if(this.singleMode){
18771             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18772                 v.setVisibilityMode(Roo.Element.DISPLAY);
18773                 v.hide();
18774             });
18775             
18776             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18777                 v.setStyle('width', '189px');
18778             });
18779         }
18780         
18781         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18782             if(!this.calendarWeeks){
18783                 v.remove();
18784                 return;
18785             }
18786             
18787             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18788             v.attr('colspan', function(i, val){
18789                 return parseInt(val) + 1;
18790             });
18791         });
18792                         
18793         
18794         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18795         
18796         this.setStartDate(this.startDate);
18797         this.setEndDate(this.endDate);
18798         
18799         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18800         
18801         this.fillDow();
18802         this.fillMonths();
18803         this.update();
18804         this.showMode();
18805         
18806         if(this.isInline) {
18807             this.showPopup();
18808         }
18809     },
18810     
18811     picker : function()
18812     {
18813         return this.pickerEl;
18814 //        return this.el.select('.datepicker', true).first();
18815     },
18816     
18817     fillDow: function()
18818     {
18819         var dowCnt = this.weekStart;
18820         
18821         var dow = {
18822             tag: 'tr',
18823             cn: [
18824                 
18825             ]
18826         };
18827         
18828         if(this.calendarWeeks){
18829             dow.cn.push({
18830                 tag: 'th',
18831                 cls: 'cw',
18832                 html: '&nbsp;'
18833             })
18834         }
18835         
18836         while (dowCnt < this.weekStart + 7) {
18837             dow.cn.push({
18838                 tag: 'th',
18839                 cls: 'dow',
18840                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18841             });
18842         }
18843         
18844         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18845     },
18846     
18847     fillMonths: function()
18848     {    
18849         var i = 0;
18850         var months = this.picker().select('>.datepicker-months td', true).first();
18851         
18852         months.dom.innerHTML = '';
18853         
18854         while (i < 12) {
18855             var month = {
18856                 tag: 'span',
18857                 cls: 'month',
18858                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18859             };
18860             
18861             months.createChild(month);
18862         }
18863         
18864     },
18865     
18866     update: function()
18867     {
18868         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;
18869         
18870         if (this.date < this.startDate) {
18871             this.viewDate = new Date(this.startDate);
18872         } else if (this.date > this.endDate) {
18873             this.viewDate = new Date(this.endDate);
18874         } else {
18875             this.viewDate = new Date(this.date);
18876         }
18877         
18878         this.fill();
18879     },
18880     
18881     fill: function() 
18882     {
18883         var d = new Date(this.viewDate),
18884                 year = d.getUTCFullYear(),
18885                 month = d.getUTCMonth(),
18886                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18887                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18888                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18889                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18890                 currentDate = this.date && this.date.valueOf(),
18891                 today = this.UTCToday();
18892         
18893         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18894         
18895 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18896         
18897 //        this.picker.select('>tfoot th.today').
18898 //                                              .text(dates[this.language].today)
18899 //                                              .toggle(this.todayBtn !== false);
18900     
18901         this.updateNavArrows();
18902         this.fillMonths();
18903                                                 
18904         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18905         
18906         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18907          
18908         prevMonth.setUTCDate(day);
18909         
18910         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18911         
18912         var nextMonth = new Date(prevMonth);
18913         
18914         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18915         
18916         nextMonth = nextMonth.valueOf();
18917         
18918         var fillMonths = false;
18919         
18920         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18921         
18922         while(prevMonth.valueOf() <= nextMonth) {
18923             var clsName = '';
18924             
18925             if (prevMonth.getUTCDay() === this.weekStart) {
18926                 if(fillMonths){
18927                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18928                 }
18929                     
18930                 fillMonths = {
18931                     tag: 'tr',
18932                     cn: []
18933                 };
18934                 
18935                 if(this.calendarWeeks){
18936                     // ISO 8601: First week contains first thursday.
18937                     // ISO also states week starts on Monday, but we can be more abstract here.
18938                     var
18939                     // Start of current week: based on weekstart/current date
18940                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18941                     // Thursday of this week
18942                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18943                     // First Thursday of year, year from thursday
18944                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18945                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18946                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18947                     
18948                     fillMonths.cn.push({
18949                         tag: 'td',
18950                         cls: 'cw',
18951                         html: calWeek
18952                     });
18953                 }
18954             }
18955             
18956             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18957                 clsName += ' old';
18958             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18959                 clsName += ' new';
18960             }
18961             if (this.todayHighlight &&
18962                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18963                 prevMonth.getUTCMonth() == today.getMonth() &&
18964                 prevMonth.getUTCDate() == today.getDate()) {
18965                 clsName += ' today';
18966             }
18967             
18968             if (currentDate && prevMonth.valueOf() === currentDate) {
18969                 clsName += ' active';
18970             }
18971             
18972             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18973                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18974                     clsName += ' disabled';
18975             }
18976             
18977             fillMonths.cn.push({
18978                 tag: 'td',
18979                 cls: 'day ' + clsName,
18980                 html: prevMonth.getDate()
18981             });
18982             
18983             prevMonth.setDate(prevMonth.getDate()+1);
18984         }
18985           
18986         var currentYear = this.date && this.date.getUTCFullYear();
18987         var currentMonth = this.date && this.date.getUTCMonth();
18988         
18989         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18990         
18991         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18992             v.removeClass('active');
18993             
18994             if(currentYear === year && k === currentMonth){
18995                 v.addClass('active');
18996             }
18997             
18998             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18999                 v.addClass('disabled');
19000             }
19001             
19002         });
19003         
19004         
19005         year = parseInt(year/10, 10) * 10;
19006         
19007         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19008         
19009         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19010         
19011         year -= 1;
19012         for (var i = -1; i < 11; i++) {
19013             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19014                 tag: 'span',
19015                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19016                 html: year
19017             });
19018             
19019             year += 1;
19020         }
19021     },
19022     
19023     showMode: function(dir) 
19024     {
19025         if (dir) {
19026             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19027         }
19028         
19029         Roo.each(this.picker().select('>div',true).elements, function(v){
19030             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19031             v.hide();
19032         });
19033         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19034     },
19035     
19036     place: function()
19037     {
19038         if(this.isInline) {
19039             return;
19040         }
19041         
19042         this.picker().removeClass(['bottom', 'top']);
19043         
19044         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19045             /*
19046              * place to the top of element!
19047              *
19048              */
19049             
19050             this.picker().addClass('top');
19051             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19052             
19053             return;
19054         }
19055         
19056         this.picker().addClass('bottom');
19057         
19058         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19059     },
19060     
19061     parseDate : function(value)
19062     {
19063         if(!value || value instanceof Date){
19064             return value;
19065         }
19066         var v = Date.parseDate(value, this.format);
19067         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19068             v = Date.parseDate(value, 'Y-m-d');
19069         }
19070         if(!v && this.altFormats){
19071             if(!this.altFormatsArray){
19072                 this.altFormatsArray = this.altFormats.split("|");
19073             }
19074             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19075                 v = Date.parseDate(value, this.altFormatsArray[i]);
19076             }
19077         }
19078         return v;
19079     },
19080     
19081     formatDate : function(date, fmt)
19082     {   
19083         return (!date || !(date instanceof Date)) ?
19084         date : date.dateFormat(fmt || this.format);
19085     },
19086     
19087     onFocus : function()
19088     {
19089         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19090         this.showPopup();
19091     },
19092     
19093     onBlur : function()
19094     {
19095         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19096         
19097         var d = this.inputEl().getValue();
19098         
19099         this.setValue(d);
19100                 
19101         this.hidePopup();
19102     },
19103     
19104     showPopup : function()
19105     {
19106         this.picker().show();
19107         this.update();
19108         this.place();
19109         
19110         this.fireEvent('showpopup', this, this.date);
19111     },
19112     
19113     hidePopup : function()
19114     {
19115         if(this.isInline) {
19116             return;
19117         }
19118         this.picker().hide();
19119         this.viewMode = this.startViewMode;
19120         this.showMode();
19121         
19122         this.fireEvent('hidepopup', this, this.date);
19123         
19124     },
19125     
19126     onMousedown: function(e)
19127     {
19128         e.stopPropagation();
19129         e.preventDefault();
19130     },
19131     
19132     keyup: function(e)
19133     {
19134         Roo.bootstrap.DateField.superclass.keyup.call(this);
19135         this.update();
19136     },
19137
19138     setValue: function(v)
19139     {
19140         if(this.fireEvent('beforeselect', this, v) !== false){
19141             var d = new Date(this.parseDate(v) ).clearTime();
19142         
19143             if(isNaN(d.getTime())){
19144                 this.date = this.viewDate = '';
19145                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19146                 return;
19147             }
19148
19149             v = this.formatDate(d);
19150
19151             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19152
19153             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19154
19155             this.update();
19156
19157             this.fireEvent('select', this, this.date);
19158         }
19159     },
19160     
19161     getValue: function()
19162     {
19163         return this.formatDate(this.date);
19164     },
19165     
19166     fireKey: function(e)
19167     {
19168         if (!this.picker().isVisible()){
19169             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19170                 this.showPopup();
19171             }
19172             return;
19173         }
19174         
19175         var dateChanged = false,
19176         dir, day, month,
19177         newDate, newViewDate;
19178         
19179         switch(e.keyCode){
19180             case 27: // escape
19181                 this.hidePopup();
19182                 e.preventDefault();
19183                 break;
19184             case 37: // left
19185             case 39: // right
19186                 if (!this.keyboardNavigation) {
19187                     break;
19188                 }
19189                 dir = e.keyCode == 37 ? -1 : 1;
19190                 
19191                 if (e.ctrlKey){
19192                     newDate = this.moveYear(this.date, dir);
19193                     newViewDate = this.moveYear(this.viewDate, dir);
19194                 } else if (e.shiftKey){
19195                     newDate = this.moveMonth(this.date, dir);
19196                     newViewDate = this.moveMonth(this.viewDate, dir);
19197                 } else {
19198                     newDate = new Date(this.date);
19199                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19200                     newViewDate = new Date(this.viewDate);
19201                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19202                 }
19203                 if (this.dateWithinRange(newDate)){
19204                     this.date = newDate;
19205                     this.viewDate = newViewDate;
19206                     this.setValue(this.formatDate(this.date));
19207 //                    this.update();
19208                     e.preventDefault();
19209                     dateChanged = true;
19210                 }
19211                 break;
19212             case 38: // up
19213             case 40: // down
19214                 if (!this.keyboardNavigation) {
19215                     break;
19216                 }
19217                 dir = e.keyCode == 38 ? -1 : 1;
19218                 if (e.ctrlKey){
19219                     newDate = this.moveYear(this.date, dir);
19220                     newViewDate = this.moveYear(this.viewDate, dir);
19221                 } else if (e.shiftKey){
19222                     newDate = this.moveMonth(this.date, dir);
19223                     newViewDate = this.moveMonth(this.viewDate, dir);
19224                 } else {
19225                     newDate = new Date(this.date);
19226                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19227                     newViewDate = new Date(this.viewDate);
19228                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19229                 }
19230                 if (this.dateWithinRange(newDate)){
19231                     this.date = newDate;
19232                     this.viewDate = newViewDate;
19233                     this.setValue(this.formatDate(this.date));
19234 //                    this.update();
19235                     e.preventDefault();
19236                     dateChanged = true;
19237                 }
19238                 break;
19239             case 13: // enter
19240                 this.setValue(this.formatDate(this.date));
19241                 this.hidePopup();
19242                 e.preventDefault();
19243                 break;
19244             case 9: // tab
19245                 this.setValue(this.formatDate(this.date));
19246                 this.hidePopup();
19247                 break;
19248             case 16: // shift
19249             case 17: // ctrl
19250             case 18: // alt
19251                 break;
19252             default :
19253                 this.hidePopup();
19254                 
19255         }
19256     },
19257     
19258     
19259     onClick: function(e) 
19260     {
19261         e.stopPropagation();
19262         e.preventDefault();
19263         
19264         var target = e.getTarget();
19265         
19266         if(target.nodeName.toLowerCase() === 'i'){
19267             target = Roo.get(target).dom.parentNode;
19268         }
19269         
19270         var nodeName = target.nodeName;
19271         var className = target.className;
19272         var html = target.innerHTML;
19273         //Roo.log(nodeName);
19274         
19275         switch(nodeName.toLowerCase()) {
19276             case 'th':
19277                 switch(className) {
19278                     case 'switch':
19279                         this.showMode(1);
19280                         break;
19281                     case 'prev':
19282                     case 'next':
19283                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19284                         switch(this.viewMode){
19285                                 case 0:
19286                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19287                                         break;
19288                                 case 1:
19289                                 case 2:
19290                                         this.viewDate = this.moveYear(this.viewDate, dir);
19291                                         break;
19292                         }
19293                         this.fill();
19294                         break;
19295                     case 'today':
19296                         var date = new Date();
19297                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19298 //                        this.fill()
19299                         this.setValue(this.formatDate(this.date));
19300                         
19301                         this.hidePopup();
19302                         break;
19303                 }
19304                 break;
19305             case 'span':
19306                 if (className.indexOf('disabled') < 0) {
19307                     this.viewDate.setUTCDate(1);
19308                     if (className.indexOf('month') > -1) {
19309                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19310                     } else {
19311                         var year = parseInt(html, 10) || 0;
19312                         this.viewDate.setUTCFullYear(year);
19313                         
19314                     }
19315                     
19316                     if(this.singleMode){
19317                         this.setValue(this.formatDate(this.viewDate));
19318                         this.hidePopup();
19319                         return;
19320                     }
19321                     
19322                     this.showMode(-1);
19323                     this.fill();
19324                 }
19325                 break;
19326                 
19327             case 'td':
19328                 //Roo.log(className);
19329                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19330                     var day = parseInt(html, 10) || 1;
19331                     var year = this.viewDate.getUTCFullYear(),
19332                         month = this.viewDate.getUTCMonth();
19333
19334                     if (className.indexOf('old') > -1) {
19335                         if(month === 0 ){
19336                             month = 11;
19337                             year -= 1;
19338                         }else{
19339                             month -= 1;
19340                         }
19341                     } else if (className.indexOf('new') > -1) {
19342                         if (month == 11) {
19343                             month = 0;
19344                             year += 1;
19345                         } else {
19346                             month += 1;
19347                         }
19348                     }
19349                     //Roo.log([year,month,day]);
19350                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19351                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19352 //                    this.fill();
19353                     //Roo.log(this.formatDate(this.date));
19354                     this.setValue(this.formatDate(this.date));
19355                     this.hidePopup();
19356                 }
19357                 break;
19358         }
19359     },
19360     
19361     setStartDate: function(startDate)
19362     {
19363         this.startDate = startDate || -Infinity;
19364         if (this.startDate !== -Infinity) {
19365             this.startDate = this.parseDate(this.startDate);
19366         }
19367         this.update();
19368         this.updateNavArrows();
19369     },
19370
19371     setEndDate: function(endDate)
19372     {
19373         this.endDate = endDate || Infinity;
19374         if (this.endDate !== Infinity) {
19375             this.endDate = this.parseDate(this.endDate);
19376         }
19377         this.update();
19378         this.updateNavArrows();
19379     },
19380     
19381     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19382     {
19383         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19384         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19385             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19386         }
19387         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19388             return parseInt(d, 10);
19389         });
19390         this.update();
19391         this.updateNavArrows();
19392     },
19393     
19394     updateNavArrows: function() 
19395     {
19396         if(this.singleMode){
19397             return;
19398         }
19399         
19400         var d = new Date(this.viewDate),
19401         year = d.getUTCFullYear(),
19402         month = d.getUTCMonth();
19403         
19404         Roo.each(this.picker().select('.prev', true).elements, function(v){
19405             v.show();
19406             switch (this.viewMode) {
19407                 case 0:
19408
19409                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19410                         v.hide();
19411                     }
19412                     break;
19413                 case 1:
19414                 case 2:
19415                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19416                         v.hide();
19417                     }
19418                     break;
19419             }
19420         });
19421         
19422         Roo.each(this.picker().select('.next', true).elements, function(v){
19423             v.show();
19424             switch (this.viewMode) {
19425                 case 0:
19426
19427                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19428                         v.hide();
19429                     }
19430                     break;
19431                 case 1:
19432                 case 2:
19433                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19434                         v.hide();
19435                     }
19436                     break;
19437             }
19438         })
19439     },
19440     
19441     moveMonth: function(date, dir)
19442     {
19443         if (!dir) {
19444             return date;
19445         }
19446         var new_date = new Date(date.valueOf()),
19447         day = new_date.getUTCDate(),
19448         month = new_date.getUTCMonth(),
19449         mag = Math.abs(dir),
19450         new_month, test;
19451         dir = dir > 0 ? 1 : -1;
19452         if (mag == 1){
19453             test = dir == -1
19454             // If going back one month, make sure month is not current month
19455             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19456             ? function(){
19457                 return new_date.getUTCMonth() == month;
19458             }
19459             // If going forward one month, make sure month is as expected
19460             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19461             : function(){
19462                 return new_date.getUTCMonth() != new_month;
19463             };
19464             new_month = month + dir;
19465             new_date.setUTCMonth(new_month);
19466             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19467             if (new_month < 0 || new_month > 11) {
19468                 new_month = (new_month + 12) % 12;
19469             }
19470         } else {
19471             // For magnitudes >1, move one month at a time...
19472             for (var i=0; i<mag; i++) {
19473                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19474                 new_date = this.moveMonth(new_date, dir);
19475             }
19476             // ...then reset the day, keeping it in the new month
19477             new_month = new_date.getUTCMonth();
19478             new_date.setUTCDate(day);
19479             test = function(){
19480                 return new_month != new_date.getUTCMonth();
19481             };
19482         }
19483         // Common date-resetting loop -- if date is beyond end of month, make it
19484         // end of month
19485         while (test()){
19486             new_date.setUTCDate(--day);
19487             new_date.setUTCMonth(new_month);
19488         }
19489         return new_date;
19490     },
19491
19492     moveYear: function(date, dir)
19493     {
19494         return this.moveMonth(date, dir*12);
19495     },
19496
19497     dateWithinRange: function(date)
19498     {
19499         return date >= this.startDate && date <= this.endDate;
19500     },
19501
19502     
19503     remove: function() 
19504     {
19505         this.picker().remove();
19506     },
19507     
19508     validateValue : function(value)
19509     {
19510         if(this.getVisibilityEl().hasClass('hidden')){
19511             return true;
19512         }
19513         
19514         if(value.length < 1)  {
19515             if(this.allowBlank){
19516                 return true;
19517             }
19518             return false;
19519         }
19520         
19521         if(value.length < this.minLength){
19522             return false;
19523         }
19524         if(value.length > this.maxLength){
19525             return false;
19526         }
19527         if(this.vtype){
19528             var vt = Roo.form.VTypes;
19529             if(!vt[this.vtype](value, this)){
19530                 return false;
19531             }
19532         }
19533         if(typeof this.validator == "function"){
19534             var msg = this.validator(value);
19535             if(msg !== true){
19536                 return false;
19537             }
19538         }
19539         
19540         if(this.regex && !this.regex.test(value)){
19541             return false;
19542         }
19543         
19544         if(typeof(this.parseDate(value)) == 'undefined'){
19545             return false;
19546         }
19547         
19548         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19549             return false;
19550         }      
19551         
19552         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19553             return false;
19554         } 
19555         
19556         
19557         return true;
19558     },
19559     
19560     reset : function()
19561     {
19562         this.date = this.viewDate = '';
19563         
19564         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19565     }
19566    
19567 });
19568
19569 Roo.apply(Roo.bootstrap.DateField,  {
19570     
19571     head : {
19572         tag: 'thead',
19573         cn: [
19574         {
19575             tag: 'tr',
19576             cn: [
19577             {
19578                 tag: 'th',
19579                 cls: 'prev',
19580                 html: '<i class="fa fa-arrow-left"/>'
19581             },
19582             {
19583                 tag: 'th',
19584                 cls: 'switch',
19585                 colspan: '5'
19586             },
19587             {
19588                 tag: 'th',
19589                 cls: 'next',
19590                 html: '<i class="fa fa-arrow-right"/>'
19591             }
19592
19593             ]
19594         }
19595         ]
19596     },
19597     
19598     content : {
19599         tag: 'tbody',
19600         cn: [
19601         {
19602             tag: 'tr',
19603             cn: [
19604             {
19605                 tag: 'td',
19606                 colspan: '7'
19607             }
19608             ]
19609         }
19610         ]
19611     },
19612     
19613     footer : {
19614         tag: 'tfoot',
19615         cn: [
19616         {
19617             tag: 'tr',
19618             cn: [
19619             {
19620                 tag: 'th',
19621                 colspan: '7',
19622                 cls: 'today'
19623             }
19624                     
19625             ]
19626         }
19627         ]
19628     },
19629     
19630     dates:{
19631         en: {
19632             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19633             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19634             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19635             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19636             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19637             today: "Today"
19638         }
19639     },
19640     
19641     modes: [
19642     {
19643         clsName: 'days',
19644         navFnc: 'Month',
19645         navStep: 1
19646     },
19647     {
19648         clsName: 'months',
19649         navFnc: 'FullYear',
19650         navStep: 1
19651     },
19652     {
19653         clsName: 'years',
19654         navFnc: 'FullYear',
19655         navStep: 10
19656     }]
19657 });
19658
19659 Roo.apply(Roo.bootstrap.DateField,  {
19660   
19661     template : {
19662         tag: 'div',
19663         cls: 'datepicker dropdown-menu roo-dynamic',
19664         cn: [
19665         {
19666             tag: 'div',
19667             cls: 'datepicker-days',
19668             cn: [
19669             {
19670                 tag: 'table',
19671                 cls: 'table-condensed',
19672                 cn:[
19673                 Roo.bootstrap.DateField.head,
19674                 {
19675                     tag: 'tbody'
19676                 },
19677                 Roo.bootstrap.DateField.footer
19678                 ]
19679             }
19680             ]
19681         },
19682         {
19683             tag: 'div',
19684             cls: 'datepicker-months',
19685             cn: [
19686             {
19687                 tag: 'table',
19688                 cls: 'table-condensed',
19689                 cn:[
19690                 Roo.bootstrap.DateField.head,
19691                 Roo.bootstrap.DateField.content,
19692                 Roo.bootstrap.DateField.footer
19693                 ]
19694             }
19695             ]
19696         },
19697         {
19698             tag: 'div',
19699             cls: 'datepicker-years',
19700             cn: [
19701             {
19702                 tag: 'table',
19703                 cls: 'table-condensed',
19704                 cn:[
19705                 Roo.bootstrap.DateField.head,
19706                 Roo.bootstrap.DateField.content,
19707                 Roo.bootstrap.DateField.footer
19708                 ]
19709             }
19710             ]
19711         }
19712         ]
19713     }
19714 });
19715
19716  
19717
19718  /*
19719  * - LGPL
19720  *
19721  * TimeField
19722  * 
19723  */
19724
19725 /**
19726  * @class Roo.bootstrap.TimeField
19727  * @extends Roo.bootstrap.Input
19728  * Bootstrap DateField class
19729  * 
19730  * 
19731  * @constructor
19732  * Create a new TimeField
19733  * @param {Object} config The config object
19734  */
19735
19736 Roo.bootstrap.TimeField = function(config){
19737     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19738     this.addEvents({
19739             /**
19740              * @event show
19741              * Fires when this field show.
19742              * @param {Roo.bootstrap.DateField} thisthis
19743              * @param {Mixed} date The date value
19744              */
19745             show : true,
19746             /**
19747              * @event show
19748              * Fires when this field hide.
19749              * @param {Roo.bootstrap.DateField} this
19750              * @param {Mixed} date The date value
19751              */
19752             hide : true,
19753             /**
19754              * @event select
19755              * Fires when select a date.
19756              * @param {Roo.bootstrap.DateField} this
19757              * @param {Mixed} date The date value
19758              */
19759             select : true
19760         });
19761 };
19762
19763 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19764     
19765     /**
19766      * @cfg {String} format
19767      * The default time format string which can be overriden for localization support.  The format must be
19768      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19769      */
19770     format : "H:i",
19771        
19772     onRender: function(ct, position)
19773     {
19774         
19775         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19776                 
19777         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19778         
19779         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19780         
19781         this.pop = this.picker().select('>.datepicker-time',true).first();
19782         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19783         
19784         this.picker().on('mousedown', this.onMousedown, this);
19785         this.picker().on('click', this.onClick, this);
19786         
19787         this.picker().addClass('datepicker-dropdown');
19788     
19789         this.fillTime();
19790         this.update();
19791             
19792         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19793         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19794         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19795         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19796         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19797         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19798
19799     },
19800     
19801     fireKey: function(e){
19802         if (!this.picker().isVisible()){
19803             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19804                 this.show();
19805             }
19806             return;
19807         }
19808
19809         e.preventDefault();
19810         
19811         switch(e.keyCode){
19812             case 27: // escape
19813                 this.hide();
19814                 break;
19815             case 37: // left
19816             case 39: // right
19817                 this.onTogglePeriod();
19818                 break;
19819             case 38: // up
19820                 this.onIncrementMinutes();
19821                 break;
19822             case 40: // down
19823                 this.onDecrementMinutes();
19824                 break;
19825             case 13: // enter
19826             case 9: // tab
19827                 this.setTime();
19828                 break;
19829         }
19830     },
19831     
19832     onClick: function(e) {
19833         e.stopPropagation();
19834         e.preventDefault();
19835     },
19836     
19837     picker : function()
19838     {
19839         return this.el.select('.datepicker', true).first();
19840     },
19841     
19842     fillTime: function()
19843     {    
19844         var time = this.pop.select('tbody', true).first();
19845         
19846         time.dom.innerHTML = '';
19847         
19848         time.createChild({
19849             tag: 'tr',
19850             cn: [
19851                 {
19852                     tag: 'td',
19853                     cn: [
19854                         {
19855                             tag: 'a',
19856                             href: '#',
19857                             cls: 'btn',
19858                             cn: [
19859                                 {
19860                                     tag: 'span',
19861                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19862                                 }
19863                             ]
19864                         } 
19865                     ]
19866                 },
19867                 {
19868                     tag: 'td',
19869                     cls: 'separator'
19870                 },
19871                 {
19872                     tag: 'td',
19873                     cn: [
19874                         {
19875                             tag: 'a',
19876                             href: '#',
19877                             cls: 'btn',
19878                             cn: [
19879                                 {
19880                                     tag: 'span',
19881                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19882                                 }
19883                             ]
19884                         }
19885                     ]
19886                 },
19887                 {
19888                     tag: 'td',
19889                     cls: 'separator'
19890                 }
19891             ]
19892         });
19893         
19894         time.createChild({
19895             tag: 'tr',
19896             cn: [
19897                 {
19898                     tag: 'td',
19899                     cn: [
19900                         {
19901                             tag: 'span',
19902                             cls: 'timepicker-hour',
19903                             html: '00'
19904                         }  
19905                     ]
19906                 },
19907                 {
19908                     tag: 'td',
19909                     cls: 'separator',
19910                     html: ':'
19911                 },
19912                 {
19913                     tag: 'td',
19914                     cn: [
19915                         {
19916                             tag: 'span',
19917                             cls: 'timepicker-minute',
19918                             html: '00'
19919                         }  
19920                     ]
19921                 },
19922                 {
19923                     tag: 'td',
19924                     cls: 'separator'
19925                 },
19926                 {
19927                     tag: 'td',
19928                     cn: [
19929                         {
19930                             tag: 'button',
19931                             type: 'button',
19932                             cls: 'btn btn-primary period',
19933                             html: 'AM'
19934                             
19935                         }
19936                     ]
19937                 }
19938             ]
19939         });
19940         
19941         time.createChild({
19942             tag: 'tr',
19943             cn: [
19944                 {
19945                     tag: 'td',
19946                     cn: [
19947                         {
19948                             tag: 'a',
19949                             href: '#',
19950                             cls: 'btn',
19951                             cn: [
19952                                 {
19953                                     tag: 'span',
19954                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19955                                 }
19956                             ]
19957                         }
19958                     ]
19959                 },
19960                 {
19961                     tag: 'td',
19962                     cls: 'separator'
19963                 },
19964                 {
19965                     tag: 'td',
19966                     cn: [
19967                         {
19968                             tag: 'a',
19969                             href: '#',
19970                             cls: 'btn',
19971                             cn: [
19972                                 {
19973                                     tag: 'span',
19974                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19975                                 }
19976                             ]
19977                         }
19978                     ]
19979                 },
19980                 {
19981                     tag: 'td',
19982                     cls: 'separator'
19983                 }
19984             ]
19985         });
19986         
19987     },
19988     
19989     update: function()
19990     {
19991         
19992         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19993         
19994         this.fill();
19995     },
19996     
19997     fill: function() 
19998     {
19999         var hours = this.time.getHours();
20000         var minutes = this.time.getMinutes();
20001         var period = 'AM';
20002         
20003         if(hours > 11){
20004             period = 'PM';
20005         }
20006         
20007         if(hours == 0){
20008             hours = 12;
20009         }
20010         
20011         
20012         if(hours > 12){
20013             hours = hours - 12;
20014         }
20015         
20016         if(hours < 10){
20017             hours = '0' + hours;
20018         }
20019         
20020         if(minutes < 10){
20021             minutes = '0' + minutes;
20022         }
20023         
20024         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20025         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20026         this.pop.select('button', true).first().dom.innerHTML = period;
20027         
20028     },
20029     
20030     place: function()
20031     {   
20032         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20033         
20034         var cls = ['bottom'];
20035         
20036         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20037             cls.pop();
20038             cls.push('top');
20039         }
20040         
20041         cls.push('right');
20042         
20043         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20044             cls.pop();
20045             cls.push('left');
20046         }
20047         
20048         this.picker().addClass(cls.join('-'));
20049         
20050         var _this = this;
20051         
20052         Roo.each(cls, function(c){
20053             if(c == 'bottom'){
20054                 _this.picker().setTop(_this.inputEl().getHeight());
20055                 return;
20056             }
20057             if(c == 'top'){
20058                 _this.picker().setTop(0 - _this.picker().getHeight());
20059                 return;
20060             }
20061             
20062             if(c == 'left'){
20063                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20064                 return;
20065             }
20066             if(c == 'right'){
20067                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20068                 return;
20069             }
20070         });
20071         
20072     },
20073   
20074     onFocus : function()
20075     {
20076         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20077         this.show();
20078     },
20079     
20080     onBlur : function()
20081     {
20082         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20083         this.hide();
20084     },
20085     
20086     show : function()
20087     {
20088         this.picker().show();
20089         this.pop.show();
20090         this.update();
20091         this.place();
20092         
20093         this.fireEvent('show', this, this.date);
20094     },
20095     
20096     hide : function()
20097     {
20098         this.picker().hide();
20099         this.pop.hide();
20100         
20101         this.fireEvent('hide', this, this.date);
20102     },
20103     
20104     setTime : function()
20105     {
20106         this.hide();
20107         this.setValue(this.time.format(this.format));
20108         
20109         this.fireEvent('select', this, this.date);
20110         
20111         
20112     },
20113     
20114     onMousedown: function(e){
20115         e.stopPropagation();
20116         e.preventDefault();
20117     },
20118     
20119     onIncrementHours: function()
20120     {
20121         Roo.log('onIncrementHours');
20122         this.time = this.time.add(Date.HOUR, 1);
20123         this.update();
20124         
20125     },
20126     
20127     onDecrementHours: function()
20128     {
20129         Roo.log('onDecrementHours');
20130         this.time = this.time.add(Date.HOUR, -1);
20131         this.update();
20132     },
20133     
20134     onIncrementMinutes: function()
20135     {
20136         Roo.log('onIncrementMinutes');
20137         this.time = this.time.add(Date.MINUTE, 1);
20138         this.update();
20139     },
20140     
20141     onDecrementMinutes: function()
20142     {
20143         Roo.log('onDecrementMinutes');
20144         this.time = this.time.add(Date.MINUTE, -1);
20145         this.update();
20146     },
20147     
20148     onTogglePeriod: function()
20149     {
20150         Roo.log('onTogglePeriod');
20151         this.time = this.time.add(Date.HOUR, 12);
20152         this.update();
20153     }
20154     
20155    
20156 });
20157
20158 Roo.apply(Roo.bootstrap.TimeField,  {
20159     
20160     content : {
20161         tag: 'tbody',
20162         cn: [
20163             {
20164                 tag: 'tr',
20165                 cn: [
20166                 {
20167                     tag: 'td',
20168                     colspan: '7'
20169                 }
20170                 ]
20171             }
20172         ]
20173     },
20174     
20175     footer : {
20176         tag: 'tfoot',
20177         cn: [
20178             {
20179                 tag: 'tr',
20180                 cn: [
20181                 {
20182                     tag: 'th',
20183                     colspan: '7',
20184                     cls: '',
20185                     cn: [
20186                         {
20187                             tag: 'button',
20188                             cls: 'btn btn-info ok',
20189                             html: 'OK'
20190                         }
20191                     ]
20192                 }
20193
20194                 ]
20195             }
20196         ]
20197     }
20198 });
20199
20200 Roo.apply(Roo.bootstrap.TimeField,  {
20201   
20202     template : {
20203         tag: 'div',
20204         cls: 'datepicker dropdown-menu',
20205         cn: [
20206             {
20207                 tag: 'div',
20208                 cls: 'datepicker-time',
20209                 cn: [
20210                 {
20211                     tag: 'table',
20212                     cls: 'table-condensed',
20213                     cn:[
20214                     Roo.bootstrap.TimeField.content,
20215                     Roo.bootstrap.TimeField.footer
20216                     ]
20217                 }
20218                 ]
20219             }
20220         ]
20221     }
20222 });
20223
20224  
20225
20226  /*
20227  * - LGPL
20228  *
20229  * MonthField
20230  * 
20231  */
20232
20233 /**
20234  * @class Roo.bootstrap.MonthField
20235  * @extends Roo.bootstrap.Input
20236  * Bootstrap MonthField class
20237  * 
20238  * @cfg {String} language default en
20239  * 
20240  * @constructor
20241  * Create a new MonthField
20242  * @param {Object} config The config object
20243  */
20244
20245 Roo.bootstrap.MonthField = function(config){
20246     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20247     
20248     this.addEvents({
20249         /**
20250          * @event show
20251          * Fires when this field show.
20252          * @param {Roo.bootstrap.MonthField} this
20253          * @param {Mixed} date The date value
20254          */
20255         show : true,
20256         /**
20257          * @event show
20258          * Fires when this field hide.
20259          * @param {Roo.bootstrap.MonthField} this
20260          * @param {Mixed} date The date value
20261          */
20262         hide : true,
20263         /**
20264          * @event select
20265          * Fires when select a date.
20266          * @param {Roo.bootstrap.MonthField} this
20267          * @param {String} oldvalue The old value
20268          * @param {String} newvalue The new value
20269          */
20270         select : true
20271     });
20272 };
20273
20274 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20275     
20276     onRender: function(ct, position)
20277     {
20278         
20279         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20280         
20281         this.language = this.language || 'en';
20282         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20283         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20284         
20285         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20286         this.isInline = false;
20287         this.isInput = true;
20288         this.component = this.el.select('.add-on', true).first() || false;
20289         this.component = (this.component && this.component.length === 0) ? false : this.component;
20290         this.hasInput = this.component && this.inputEL().length;
20291         
20292         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20293         
20294         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20295         
20296         this.picker().on('mousedown', this.onMousedown, this);
20297         this.picker().on('click', this.onClick, this);
20298         
20299         this.picker().addClass('datepicker-dropdown');
20300         
20301         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20302             v.setStyle('width', '189px');
20303         });
20304         
20305         this.fillMonths();
20306         
20307         this.update();
20308         
20309         if(this.isInline) {
20310             this.show();
20311         }
20312         
20313     },
20314     
20315     setValue: function(v, suppressEvent)
20316     {   
20317         var o = this.getValue();
20318         
20319         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20320         
20321         this.update();
20322
20323         if(suppressEvent !== true){
20324             this.fireEvent('select', this, o, v);
20325         }
20326         
20327     },
20328     
20329     getValue: function()
20330     {
20331         return this.value;
20332     },
20333     
20334     onClick: function(e) 
20335     {
20336         e.stopPropagation();
20337         e.preventDefault();
20338         
20339         var target = e.getTarget();
20340         
20341         if(target.nodeName.toLowerCase() === 'i'){
20342             target = Roo.get(target).dom.parentNode;
20343         }
20344         
20345         var nodeName = target.nodeName;
20346         var className = target.className;
20347         var html = target.innerHTML;
20348         
20349         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20350             return;
20351         }
20352         
20353         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20354         
20355         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20356         
20357         this.hide();
20358                         
20359     },
20360     
20361     picker : function()
20362     {
20363         return this.pickerEl;
20364     },
20365     
20366     fillMonths: function()
20367     {    
20368         var i = 0;
20369         var months = this.picker().select('>.datepicker-months td', true).first();
20370         
20371         months.dom.innerHTML = '';
20372         
20373         while (i < 12) {
20374             var month = {
20375                 tag: 'span',
20376                 cls: 'month',
20377                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20378             };
20379             
20380             months.createChild(month);
20381         }
20382         
20383     },
20384     
20385     update: function()
20386     {
20387         var _this = this;
20388         
20389         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20390             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20391         }
20392         
20393         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20394             e.removeClass('active');
20395             
20396             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20397                 e.addClass('active');
20398             }
20399         })
20400     },
20401     
20402     place: function()
20403     {
20404         if(this.isInline) {
20405             return;
20406         }
20407         
20408         this.picker().removeClass(['bottom', 'top']);
20409         
20410         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20411             /*
20412              * place to the top of element!
20413              *
20414              */
20415             
20416             this.picker().addClass('top');
20417             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20418             
20419             return;
20420         }
20421         
20422         this.picker().addClass('bottom');
20423         
20424         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20425     },
20426     
20427     onFocus : function()
20428     {
20429         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20430         this.show();
20431     },
20432     
20433     onBlur : function()
20434     {
20435         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20436         
20437         var d = this.inputEl().getValue();
20438         
20439         this.setValue(d);
20440                 
20441         this.hide();
20442     },
20443     
20444     show : function()
20445     {
20446         this.picker().show();
20447         this.picker().select('>.datepicker-months', true).first().show();
20448         this.update();
20449         this.place();
20450         
20451         this.fireEvent('show', this, this.date);
20452     },
20453     
20454     hide : function()
20455     {
20456         if(this.isInline) {
20457             return;
20458         }
20459         this.picker().hide();
20460         this.fireEvent('hide', this, this.date);
20461         
20462     },
20463     
20464     onMousedown: function(e)
20465     {
20466         e.stopPropagation();
20467         e.preventDefault();
20468     },
20469     
20470     keyup: function(e)
20471     {
20472         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20473         this.update();
20474     },
20475
20476     fireKey: function(e)
20477     {
20478         if (!this.picker().isVisible()){
20479             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20480                 this.show();
20481             }
20482             return;
20483         }
20484         
20485         var dir;
20486         
20487         switch(e.keyCode){
20488             case 27: // escape
20489                 this.hide();
20490                 e.preventDefault();
20491                 break;
20492             case 37: // left
20493             case 39: // right
20494                 dir = e.keyCode == 37 ? -1 : 1;
20495                 
20496                 this.vIndex = this.vIndex + dir;
20497                 
20498                 if(this.vIndex < 0){
20499                     this.vIndex = 0;
20500                 }
20501                 
20502                 if(this.vIndex > 11){
20503                     this.vIndex = 11;
20504                 }
20505                 
20506                 if(isNaN(this.vIndex)){
20507                     this.vIndex = 0;
20508                 }
20509                 
20510                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20511                 
20512                 break;
20513             case 38: // up
20514             case 40: // down
20515                 
20516                 dir = e.keyCode == 38 ? -1 : 1;
20517                 
20518                 this.vIndex = this.vIndex + dir * 4;
20519                 
20520                 if(this.vIndex < 0){
20521                     this.vIndex = 0;
20522                 }
20523                 
20524                 if(this.vIndex > 11){
20525                     this.vIndex = 11;
20526                 }
20527                 
20528                 if(isNaN(this.vIndex)){
20529                     this.vIndex = 0;
20530                 }
20531                 
20532                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20533                 break;
20534                 
20535             case 13: // enter
20536                 
20537                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20538                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20539                 }
20540                 
20541                 this.hide();
20542                 e.preventDefault();
20543                 break;
20544             case 9: // tab
20545                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20546                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20547                 }
20548                 this.hide();
20549                 break;
20550             case 16: // shift
20551             case 17: // ctrl
20552             case 18: // alt
20553                 break;
20554             default :
20555                 this.hide();
20556                 
20557         }
20558     },
20559     
20560     remove: function() 
20561     {
20562         this.picker().remove();
20563     }
20564    
20565 });
20566
20567 Roo.apply(Roo.bootstrap.MonthField,  {
20568     
20569     content : {
20570         tag: 'tbody',
20571         cn: [
20572         {
20573             tag: 'tr',
20574             cn: [
20575             {
20576                 tag: 'td',
20577                 colspan: '7'
20578             }
20579             ]
20580         }
20581         ]
20582     },
20583     
20584     dates:{
20585         en: {
20586             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20587             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20588         }
20589     }
20590 });
20591
20592 Roo.apply(Roo.bootstrap.MonthField,  {
20593   
20594     template : {
20595         tag: 'div',
20596         cls: 'datepicker dropdown-menu roo-dynamic',
20597         cn: [
20598             {
20599                 tag: 'div',
20600                 cls: 'datepicker-months',
20601                 cn: [
20602                 {
20603                     tag: 'table',
20604                     cls: 'table-condensed',
20605                     cn:[
20606                         Roo.bootstrap.DateField.content
20607                     ]
20608                 }
20609                 ]
20610             }
20611         ]
20612     }
20613 });
20614
20615  
20616
20617  
20618  /*
20619  * - LGPL
20620  *
20621  * CheckBox
20622  * 
20623  */
20624
20625 /**
20626  * @class Roo.bootstrap.CheckBox
20627  * @extends Roo.bootstrap.Input
20628  * Bootstrap CheckBox class
20629  * 
20630  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20631  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20632  * @cfg {String} boxLabel The text that appears beside the checkbox
20633  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20634  * @cfg {Boolean} checked initnal the element
20635  * @cfg {Boolean} inline inline the element (default false)
20636  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20637  * @cfg {String} tooltip label tooltip
20638  * 
20639  * @constructor
20640  * Create a new CheckBox
20641  * @param {Object} config The config object
20642  */
20643
20644 Roo.bootstrap.CheckBox = function(config){
20645     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20646    
20647     this.addEvents({
20648         /**
20649         * @event check
20650         * Fires when the element is checked or unchecked.
20651         * @param {Roo.bootstrap.CheckBox} this This input
20652         * @param {Boolean} checked The new checked value
20653         */
20654        check : true,
20655        /**
20656         * @event click
20657         * Fires when the element is click.
20658         * @param {Roo.bootstrap.CheckBox} this This input
20659         */
20660        click : true
20661     });
20662     
20663 };
20664
20665 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20666   
20667     inputType: 'checkbox',
20668     inputValue: 1,
20669     valueOff: 0,
20670     boxLabel: false,
20671     checked: false,
20672     weight : false,
20673     inline: false,
20674     tooltip : '',
20675     
20676     getAutoCreate : function()
20677     {
20678         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20679         
20680         var id = Roo.id();
20681         
20682         var cfg = {};
20683         
20684         cfg.cls = 'form-group ' + this.inputType; //input-group
20685         
20686         if(this.inline){
20687             cfg.cls += ' ' + this.inputType + '-inline';
20688         }
20689         
20690         var input =  {
20691             tag: 'input',
20692             id : id,
20693             type : this.inputType,
20694             value : this.inputValue,
20695             cls : 'roo-' + this.inputType, //'form-box',
20696             placeholder : this.placeholder || ''
20697             
20698         };
20699         
20700         if(this.inputType != 'radio'){
20701             var hidden =  {
20702                 tag: 'input',
20703                 type : 'hidden',
20704                 cls : 'roo-hidden-value',
20705                 value : this.checked ? this.inputValue : this.valueOff
20706             };
20707         }
20708         
20709             
20710         if (this.weight) { // Validity check?
20711             cfg.cls += " " + this.inputType + "-" + this.weight;
20712         }
20713         
20714         if (this.disabled) {
20715             input.disabled=true;
20716         }
20717         
20718         if(this.checked){
20719             input.checked = this.checked;
20720         }
20721         
20722         if (this.name) {
20723             
20724             input.name = this.name;
20725             
20726             if(this.inputType != 'radio'){
20727                 hidden.name = this.name;
20728                 input.name = '_hidden_' + this.name;
20729             }
20730         }
20731         
20732         if (this.size) {
20733             input.cls += ' input-' + this.size;
20734         }
20735         
20736         var settings=this;
20737         
20738         ['xs','sm','md','lg'].map(function(size){
20739             if (settings[size]) {
20740                 cfg.cls += ' col-' + size + '-' + settings[size];
20741             }
20742         });
20743         
20744         var inputblock = input;
20745          
20746         if (this.before || this.after) {
20747             
20748             inputblock = {
20749                 cls : 'input-group',
20750                 cn :  [] 
20751             };
20752             
20753             if (this.before) {
20754                 inputblock.cn.push({
20755                     tag :'span',
20756                     cls : 'input-group-addon',
20757                     html : this.before
20758                 });
20759             }
20760             
20761             inputblock.cn.push(input);
20762             
20763             if(this.inputType != 'radio'){
20764                 inputblock.cn.push(hidden);
20765             }
20766             
20767             if (this.after) {
20768                 inputblock.cn.push({
20769                     tag :'span',
20770                     cls : 'input-group-addon',
20771                     html : this.after
20772                 });
20773             }
20774             
20775         }
20776         
20777         if (align ==='left' && this.fieldLabel.length) {
20778 //                Roo.log("left and has label");
20779             cfg.cn = [
20780                 {
20781                     tag: 'label',
20782                     'for' :  id,
20783                     cls : 'control-label',
20784                     html : this.fieldLabel
20785                 },
20786                 {
20787                     cls : "", 
20788                     cn: [
20789                         inputblock
20790                     ]
20791                 }
20792             ];
20793             
20794             if(this.labelWidth > 12){
20795                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20796             }
20797             
20798             if(this.labelWidth < 13 && this.labelmd == 0){
20799                 this.labelmd = this.labelWidth;
20800             }
20801             
20802             if(this.labellg > 0){
20803                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20804                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20805             }
20806             
20807             if(this.labelmd > 0){
20808                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20809                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20810             }
20811             
20812             if(this.labelsm > 0){
20813                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20814                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20815             }
20816             
20817             if(this.labelxs > 0){
20818                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20819                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20820             }
20821             
20822         } else if ( this.fieldLabel.length) {
20823 //                Roo.log(" label");
20824                 cfg.cn = [
20825                    
20826                     {
20827                         tag: this.boxLabel ? 'span' : 'label',
20828                         'for': id,
20829                         cls: 'control-label box-input-label',
20830                         //cls : 'input-group-addon',
20831                         html : this.fieldLabel
20832                     },
20833                     
20834                     inputblock
20835                     
20836                 ];
20837
20838         } else {
20839             
20840 //                Roo.log(" no label && no align");
20841                 cfg.cn = [  inputblock ] ;
20842                 
20843                 
20844         }
20845         
20846         if(this.boxLabel){
20847              var boxLabelCfg = {
20848                 tag: 'label',
20849                 //'for': id, // box label is handled by onclick - so no for...
20850                 cls: 'box-label',
20851                 html: this.boxLabel
20852             };
20853             
20854             if(this.tooltip){
20855                 boxLabelCfg.tooltip = this.tooltip;
20856             }
20857              
20858             cfg.cn.push(boxLabelCfg);
20859         }
20860         
20861         if(this.inputType != 'radio'){
20862             cfg.cn.push(hidden);
20863         }
20864         
20865         return cfg;
20866         
20867     },
20868     
20869     /**
20870      * return the real input element.
20871      */
20872     inputEl: function ()
20873     {
20874         return this.el.select('input.roo-' + this.inputType,true).first();
20875     },
20876     hiddenEl: function ()
20877     {
20878         return this.el.select('input.roo-hidden-value',true).first();
20879     },
20880     
20881     labelEl: function()
20882     {
20883         return this.el.select('label.control-label',true).first();
20884     },
20885     /* depricated... */
20886     
20887     label: function()
20888     {
20889         return this.labelEl();
20890     },
20891     
20892     boxLabelEl: function()
20893     {
20894         return this.el.select('label.box-label',true).first();
20895     },
20896     
20897     initEvents : function()
20898     {
20899 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20900         
20901         this.inputEl().on('click', this.onClick,  this);
20902         
20903         if (this.boxLabel) { 
20904             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20905         }
20906         
20907         this.startValue = this.getValue();
20908         
20909         if(this.groupId){
20910             Roo.bootstrap.CheckBox.register(this);
20911         }
20912     },
20913     
20914     onClick : function(e)
20915     {   
20916         if(this.fireEvent('click', this, e) !== false){
20917             this.setChecked(!this.checked);
20918         }
20919         
20920     },
20921     
20922     setChecked : function(state,suppressEvent)
20923     {
20924         this.startValue = this.getValue();
20925
20926         if(this.inputType == 'radio'){
20927             
20928             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20929                 e.dom.checked = false;
20930             });
20931             
20932             this.inputEl().dom.checked = true;
20933             
20934             this.inputEl().dom.value = this.inputValue;
20935             
20936             if(suppressEvent !== true){
20937                 this.fireEvent('check', this, true);
20938             }
20939             
20940             this.validate();
20941             
20942             return;
20943         }
20944         
20945         this.checked = state;
20946         
20947         this.inputEl().dom.checked = state;
20948         
20949         
20950         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20951         
20952         if(suppressEvent !== true){
20953             this.fireEvent('check', this, state);
20954         }
20955         
20956         this.validate();
20957     },
20958     
20959     getValue : function()
20960     {
20961         if(this.inputType == 'radio'){
20962             return this.getGroupValue();
20963         }
20964         
20965         return this.hiddenEl().dom.value;
20966         
20967     },
20968     
20969     getGroupValue : function()
20970     {
20971         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20972             return '';
20973         }
20974         
20975         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20976     },
20977     
20978     setValue : function(v,suppressEvent)
20979     {
20980         if(this.inputType == 'radio'){
20981             this.setGroupValue(v, suppressEvent);
20982             return;
20983         }
20984         
20985         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20986         
20987         this.validate();
20988     },
20989     
20990     setGroupValue : function(v, suppressEvent)
20991     {
20992         this.startValue = this.getValue();
20993         
20994         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20995             e.dom.checked = false;
20996             
20997             if(e.dom.value == v){
20998                 e.dom.checked = true;
20999             }
21000         });
21001         
21002         if(suppressEvent !== true){
21003             this.fireEvent('check', this, true);
21004         }
21005
21006         this.validate();
21007         
21008         return;
21009     },
21010     
21011     validate : function()
21012     {
21013         if(this.getVisibilityEl().hasClass('hidden')){
21014             return true;
21015         }
21016         
21017         if(
21018                 this.disabled || 
21019                 (this.inputType == 'radio' && this.validateRadio()) ||
21020                 (this.inputType == 'checkbox' && this.validateCheckbox())
21021         ){
21022             this.markValid();
21023             return true;
21024         }
21025         
21026         this.markInvalid();
21027         return false;
21028     },
21029     
21030     validateRadio : function()
21031     {
21032         if(this.getVisibilityEl().hasClass('hidden')){
21033             return true;
21034         }
21035         
21036         if(this.allowBlank){
21037             return true;
21038         }
21039         
21040         var valid = false;
21041         
21042         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21043             if(!e.dom.checked){
21044                 return;
21045             }
21046             
21047             valid = true;
21048             
21049             return false;
21050         });
21051         
21052         return valid;
21053     },
21054     
21055     validateCheckbox : function()
21056     {
21057         if(!this.groupId){
21058             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21059             //return (this.getValue() == this.inputValue) ? true : false;
21060         }
21061         
21062         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21063         
21064         if(!group){
21065             return false;
21066         }
21067         
21068         var r = false;
21069         
21070         for(var i in group){
21071             if(group[i].el.isVisible(true)){
21072                 r = false;
21073                 break;
21074             }
21075             
21076             r = true;
21077         }
21078         
21079         for(var i in group){
21080             if(r){
21081                 break;
21082             }
21083             
21084             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21085         }
21086         
21087         return r;
21088     },
21089     
21090     /**
21091      * Mark this field as valid
21092      */
21093     markValid : function()
21094     {
21095         var _this = this;
21096         
21097         this.fireEvent('valid', this);
21098         
21099         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21100         
21101         if(this.groupId){
21102             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21103         }
21104         
21105         if(label){
21106             label.markValid();
21107         }
21108
21109         if(this.inputType == 'radio'){
21110             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21111                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21112                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21113             });
21114             
21115             return;
21116         }
21117
21118         if(!this.groupId){
21119             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21120             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21121             return;
21122         }
21123         
21124         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21125         
21126         if(!group){
21127             return;
21128         }
21129         
21130         for(var i in group){
21131             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21132             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21133         }
21134     },
21135     
21136      /**
21137      * Mark this field as invalid
21138      * @param {String} msg The validation message
21139      */
21140     markInvalid : function(msg)
21141     {
21142         if(this.allowBlank){
21143             return;
21144         }
21145         
21146         var _this = this;
21147         
21148         this.fireEvent('invalid', this, msg);
21149         
21150         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21151         
21152         if(this.groupId){
21153             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21154         }
21155         
21156         if(label){
21157             label.markInvalid();
21158         }
21159             
21160         if(this.inputType == 'radio'){
21161             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21162                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21163                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21164             });
21165             
21166             return;
21167         }
21168         
21169         if(!this.groupId){
21170             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21171             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21172             return;
21173         }
21174         
21175         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21176         
21177         if(!group){
21178             return;
21179         }
21180         
21181         for(var i in group){
21182             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21183             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21184         }
21185         
21186     },
21187     
21188     clearInvalid : function()
21189     {
21190         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21191         
21192         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21193         
21194         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21195         
21196         if (label && label.iconEl) {
21197             label.iconEl.removeClass(label.validClass);
21198             label.iconEl.removeClass(label.invalidClass);
21199         }
21200     },
21201     
21202     disable : function()
21203     {
21204         if(this.inputType != 'radio'){
21205             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21206             return;
21207         }
21208         
21209         var _this = this;
21210         
21211         if(this.rendered){
21212             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21213                 _this.getActionEl().addClass(this.disabledClass);
21214                 e.dom.disabled = true;
21215             });
21216         }
21217         
21218         this.disabled = true;
21219         this.fireEvent("disable", this);
21220         return this;
21221     },
21222
21223     enable : function()
21224     {
21225         if(this.inputType != 'radio'){
21226             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21227             return;
21228         }
21229         
21230         var _this = this;
21231         
21232         if(this.rendered){
21233             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21234                 _this.getActionEl().removeClass(this.disabledClass);
21235                 e.dom.disabled = false;
21236             });
21237         }
21238         
21239         this.disabled = false;
21240         this.fireEvent("enable", this);
21241         return this;
21242     },
21243     
21244     setBoxLabel : function(v)
21245     {
21246         this.boxLabel = v;
21247         
21248         if(this.rendered){
21249             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21250         }
21251     }
21252
21253 });
21254
21255 Roo.apply(Roo.bootstrap.CheckBox, {
21256     
21257     groups: {},
21258     
21259      /**
21260     * register a CheckBox Group
21261     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21262     */
21263     register : function(checkbox)
21264     {
21265         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21266             this.groups[checkbox.groupId] = {};
21267         }
21268         
21269         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21270             return;
21271         }
21272         
21273         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21274         
21275     },
21276     /**
21277     * fetch a CheckBox Group based on the group ID
21278     * @param {string} the group ID
21279     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21280     */
21281     get: function(groupId) {
21282         if (typeof(this.groups[groupId]) == 'undefined') {
21283             return false;
21284         }
21285         
21286         return this.groups[groupId] ;
21287     }
21288     
21289     
21290 });
21291 /*
21292  * - LGPL
21293  *
21294  * RadioItem
21295  * 
21296  */
21297
21298 /**
21299  * @class Roo.bootstrap.Radio
21300  * @extends Roo.bootstrap.Component
21301  * Bootstrap Radio class
21302  * @cfg {String} boxLabel - the label associated
21303  * @cfg {String} value - the value of radio
21304  * 
21305  * @constructor
21306  * Create a new Radio
21307  * @param {Object} config The config object
21308  */
21309 Roo.bootstrap.Radio = function(config){
21310     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21311     
21312 };
21313
21314 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21315     
21316     boxLabel : '',
21317     
21318     value : '',
21319     
21320     getAutoCreate : function()
21321     {
21322         var cfg = {
21323             tag : 'div',
21324             cls : 'form-group radio',
21325             cn : [
21326                 {
21327                     tag : 'label',
21328                     cls : 'box-label',
21329                     html : this.boxLabel
21330                 }
21331             ]
21332         };
21333         
21334         return cfg;
21335     },
21336     
21337     initEvents : function() 
21338     {
21339         this.parent().register(this);
21340         
21341         this.el.on('click', this.onClick, this);
21342         
21343     },
21344     
21345     onClick : function(e)
21346     {
21347         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21348             this.setChecked(true);
21349         }
21350     },
21351     
21352     setChecked : function(state, suppressEvent)
21353     {
21354         this.parent().setValue(this.value, suppressEvent);
21355         
21356     },
21357     
21358     setBoxLabel : function(v)
21359     {
21360         this.boxLabel = v;
21361         
21362         if(this.rendered){
21363             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21364         }
21365     }
21366     
21367 });
21368  
21369
21370  /*
21371  * - LGPL
21372  *
21373  * Input
21374  * 
21375  */
21376
21377 /**
21378  * @class Roo.bootstrap.SecurePass
21379  * @extends Roo.bootstrap.Input
21380  * Bootstrap SecurePass class
21381  *
21382  * 
21383  * @constructor
21384  * Create a new SecurePass
21385  * @param {Object} config The config object
21386  */
21387  
21388 Roo.bootstrap.SecurePass = function (config) {
21389     // these go here, so the translation tool can replace them..
21390     this.errors = {
21391         PwdEmpty: "Please type a password, and then retype it to confirm.",
21392         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21393         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21394         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21395         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21396         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21397         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21398         TooWeak: "Your password is Too Weak."
21399     },
21400     this.meterLabel = "Password strength:";
21401     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21402     this.meterClass = [
21403         "roo-password-meter-tooweak", 
21404         "roo-password-meter-weak", 
21405         "roo-password-meter-medium", 
21406         "roo-password-meter-strong", 
21407         "roo-password-meter-grey"
21408     ];
21409     
21410     this.errors = {};
21411     
21412     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21413 }
21414
21415 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21416     /**
21417      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21418      * {
21419      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21420      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21421      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21422      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21423      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21424      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21425      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21426      * })
21427      */
21428     // private
21429     
21430     meterWidth: 300,
21431     errorMsg :'',    
21432     errors: false,
21433     imageRoot: '/',
21434     /**
21435      * @cfg {String/Object} Label for the strength meter (defaults to
21436      * 'Password strength:')
21437      */
21438     // private
21439     meterLabel: '',
21440     /**
21441      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21442      * ['Weak', 'Medium', 'Strong'])
21443      */
21444     // private    
21445     pwdStrengths: false,    
21446     // private
21447     strength: 0,
21448     // private
21449     _lastPwd: null,
21450     // private
21451     kCapitalLetter: 0,
21452     kSmallLetter: 1,
21453     kDigit: 2,
21454     kPunctuation: 3,
21455     
21456     insecure: false,
21457     // private
21458     initEvents: function ()
21459     {
21460         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21461
21462         if (this.el.is('input[type=password]') && Roo.isSafari) {
21463             this.el.on('keydown', this.SafariOnKeyDown, this);
21464         }
21465
21466         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21467     },
21468     // private
21469     onRender: function (ct, position)
21470     {
21471         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21472         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21473         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21474
21475         this.trigger.createChild({
21476                    cn: [
21477                     {
21478                     //id: 'PwdMeter',
21479                     tag: 'div',
21480                     cls: 'roo-password-meter-grey col-xs-12',
21481                     style: {
21482                         //width: 0,
21483                         //width: this.meterWidth + 'px'                                                
21484                         }
21485                     },
21486                     {                            
21487                          cls: 'roo-password-meter-text'                          
21488                     }
21489                 ]            
21490         });
21491
21492          
21493         if (this.hideTrigger) {
21494             this.trigger.setDisplayed(false);
21495         }
21496         this.setSize(this.width || '', this.height || '');
21497     },
21498     // private
21499     onDestroy: function ()
21500     {
21501         if (this.trigger) {
21502             this.trigger.removeAllListeners();
21503             this.trigger.remove();
21504         }
21505         if (this.wrap) {
21506             this.wrap.remove();
21507         }
21508         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21509     },
21510     // private
21511     checkStrength: function ()
21512     {
21513         var pwd = this.inputEl().getValue();
21514         if (pwd == this._lastPwd) {
21515             return;
21516         }
21517
21518         var strength;
21519         if (this.ClientSideStrongPassword(pwd)) {
21520             strength = 3;
21521         } else if (this.ClientSideMediumPassword(pwd)) {
21522             strength = 2;
21523         } else if (this.ClientSideWeakPassword(pwd)) {
21524             strength = 1;
21525         } else {
21526             strength = 0;
21527         }
21528         
21529         Roo.log('strength1: ' + strength);
21530         
21531         //var pm = this.trigger.child('div/div/div').dom;
21532         var pm = this.trigger.child('div/div');
21533         pm.removeClass(this.meterClass);
21534         pm.addClass(this.meterClass[strength]);
21535                 
21536         
21537         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21538                 
21539         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21540         
21541         this._lastPwd = pwd;
21542     },
21543     reset: function ()
21544     {
21545         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21546         
21547         this._lastPwd = '';
21548         
21549         var pm = this.trigger.child('div/div');
21550         pm.removeClass(this.meterClass);
21551         pm.addClass('roo-password-meter-grey');        
21552         
21553         
21554         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21555         
21556         pt.innerHTML = '';
21557         this.inputEl().dom.type='password';
21558     },
21559     // private
21560     validateValue: function (value)
21561     {
21562         
21563         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21564             return false;
21565         }
21566         if (value.length == 0) {
21567             if (this.allowBlank) {
21568                 this.clearInvalid();
21569                 return true;
21570             }
21571
21572             this.markInvalid(this.errors.PwdEmpty);
21573             this.errorMsg = this.errors.PwdEmpty;
21574             return false;
21575         }
21576         
21577         if(this.insecure){
21578             return true;
21579         }
21580         
21581         if ('[\x21-\x7e]*'.match(value)) {
21582             this.markInvalid(this.errors.PwdBadChar);
21583             this.errorMsg = this.errors.PwdBadChar;
21584             return false;
21585         }
21586         if (value.length < 6) {
21587             this.markInvalid(this.errors.PwdShort);
21588             this.errorMsg = this.errors.PwdShort;
21589             return false;
21590         }
21591         if (value.length > 16) {
21592             this.markInvalid(this.errors.PwdLong);
21593             this.errorMsg = this.errors.PwdLong;
21594             return false;
21595         }
21596         var strength;
21597         if (this.ClientSideStrongPassword(value)) {
21598             strength = 3;
21599         } else if (this.ClientSideMediumPassword(value)) {
21600             strength = 2;
21601         } else if (this.ClientSideWeakPassword(value)) {
21602             strength = 1;
21603         } else {
21604             strength = 0;
21605         }
21606
21607         
21608         if (strength < 2) {
21609             //this.markInvalid(this.errors.TooWeak);
21610             this.errorMsg = this.errors.TooWeak;
21611             //return false;
21612         }
21613         
21614         
21615         console.log('strength2: ' + strength);
21616         
21617         //var pm = this.trigger.child('div/div/div').dom;
21618         
21619         var pm = this.trigger.child('div/div');
21620         pm.removeClass(this.meterClass);
21621         pm.addClass(this.meterClass[strength]);
21622                 
21623         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21624                 
21625         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21626         
21627         this.errorMsg = ''; 
21628         return true;
21629     },
21630     // private
21631     CharacterSetChecks: function (type)
21632     {
21633         this.type = type;
21634         this.fResult = false;
21635     },
21636     // private
21637     isctype: function (character, type)
21638     {
21639         switch (type) {  
21640             case this.kCapitalLetter:
21641                 if (character >= 'A' && character <= 'Z') {
21642                     return true;
21643                 }
21644                 break;
21645             
21646             case this.kSmallLetter:
21647                 if (character >= 'a' && character <= 'z') {
21648                     return true;
21649                 }
21650                 break;
21651             
21652             case this.kDigit:
21653                 if (character >= '0' && character <= '9') {
21654                     return true;
21655                 }
21656                 break;
21657             
21658             case this.kPunctuation:
21659                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21660                     return true;
21661                 }
21662                 break;
21663             
21664             default:
21665                 return false;
21666         }
21667
21668     },
21669     // private
21670     IsLongEnough: function (pwd, size)
21671     {
21672         return !(pwd == null || isNaN(size) || pwd.length < size);
21673     },
21674     // private
21675     SpansEnoughCharacterSets: function (word, nb)
21676     {
21677         if (!this.IsLongEnough(word, nb))
21678         {
21679             return false;
21680         }
21681
21682         var characterSetChecks = new Array(
21683             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21684             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21685         );
21686         
21687         for (var index = 0; index < word.length; ++index) {
21688             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21689                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21690                     characterSetChecks[nCharSet].fResult = true;
21691                     break;
21692                 }
21693             }
21694         }
21695
21696         var nCharSets = 0;
21697         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21698             if (characterSetChecks[nCharSet].fResult) {
21699                 ++nCharSets;
21700             }
21701         }
21702
21703         if (nCharSets < nb) {
21704             return false;
21705         }
21706         return true;
21707     },
21708     // private
21709     ClientSideStrongPassword: function (pwd)
21710     {
21711         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21712     },
21713     // private
21714     ClientSideMediumPassword: function (pwd)
21715     {
21716         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21717     },
21718     // private
21719     ClientSideWeakPassword: function (pwd)
21720     {
21721         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21722     }
21723           
21724 })//<script type="text/javascript">
21725
21726 /*
21727  * Based  Ext JS Library 1.1.1
21728  * Copyright(c) 2006-2007, Ext JS, LLC.
21729  * LGPL
21730  *
21731  */
21732  
21733 /**
21734  * @class Roo.HtmlEditorCore
21735  * @extends Roo.Component
21736  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21737  *
21738  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21739  */
21740
21741 Roo.HtmlEditorCore = function(config){
21742     
21743     
21744     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21745     
21746     
21747     this.addEvents({
21748         /**
21749          * @event initialize
21750          * Fires when the editor is fully initialized (including the iframe)
21751          * @param {Roo.HtmlEditorCore} this
21752          */
21753         initialize: true,
21754         /**
21755          * @event activate
21756          * Fires when the editor is first receives the focus. Any insertion must wait
21757          * until after this event.
21758          * @param {Roo.HtmlEditorCore} this
21759          */
21760         activate: true,
21761          /**
21762          * @event beforesync
21763          * Fires before the textarea is updated with content from the editor iframe. Return false
21764          * to cancel the sync.
21765          * @param {Roo.HtmlEditorCore} this
21766          * @param {String} html
21767          */
21768         beforesync: true,
21769          /**
21770          * @event beforepush
21771          * Fires before the iframe editor is updated with content from the textarea. Return false
21772          * to cancel the push.
21773          * @param {Roo.HtmlEditorCore} this
21774          * @param {String} html
21775          */
21776         beforepush: true,
21777          /**
21778          * @event sync
21779          * Fires when the textarea is updated with content from the editor iframe.
21780          * @param {Roo.HtmlEditorCore} this
21781          * @param {String} html
21782          */
21783         sync: true,
21784          /**
21785          * @event push
21786          * Fires when the iframe editor is updated with content from the textarea.
21787          * @param {Roo.HtmlEditorCore} this
21788          * @param {String} html
21789          */
21790         push: true,
21791         
21792         /**
21793          * @event editorevent
21794          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21795          * @param {Roo.HtmlEditorCore} this
21796          */
21797         editorevent: true
21798         
21799     });
21800     
21801     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21802     
21803     // defaults : white / black...
21804     this.applyBlacklists();
21805     
21806     
21807     
21808 };
21809
21810
21811 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21812
21813
21814      /**
21815      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21816      */
21817     
21818     owner : false,
21819     
21820      /**
21821      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21822      *                        Roo.resizable.
21823      */
21824     resizable : false,
21825      /**
21826      * @cfg {Number} height (in pixels)
21827      */   
21828     height: 300,
21829    /**
21830      * @cfg {Number} width (in pixels)
21831      */   
21832     width: 500,
21833     
21834     /**
21835      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21836      * 
21837      */
21838     stylesheets: false,
21839     
21840     // id of frame..
21841     frameId: false,
21842     
21843     // private properties
21844     validationEvent : false,
21845     deferHeight: true,
21846     initialized : false,
21847     activated : false,
21848     sourceEditMode : false,
21849     onFocus : Roo.emptyFn,
21850     iframePad:3,
21851     hideMode:'offsets',
21852     
21853     clearUp: true,
21854     
21855     // blacklist + whitelisted elements..
21856     black: false,
21857     white: false,
21858      
21859     bodyCls : '',
21860
21861     /**
21862      * Protected method that will not generally be called directly. It
21863      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21864      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21865      */
21866     getDocMarkup : function(){
21867         // body styles..
21868         var st = '';
21869         
21870         // inherit styels from page...?? 
21871         if (this.stylesheets === false) {
21872             
21873             Roo.get(document.head).select('style').each(function(node) {
21874                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21875             });
21876             
21877             Roo.get(document.head).select('link').each(function(node) { 
21878                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21879             });
21880             
21881         } else if (!this.stylesheets.length) {
21882                 // simple..
21883                 st = '<style type="text/css">' +
21884                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21885                    '</style>';
21886         } else { 
21887             st = '<style type="text/css">' +
21888                     this.stylesheets +
21889                 '</style>';
21890         }
21891         
21892         st +=  '<style type="text/css">' +
21893             'IMG { cursor: pointer } ' +
21894         '</style>';
21895
21896         var cls = 'roo-htmleditor-body';
21897         
21898         if(this.bodyCls.length){
21899             cls += ' ' + this.bodyCls;
21900         }
21901         
21902         return '<html><head>' + st  +
21903             //<style type="text/css">' +
21904             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21905             //'</style>' +
21906             ' </head><body class="' +  cls + '"></body></html>';
21907     },
21908
21909     // private
21910     onRender : function(ct, position)
21911     {
21912         var _t = this;
21913         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21914         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21915         
21916         
21917         this.el.dom.style.border = '0 none';
21918         this.el.dom.setAttribute('tabIndex', -1);
21919         this.el.addClass('x-hidden hide');
21920         
21921         
21922         
21923         if(Roo.isIE){ // fix IE 1px bogus margin
21924             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21925         }
21926        
21927         
21928         this.frameId = Roo.id();
21929         
21930          
21931         
21932         var iframe = this.owner.wrap.createChild({
21933             tag: 'iframe',
21934             cls: 'form-control', // bootstrap..
21935             id: this.frameId,
21936             name: this.frameId,
21937             frameBorder : 'no',
21938             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21939         }, this.el
21940         );
21941         
21942         
21943         this.iframe = iframe.dom;
21944
21945          this.assignDocWin();
21946         
21947         this.doc.designMode = 'on';
21948        
21949         this.doc.open();
21950         this.doc.write(this.getDocMarkup());
21951         this.doc.close();
21952
21953         
21954         var task = { // must defer to wait for browser to be ready
21955             run : function(){
21956                 //console.log("run task?" + this.doc.readyState);
21957                 this.assignDocWin();
21958                 if(this.doc.body || this.doc.readyState == 'complete'){
21959                     try {
21960                         this.doc.designMode="on";
21961                     } catch (e) {
21962                         return;
21963                     }
21964                     Roo.TaskMgr.stop(task);
21965                     this.initEditor.defer(10, this);
21966                 }
21967             },
21968             interval : 10,
21969             duration: 10000,
21970             scope: this
21971         };
21972         Roo.TaskMgr.start(task);
21973
21974     },
21975
21976     // private
21977     onResize : function(w, h)
21978     {
21979          Roo.log('resize: ' +w + ',' + h );
21980         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21981         if(!this.iframe){
21982             return;
21983         }
21984         if(typeof w == 'number'){
21985             
21986             this.iframe.style.width = w + 'px';
21987         }
21988         if(typeof h == 'number'){
21989             
21990             this.iframe.style.height = h + 'px';
21991             if(this.doc){
21992                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21993             }
21994         }
21995         
21996     },
21997
21998     /**
21999      * Toggles the editor between standard and source edit mode.
22000      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22001      */
22002     toggleSourceEdit : function(sourceEditMode){
22003         
22004         this.sourceEditMode = sourceEditMode === true;
22005         
22006         if(this.sourceEditMode){
22007  
22008             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22009             
22010         }else{
22011             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22012             //this.iframe.className = '';
22013             this.deferFocus();
22014         }
22015         //this.setSize(this.owner.wrap.getSize());
22016         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22017     },
22018
22019     
22020   
22021
22022     /**
22023      * Protected method that will not generally be called directly. If you need/want
22024      * custom HTML cleanup, this is the method you should override.
22025      * @param {String} html The HTML to be cleaned
22026      * return {String} The cleaned HTML
22027      */
22028     cleanHtml : function(html){
22029         html = String(html);
22030         if(html.length > 5){
22031             if(Roo.isSafari){ // strip safari nonsense
22032                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22033             }
22034         }
22035         if(html == '&nbsp;'){
22036             html = '';
22037         }
22038         return html;
22039     },
22040
22041     /**
22042      * HTML Editor -> Textarea
22043      * Protected method that will not generally be called directly. Syncs the contents
22044      * of the editor iframe with the textarea.
22045      */
22046     syncValue : function(){
22047         if(this.initialized){
22048             var bd = (this.doc.body || this.doc.documentElement);
22049             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22050             var html = bd.innerHTML;
22051             if(Roo.isSafari){
22052                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22053                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22054                 if(m && m[1]){
22055                     html = '<div style="'+m[0]+'">' + html + '</div>';
22056                 }
22057             }
22058             html = this.cleanHtml(html);
22059             // fix up the special chars.. normaly like back quotes in word...
22060             // however we do not want to do this with chinese..
22061             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22062                 var cc = b.charCodeAt();
22063                 if (
22064                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22065                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22066                     (cc >= 0xf900 && cc < 0xfb00 )
22067                 ) {
22068                         return b;
22069                 }
22070                 return "&#"+cc+";" 
22071             });
22072             if(this.owner.fireEvent('beforesync', this, html) !== false){
22073                 this.el.dom.value = html;
22074                 this.owner.fireEvent('sync', this, html);
22075             }
22076         }
22077     },
22078
22079     /**
22080      * Protected method that will not generally be called directly. Pushes the value of the textarea
22081      * into the iframe editor.
22082      */
22083     pushValue : function(){
22084         if(this.initialized){
22085             var v = this.el.dom.value.trim();
22086             
22087 //            if(v.length < 1){
22088 //                v = '&#160;';
22089 //            }
22090             
22091             if(this.owner.fireEvent('beforepush', this, v) !== false){
22092                 var d = (this.doc.body || this.doc.documentElement);
22093                 d.innerHTML = v;
22094                 this.cleanUpPaste();
22095                 this.el.dom.value = d.innerHTML;
22096                 this.owner.fireEvent('push', this, v);
22097             }
22098         }
22099     },
22100
22101     // private
22102     deferFocus : function(){
22103         this.focus.defer(10, this);
22104     },
22105
22106     // doc'ed in Field
22107     focus : function(){
22108         if(this.win && !this.sourceEditMode){
22109             this.win.focus();
22110         }else{
22111             this.el.focus();
22112         }
22113     },
22114     
22115     assignDocWin: function()
22116     {
22117         var iframe = this.iframe;
22118         
22119          if(Roo.isIE){
22120             this.doc = iframe.contentWindow.document;
22121             this.win = iframe.contentWindow;
22122         } else {
22123 //            if (!Roo.get(this.frameId)) {
22124 //                return;
22125 //            }
22126 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22127 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22128             
22129             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22130                 return;
22131             }
22132             
22133             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22134             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22135         }
22136     },
22137     
22138     // private
22139     initEditor : function(){
22140         //console.log("INIT EDITOR");
22141         this.assignDocWin();
22142         
22143         
22144         
22145         this.doc.designMode="on";
22146         this.doc.open();
22147         this.doc.write(this.getDocMarkup());
22148         this.doc.close();
22149         
22150         var dbody = (this.doc.body || this.doc.documentElement);
22151         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22152         // this copies styles from the containing element into thsi one..
22153         // not sure why we need all of this..
22154         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22155         
22156         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22157         //ss['background-attachment'] = 'fixed'; // w3c
22158         dbody.bgProperties = 'fixed'; // ie
22159         //Roo.DomHelper.applyStyles(dbody, ss);
22160         Roo.EventManager.on(this.doc, {
22161             //'mousedown': this.onEditorEvent,
22162             'mouseup': this.onEditorEvent,
22163             'dblclick': this.onEditorEvent,
22164             'click': this.onEditorEvent,
22165             'keyup': this.onEditorEvent,
22166             buffer:100,
22167             scope: this
22168         });
22169         if(Roo.isGecko){
22170             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22171         }
22172         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22173             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22174         }
22175         this.initialized = true;
22176
22177         this.owner.fireEvent('initialize', this);
22178         this.pushValue();
22179     },
22180
22181     // private
22182     onDestroy : function(){
22183         
22184         
22185         
22186         if(this.rendered){
22187             
22188             //for (var i =0; i < this.toolbars.length;i++) {
22189             //    // fixme - ask toolbars for heights?
22190             //    this.toolbars[i].onDestroy();
22191            // }
22192             
22193             //this.wrap.dom.innerHTML = '';
22194             //this.wrap.remove();
22195         }
22196     },
22197
22198     // private
22199     onFirstFocus : function(){
22200         
22201         this.assignDocWin();
22202         
22203         
22204         this.activated = true;
22205          
22206     
22207         if(Roo.isGecko){ // prevent silly gecko errors
22208             this.win.focus();
22209             var s = this.win.getSelection();
22210             if(!s.focusNode || s.focusNode.nodeType != 3){
22211                 var r = s.getRangeAt(0);
22212                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22213                 r.collapse(true);
22214                 this.deferFocus();
22215             }
22216             try{
22217                 this.execCmd('useCSS', true);
22218                 this.execCmd('styleWithCSS', false);
22219             }catch(e){}
22220         }
22221         this.owner.fireEvent('activate', this);
22222     },
22223
22224     // private
22225     adjustFont: function(btn){
22226         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22227         //if(Roo.isSafari){ // safari
22228         //    adjust *= 2;
22229        // }
22230         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22231         if(Roo.isSafari){ // safari
22232             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22233             v =  (v < 10) ? 10 : v;
22234             v =  (v > 48) ? 48 : v;
22235             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22236             
22237         }
22238         
22239         
22240         v = Math.max(1, v+adjust);
22241         
22242         this.execCmd('FontSize', v  );
22243     },
22244
22245     onEditorEvent : function(e)
22246     {
22247         this.owner.fireEvent('editorevent', this, e);
22248       //  this.updateToolbar();
22249         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22250     },
22251
22252     insertTag : function(tg)
22253     {
22254         // could be a bit smarter... -> wrap the current selected tRoo..
22255         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22256             
22257             range = this.createRange(this.getSelection());
22258             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22259             wrappingNode.appendChild(range.extractContents());
22260             range.insertNode(wrappingNode);
22261
22262             return;
22263             
22264             
22265             
22266         }
22267         this.execCmd("formatblock",   tg);
22268         
22269     },
22270     
22271     insertText : function(txt)
22272     {
22273         
22274         
22275         var range = this.createRange();
22276         range.deleteContents();
22277                //alert(Sender.getAttribute('label'));
22278                
22279         range.insertNode(this.doc.createTextNode(txt));
22280     } ,
22281     
22282      
22283
22284     /**
22285      * Executes a Midas editor command on the editor document and performs necessary focus and
22286      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22287      * @param {String} cmd The Midas command
22288      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22289      */
22290     relayCmd : function(cmd, value){
22291         this.win.focus();
22292         this.execCmd(cmd, value);
22293         this.owner.fireEvent('editorevent', this);
22294         //this.updateToolbar();
22295         this.owner.deferFocus();
22296     },
22297
22298     /**
22299      * Executes a Midas editor command directly on the editor document.
22300      * For visual commands, you should use {@link #relayCmd} instead.
22301      * <b>This should only be called after the editor is initialized.</b>
22302      * @param {String} cmd The Midas command
22303      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22304      */
22305     execCmd : function(cmd, value){
22306         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22307         this.syncValue();
22308     },
22309  
22310  
22311    
22312     /**
22313      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22314      * to insert tRoo.
22315      * @param {String} text | dom node.. 
22316      */
22317     insertAtCursor : function(text)
22318     {
22319         
22320         if(!this.activated){
22321             return;
22322         }
22323         /*
22324         if(Roo.isIE){
22325             this.win.focus();
22326             var r = this.doc.selection.createRange();
22327             if(r){
22328                 r.collapse(true);
22329                 r.pasteHTML(text);
22330                 this.syncValue();
22331                 this.deferFocus();
22332             
22333             }
22334             return;
22335         }
22336         */
22337         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22338             this.win.focus();
22339             
22340             
22341             // from jquery ui (MIT licenced)
22342             var range, node;
22343             var win = this.win;
22344             
22345             if (win.getSelection && win.getSelection().getRangeAt) {
22346                 range = win.getSelection().getRangeAt(0);
22347                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22348                 range.insertNode(node);
22349             } else if (win.document.selection && win.document.selection.createRange) {
22350                 // no firefox support
22351                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22352                 win.document.selection.createRange().pasteHTML(txt);
22353             } else {
22354                 // no firefox support
22355                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22356                 this.execCmd('InsertHTML', txt);
22357             } 
22358             
22359             this.syncValue();
22360             
22361             this.deferFocus();
22362         }
22363     },
22364  // private
22365     mozKeyPress : function(e){
22366         if(e.ctrlKey){
22367             var c = e.getCharCode(), cmd;
22368           
22369             if(c > 0){
22370                 c = String.fromCharCode(c).toLowerCase();
22371                 switch(c){
22372                     case 'b':
22373                         cmd = 'bold';
22374                         break;
22375                     case 'i':
22376                         cmd = 'italic';
22377                         break;
22378                     
22379                     case 'u':
22380                         cmd = 'underline';
22381                         break;
22382                     
22383                     case 'v':
22384                         this.cleanUpPaste.defer(100, this);
22385                         return;
22386                         
22387                 }
22388                 if(cmd){
22389                     this.win.focus();
22390                     this.execCmd(cmd);
22391                     this.deferFocus();
22392                     e.preventDefault();
22393                 }
22394                 
22395             }
22396         }
22397     },
22398
22399     // private
22400     fixKeys : function(){ // load time branching for fastest keydown performance
22401         if(Roo.isIE){
22402             return function(e){
22403                 var k = e.getKey(), r;
22404                 if(k == e.TAB){
22405                     e.stopEvent();
22406                     r = this.doc.selection.createRange();
22407                     if(r){
22408                         r.collapse(true);
22409                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22410                         this.deferFocus();
22411                     }
22412                     return;
22413                 }
22414                 
22415                 if(k == e.ENTER){
22416                     r = this.doc.selection.createRange();
22417                     if(r){
22418                         var target = r.parentElement();
22419                         if(!target || target.tagName.toLowerCase() != 'li'){
22420                             e.stopEvent();
22421                             r.pasteHTML('<br />');
22422                             r.collapse(false);
22423                             r.select();
22424                         }
22425                     }
22426                 }
22427                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22428                     this.cleanUpPaste.defer(100, this);
22429                     return;
22430                 }
22431                 
22432                 
22433             };
22434         }else if(Roo.isOpera){
22435             return function(e){
22436                 var k = e.getKey();
22437                 if(k == e.TAB){
22438                     e.stopEvent();
22439                     this.win.focus();
22440                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22441                     this.deferFocus();
22442                 }
22443                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22444                     this.cleanUpPaste.defer(100, this);
22445                     return;
22446                 }
22447                 
22448             };
22449         }else if(Roo.isSafari){
22450             return function(e){
22451                 var k = e.getKey();
22452                 
22453                 if(k == e.TAB){
22454                     e.stopEvent();
22455                     this.execCmd('InsertText','\t');
22456                     this.deferFocus();
22457                     return;
22458                 }
22459                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22460                     this.cleanUpPaste.defer(100, this);
22461                     return;
22462                 }
22463                 
22464              };
22465         }
22466     }(),
22467     
22468     getAllAncestors: function()
22469     {
22470         var p = this.getSelectedNode();
22471         var a = [];
22472         if (!p) {
22473             a.push(p); // push blank onto stack..
22474             p = this.getParentElement();
22475         }
22476         
22477         
22478         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22479             a.push(p);
22480             p = p.parentNode;
22481         }
22482         a.push(this.doc.body);
22483         return a;
22484     },
22485     lastSel : false,
22486     lastSelNode : false,
22487     
22488     
22489     getSelection : function() 
22490     {
22491         this.assignDocWin();
22492         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22493     },
22494     
22495     getSelectedNode: function() 
22496     {
22497         // this may only work on Gecko!!!
22498         
22499         // should we cache this!!!!
22500         
22501         
22502         
22503          
22504         var range = this.createRange(this.getSelection()).cloneRange();
22505         
22506         if (Roo.isIE) {
22507             var parent = range.parentElement();
22508             while (true) {
22509                 var testRange = range.duplicate();
22510                 testRange.moveToElementText(parent);
22511                 if (testRange.inRange(range)) {
22512                     break;
22513                 }
22514                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22515                     break;
22516                 }
22517                 parent = parent.parentElement;
22518             }
22519             return parent;
22520         }
22521         
22522         // is ancestor a text element.
22523         var ac =  range.commonAncestorContainer;
22524         if (ac.nodeType == 3) {
22525             ac = ac.parentNode;
22526         }
22527         
22528         var ar = ac.childNodes;
22529          
22530         var nodes = [];
22531         var other_nodes = [];
22532         var has_other_nodes = false;
22533         for (var i=0;i<ar.length;i++) {
22534             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22535                 continue;
22536             }
22537             // fullly contained node.
22538             
22539             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22540                 nodes.push(ar[i]);
22541                 continue;
22542             }
22543             
22544             // probably selected..
22545             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22546                 other_nodes.push(ar[i]);
22547                 continue;
22548             }
22549             // outer..
22550             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22551                 continue;
22552             }
22553             
22554             
22555             has_other_nodes = true;
22556         }
22557         if (!nodes.length && other_nodes.length) {
22558             nodes= other_nodes;
22559         }
22560         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22561             return false;
22562         }
22563         
22564         return nodes[0];
22565     },
22566     createRange: function(sel)
22567     {
22568         // this has strange effects when using with 
22569         // top toolbar - not sure if it's a great idea.
22570         //this.editor.contentWindow.focus();
22571         if (typeof sel != "undefined") {
22572             try {
22573                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22574             } catch(e) {
22575                 return this.doc.createRange();
22576             }
22577         } else {
22578             return this.doc.createRange();
22579         }
22580     },
22581     getParentElement: function()
22582     {
22583         
22584         this.assignDocWin();
22585         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22586         
22587         var range = this.createRange(sel);
22588          
22589         try {
22590             var p = range.commonAncestorContainer;
22591             while (p.nodeType == 3) { // text node
22592                 p = p.parentNode;
22593             }
22594             return p;
22595         } catch (e) {
22596             return null;
22597         }
22598     
22599     },
22600     /***
22601      *
22602      * Range intersection.. the hard stuff...
22603      *  '-1' = before
22604      *  '0' = hits..
22605      *  '1' = after.
22606      *         [ -- selected range --- ]
22607      *   [fail]                        [fail]
22608      *
22609      *    basically..
22610      *      if end is before start or  hits it. fail.
22611      *      if start is after end or hits it fail.
22612      *
22613      *   if either hits (but other is outside. - then it's not 
22614      *   
22615      *    
22616      **/
22617     
22618     
22619     // @see http://www.thismuchiknow.co.uk/?p=64.
22620     rangeIntersectsNode : function(range, node)
22621     {
22622         var nodeRange = node.ownerDocument.createRange();
22623         try {
22624             nodeRange.selectNode(node);
22625         } catch (e) {
22626             nodeRange.selectNodeContents(node);
22627         }
22628     
22629         var rangeStartRange = range.cloneRange();
22630         rangeStartRange.collapse(true);
22631     
22632         var rangeEndRange = range.cloneRange();
22633         rangeEndRange.collapse(false);
22634     
22635         var nodeStartRange = nodeRange.cloneRange();
22636         nodeStartRange.collapse(true);
22637     
22638         var nodeEndRange = nodeRange.cloneRange();
22639         nodeEndRange.collapse(false);
22640     
22641         return rangeStartRange.compareBoundaryPoints(
22642                  Range.START_TO_START, nodeEndRange) == -1 &&
22643                rangeEndRange.compareBoundaryPoints(
22644                  Range.START_TO_START, nodeStartRange) == 1;
22645         
22646          
22647     },
22648     rangeCompareNode : function(range, node)
22649     {
22650         var nodeRange = node.ownerDocument.createRange();
22651         try {
22652             nodeRange.selectNode(node);
22653         } catch (e) {
22654             nodeRange.selectNodeContents(node);
22655         }
22656         
22657         
22658         range.collapse(true);
22659     
22660         nodeRange.collapse(true);
22661      
22662         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22663         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22664          
22665         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22666         
22667         var nodeIsBefore   =  ss == 1;
22668         var nodeIsAfter    = ee == -1;
22669         
22670         if (nodeIsBefore && nodeIsAfter) {
22671             return 0; // outer
22672         }
22673         if (!nodeIsBefore && nodeIsAfter) {
22674             return 1; //right trailed.
22675         }
22676         
22677         if (nodeIsBefore && !nodeIsAfter) {
22678             return 2;  // left trailed.
22679         }
22680         // fully contined.
22681         return 3;
22682     },
22683
22684     // private? - in a new class?
22685     cleanUpPaste :  function()
22686     {
22687         // cleans up the whole document..
22688         Roo.log('cleanuppaste');
22689         
22690         this.cleanUpChildren(this.doc.body);
22691         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22692         if (clean != this.doc.body.innerHTML) {
22693             this.doc.body.innerHTML = clean;
22694         }
22695         
22696     },
22697     
22698     cleanWordChars : function(input) {// change the chars to hex code
22699         var he = Roo.HtmlEditorCore;
22700         
22701         var output = input;
22702         Roo.each(he.swapCodes, function(sw) { 
22703             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22704             
22705             output = output.replace(swapper, sw[1]);
22706         });
22707         
22708         return output;
22709     },
22710     
22711     
22712     cleanUpChildren : function (n)
22713     {
22714         if (!n.childNodes.length) {
22715             return;
22716         }
22717         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22718            this.cleanUpChild(n.childNodes[i]);
22719         }
22720     },
22721     
22722     
22723         
22724     
22725     cleanUpChild : function (node)
22726     {
22727         var ed = this;
22728         //console.log(node);
22729         if (node.nodeName == "#text") {
22730             // clean up silly Windows -- stuff?
22731             return; 
22732         }
22733         if (node.nodeName == "#comment") {
22734             node.parentNode.removeChild(node);
22735             // clean up silly Windows -- stuff?
22736             return; 
22737         }
22738         var lcname = node.tagName.toLowerCase();
22739         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22740         // whitelist of tags..
22741         
22742         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22743             // remove node.
22744             node.parentNode.removeChild(node);
22745             return;
22746             
22747         }
22748         
22749         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22750         
22751         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22752         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22753         
22754         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22755         //    remove_keep_children = true;
22756         //}
22757         
22758         if (remove_keep_children) {
22759             this.cleanUpChildren(node);
22760             // inserts everything just before this node...
22761             while (node.childNodes.length) {
22762                 var cn = node.childNodes[0];
22763                 node.removeChild(cn);
22764                 node.parentNode.insertBefore(cn, node);
22765             }
22766             node.parentNode.removeChild(node);
22767             return;
22768         }
22769         
22770         if (!node.attributes || !node.attributes.length) {
22771             this.cleanUpChildren(node);
22772             return;
22773         }
22774         
22775         function cleanAttr(n,v)
22776         {
22777             
22778             if (v.match(/^\./) || v.match(/^\//)) {
22779                 return;
22780             }
22781             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22782                 return;
22783             }
22784             if (v.match(/^#/)) {
22785                 return;
22786             }
22787 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22788             node.removeAttribute(n);
22789             
22790         }
22791         
22792         var cwhite = this.cwhite;
22793         var cblack = this.cblack;
22794             
22795         function cleanStyle(n,v)
22796         {
22797             if (v.match(/expression/)) { //XSS?? should we even bother..
22798                 node.removeAttribute(n);
22799                 return;
22800             }
22801             
22802             var parts = v.split(/;/);
22803             var clean = [];
22804             
22805             Roo.each(parts, function(p) {
22806                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22807                 if (!p.length) {
22808                     return true;
22809                 }
22810                 var l = p.split(':').shift().replace(/\s+/g,'');
22811                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22812                 
22813                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22814 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22815                     //node.removeAttribute(n);
22816                     return true;
22817                 }
22818                 //Roo.log()
22819                 // only allow 'c whitelisted system attributes'
22820                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22821 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22822                     //node.removeAttribute(n);
22823                     return true;
22824                 }
22825                 
22826                 
22827                  
22828                 
22829                 clean.push(p);
22830                 return true;
22831             });
22832             if (clean.length) { 
22833                 node.setAttribute(n, clean.join(';'));
22834             } else {
22835                 node.removeAttribute(n);
22836             }
22837             
22838         }
22839         
22840         
22841         for (var i = node.attributes.length-1; i > -1 ; i--) {
22842             var a = node.attributes[i];
22843             //console.log(a);
22844             
22845             if (a.name.toLowerCase().substr(0,2)=='on')  {
22846                 node.removeAttribute(a.name);
22847                 continue;
22848             }
22849             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22850                 node.removeAttribute(a.name);
22851                 continue;
22852             }
22853             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22854                 cleanAttr(a.name,a.value); // fixme..
22855                 continue;
22856             }
22857             if (a.name == 'style') {
22858                 cleanStyle(a.name,a.value);
22859                 continue;
22860             }
22861             /// clean up MS crap..
22862             // tecnically this should be a list of valid class'es..
22863             
22864             
22865             if (a.name == 'class') {
22866                 if (a.value.match(/^Mso/)) {
22867                     node.className = '';
22868                 }
22869                 
22870                 if (a.value.match(/^body$/)) {
22871                     node.className = '';
22872                 }
22873                 continue;
22874             }
22875             
22876             // style cleanup!?
22877             // class cleanup?
22878             
22879         }
22880         
22881         
22882         this.cleanUpChildren(node);
22883         
22884         
22885     },
22886     
22887     /**
22888      * Clean up MS wordisms...
22889      */
22890     cleanWord : function(node)
22891     {
22892         
22893         
22894         if (!node) {
22895             this.cleanWord(this.doc.body);
22896             return;
22897         }
22898         if (node.nodeName == "#text") {
22899             // clean up silly Windows -- stuff?
22900             return; 
22901         }
22902         if (node.nodeName == "#comment") {
22903             node.parentNode.removeChild(node);
22904             // clean up silly Windows -- stuff?
22905             return; 
22906         }
22907         
22908         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22909             node.parentNode.removeChild(node);
22910             return;
22911         }
22912         
22913         // remove - but keep children..
22914         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22915             while (node.childNodes.length) {
22916                 var cn = node.childNodes[0];
22917                 node.removeChild(cn);
22918                 node.parentNode.insertBefore(cn, node);
22919             }
22920             node.parentNode.removeChild(node);
22921             this.iterateChildren(node, this.cleanWord);
22922             return;
22923         }
22924         // clean styles
22925         if (node.className.length) {
22926             
22927             var cn = node.className.split(/\W+/);
22928             var cna = [];
22929             Roo.each(cn, function(cls) {
22930                 if (cls.match(/Mso[a-zA-Z]+/)) {
22931                     return;
22932                 }
22933                 cna.push(cls);
22934             });
22935             node.className = cna.length ? cna.join(' ') : '';
22936             if (!cna.length) {
22937                 node.removeAttribute("class");
22938             }
22939         }
22940         
22941         if (node.hasAttribute("lang")) {
22942             node.removeAttribute("lang");
22943         }
22944         
22945         if (node.hasAttribute("style")) {
22946             
22947             var styles = node.getAttribute("style").split(";");
22948             var nstyle = [];
22949             Roo.each(styles, function(s) {
22950                 if (!s.match(/:/)) {
22951                     return;
22952                 }
22953                 var kv = s.split(":");
22954                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22955                     return;
22956                 }
22957                 // what ever is left... we allow.
22958                 nstyle.push(s);
22959             });
22960             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22961             if (!nstyle.length) {
22962                 node.removeAttribute('style');
22963             }
22964         }
22965         this.iterateChildren(node, this.cleanWord);
22966         
22967         
22968         
22969     },
22970     /**
22971      * iterateChildren of a Node, calling fn each time, using this as the scole..
22972      * @param {DomNode} node node to iterate children of.
22973      * @param {Function} fn method of this class to call on each item.
22974      */
22975     iterateChildren : function(node, fn)
22976     {
22977         if (!node.childNodes.length) {
22978                 return;
22979         }
22980         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22981            fn.call(this, node.childNodes[i])
22982         }
22983     },
22984     
22985     
22986     /**
22987      * cleanTableWidths.
22988      *
22989      * Quite often pasting from word etc.. results in tables with column and widths.
22990      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22991      *
22992      */
22993     cleanTableWidths : function(node)
22994     {
22995          
22996          
22997         if (!node) {
22998             this.cleanTableWidths(this.doc.body);
22999             return;
23000         }
23001         
23002         // ignore list...
23003         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23004             return; 
23005         }
23006         Roo.log(node.tagName);
23007         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23008             this.iterateChildren(node, this.cleanTableWidths);
23009             return;
23010         }
23011         if (node.hasAttribute('width')) {
23012             node.removeAttribute('width');
23013         }
23014         
23015          
23016         if (node.hasAttribute("style")) {
23017             // pretty basic...
23018             
23019             var styles = node.getAttribute("style").split(";");
23020             var nstyle = [];
23021             Roo.each(styles, function(s) {
23022                 if (!s.match(/:/)) {
23023                     return;
23024                 }
23025                 var kv = s.split(":");
23026                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23027                     return;
23028                 }
23029                 // what ever is left... we allow.
23030                 nstyle.push(s);
23031             });
23032             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23033             if (!nstyle.length) {
23034                 node.removeAttribute('style');
23035             }
23036         }
23037         
23038         this.iterateChildren(node, this.cleanTableWidths);
23039         
23040         
23041     },
23042     
23043     
23044     
23045     
23046     domToHTML : function(currentElement, depth, nopadtext) {
23047         
23048         depth = depth || 0;
23049         nopadtext = nopadtext || false;
23050     
23051         if (!currentElement) {
23052             return this.domToHTML(this.doc.body);
23053         }
23054         
23055         //Roo.log(currentElement);
23056         var j;
23057         var allText = false;
23058         var nodeName = currentElement.nodeName;
23059         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23060         
23061         if  (nodeName == '#text') {
23062             
23063             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23064         }
23065         
23066         
23067         var ret = '';
23068         if (nodeName != 'BODY') {
23069              
23070             var i = 0;
23071             // Prints the node tagName, such as <A>, <IMG>, etc
23072             if (tagName) {
23073                 var attr = [];
23074                 for(i = 0; i < currentElement.attributes.length;i++) {
23075                     // quoting?
23076                     var aname = currentElement.attributes.item(i).name;
23077                     if (!currentElement.attributes.item(i).value.length) {
23078                         continue;
23079                     }
23080                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23081                 }
23082                 
23083                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23084             } 
23085             else {
23086                 
23087                 // eack
23088             }
23089         } else {
23090             tagName = false;
23091         }
23092         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23093             return ret;
23094         }
23095         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23096             nopadtext = true;
23097         }
23098         
23099         
23100         // Traverse the tree
23101         i = 0;
23102         var currentElementChild = currentElement.childNodes.item(i);
23103         var allText = true;
23104         var innerHTML  = '';
23105         lastnode = '';
23106         while (currentElementChild) {
23107             // Formatting code (indent the tree so it looks nice on the screen)
23108             var nopad = nopadtext;
23109             if (lastnode == 'SPAN') {
23110                 nopad  = true;
23111             }
23112             // text
23113             if  (currentElementChild.nodeName == '#text') {
23114                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23115                 toadd = nopadtext ? toadd : toadd.trim();
23116                 if (!nopad && toadd.length > 80) {
23117                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23118                 }
23119                 innerHTML  += toadd;
23120                 
23121                 i++;
23122                 currentElementChild = currentElement.childNodes.item(i);
23123                 lastNode = '';
23124                 continue;
23125             }
23126             allText = false;
23127             
23128             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23129                 
23130             // Recursively traverse the tree structure of the child node
23131             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23132             lastnode = currentElementChild.nodeName;
23133             i++;
23134             currentElementChild=currentElement.childNodes.item(i);
23135         }
23136         
23137         ret += innerHTML;
23138         
23139         if (!allText) {
23140                 // The remaining code is mostly for formatting the tree
23141             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23142         }
23143         
23144         
23145         if (tagName) {
23146             ret+= "</"+tagName+">";
23147         }
23148         return ret;
23149         
23150     },
23151         
23152     applyBlacklists : function()
23153     {
23154         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23155         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23156         
23157         this.white = [];
23158         this.black = [];
23159         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23160             if (b.indexOf(tag) > -1) {
23161                 return;
23162             }
23163             this.white.push(tag);
23164             
23165         }, this);
23166         
23167         Roo.each(w, function(tag) {
23168             if (b.indexOf(tag) > -1) {
23169                 return;
23170             }
23171             if (this.white.indexOf(tag) > -1) {
23172                 return;
23173             }
23174             this.white.push(tag);
23175             
23176         }, this);
23177         
23178         
23179         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23180             if (w.indexOf(tag) > -1) {
23181                 return;
23182             }
23183             this.black.push(tag);
23184             
23185         }, this);
23186         
23187         Roo.each(b, function(tag) {
23188             if (w.indexOf(tag) > -1) {
23189                 return;
23190             }
23191             if (this.black.indexOf(tag) > -1) {
23192                 return;
23193             }
23194             this.black.push(tag);
23195             
23196         }, this);
23197         
23198         
23199         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23200         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23201         
23202         this.cwhite = [];
23203         this.cblack = [];
23204         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23205             if (b.indexOf(tag) > -1) {
23206                 return;
23207             }
23208             this.cwhite.push(tag);
23209             
23210         }, this);
23211         
23212         Roo.each(w, function(tag) {
23213             if (b.indexOf(tag) > -1) {
23214                 return;
23215             }
23216             if (this.cwhite.indexOf(tag) > -1) {
23217                 return;
23218             }
23219             this.cwhite.push(tag);
23220             
23221         }, this);
23222         
23223         
23224         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23225             if (w.indexOf(tag) > -1) {
23226                 return;
23227             }
23228             this.cblack.push(tag);
23229             
23230         }, this);
23231         
23232         Roo.each(b, function(tag) {
23233             if (w.indexOf(tag) > -1) {
23234                 return;
23235             }
23236             if (this.cblack.indexOf(tag) > -1) {
23237                 return;
23238             }
23239             this.cblack.push(tag);
23240             
23241         }, this);
23242     },
23243     
23244     setStylesheets : function(stylesheets)
23245     {
23246         if(typeof(stylesheets) == 'string'){
23247             Roo.get(this.iframe.contentDocument.head).createChild({
23248                 tag : 'link',
23249                 rel : 'stylesheet',
23250                 type : 'text/css',
23251                 href : stylesheets
23252             });
23253             
23254             return;
23255         }
23256         var _this = this;
23257      
23258         Roo.each(stylesheets, function(s) {
23259             if(!s.length){
23260                 return;
23261             }
23262             
23263             Roo.get(_this.iframe.contentDocument.head).createChild({
23264                 tag : 'link',
23265                 rel : 'stylesheet',
23266                 type : 'text/css',
23267                 href : s
23268             });
23269         });
23270
23271         
23272     },
23273     
23274     removeStylesheets : function()
23275     {
23276         var _this = this;
23277         
23278         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23279             s.remove();
23280         });
23281     },
23282     
23283     setStyle : function(style)
23284     {
23285         Roo.get(this.iframe.contentDocument.head).createChild({
23286             tag : 'style',
23287             type : 'text/css',
23288             html : style
23289         });
23290
23291         return;
23292     }
23293     
23294     // hide stuff that is not compatible
23295     /**
23296      * @event blur
23297      * @hide
23298      */
23299     /**
23300      * @event change
23301      * @hide
23302      */
23303     /**
23304      * @event focus
23305      * @hide
23306      */
23307     /**
23308      * @event specialkey
23309      * @hide
23310      */
23311     /**
23312      * @cfg {String} fieldClass @hide
23313      */
23314     /**
23315      * @cfg {String} focusClass @hide
23316      */
23317     /**
23318      * @cfg {String} autoCreate @hide
23319      */
23320     /**
23321      * @cfg {String} inputType @hide
23322      */
23323     /**
23324      * @cfg {String} invalidClass @hide
23325      */
23326     /**
23327      * @cfg {String} invalidText @hide
23328      */
23329     /**
23330      * @cfg {String} msgFx @hide
23331      */
23332     /**
23333      * @cfg {String} validateOnBlur @hide
23334      */
23335 });
23336
23337 Roo.HtmlEditorCore.white = [
23338         'area', 'br', 'img', 'input', 'hr', 'wbr',
23339         
23340        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23341        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23342        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23343        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23344        'table',   'ul',         'xmp', 
23345        
23346        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23347       'thead',   'tr', 
23348      
23349       'dir', 'menu', 'ol', 'ul', 'dl',
23350        
23351       'embed',  'object'
23352 ];
23353
23354
23355 Roo.HtmlEditorCore.black = [
23356     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23357         'applet', // 
23358         'base',   'basefont', 'bgsound', 'blink',  'body', 
23359         'frame',  'frameset', 'head',    'html',   'ilayer', 
23360         'iframe', 'layer',  'link',     'meta',    'object',   
23361         'script', 'style' ,'title',  'xml' // clean later..
23362 ];
23363 Roo.HtmlEditorCore.clean = [
23364     'script', 'style', 'title', 'xml'
23365 ];
23366 Roo.HtmlEditorCore.remove = [
23367     'font'
23368 ];
23369 // attributes..
23370
23371 Roo.HtmlEditorCore.ablack = [
23372     'on'
23373 ];
23374     
23375 Roo.HtmlEditorCore.aclean = [ 
23376     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23377 ];
23378
23379 // protocols..
23380 Roo.HtmlEditorCore.pwhite= [
23381         'http',  'https',  'mailto'
23382 ];
23383
23384 // white listed style attributes.
23385 Roo.HtmlEditorCore.cwhite= [
23386       //  'text-align', /// default is to allow most things..
23387       
23388          
23389 //        'font-size'//??
23390 ];
23391
23392 // black listed style attributes.
23393 Roo.HtmlEditorCore.cblack= [
23394       //  'font-size' -- this can be set by the project 
23395 ];
23396
23397
23398 Roo.HtmlEditorCore.swapCodes   =[ 
23399     [    8211, "--" ], 
23400     [    8212, "--" ], 
23401     [    8216,  "'" ],  
23402     [    8217, "'" ],  
23403     [    8220, '"' ],  
23404     [    8221, '"' ],  
23405     [    8226, "*" ],  
23406     [    8230, "..." ]
23407 ]; 
23408
23409     /*
23410  * - LGPL
23411  *
23412  * HtmlEditor
23413  * 
23414  */
23415
23416 /**
23417  * @class Roo.bootstrap.HtmlEditor
23418  * @extends Roo.bootstrap.TextArea
23419  * Bootstrap HtmlEditor class
23420
23421  * @constructor
23422  * Create a new HtmlEditor
23423  * @param {Object} config The config object
23424  */
23425
23426 Roo.bootstrap.HtmlEditor = function(config){
23427     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23428     if (!this.toolbars) {
23429         this.toolbars = [];
23430     }
23431     
23432     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23433     this.addEvents({
23434             /**
23435              * @event initialize
23436              * Fires when the editor is fully initialized (including the iframe)
23437              * @param {HtmlEditor} this
23438              */
23439             initialize: true,
23440             /**
23441              * @event activate
23442              * Fires when the editor is first receives the focus. Any insertion must wait
23443              * until after this event.
23444              * @param {HtmlEditor} this
23445              */
23446             activate: true,
23447              /**
23448              * @event beforesync
23449              * Fires before the textarea is updated with content from the editor iframe. Return false
23450              * to cancel the sync.
23451              * @param {HtmlEditor} this
23452              * @param {String} html
23453              */
23454             beforesync: true,
23455              /**
23456              * @event beforepush
23457              * Fires before the iframe editor is updated with content from the textarea. Return false
23458              * to cancel the push.
23459              * @param {HtmlEditor} this
23460              * @param {String} html
23461              */
23462             beforepush: true,
23463              /**
23464              * @event sync
23465              * Fires when the textarea is updated with content from the editor iframe.
23466              * @param {HtmlEditor} this
23467              * @param {String} html
23468              */
23469             sync: true,
23470              /**
23471              * @event push
23472              * Fires when the iframe editor is updated with content from the textarea.
23473              * @param {HtmlEditor} this
23474              * @param {String} html
23475              */
23476             push: true,
23477              /**
23478              * @event editmodechange
23479              * Fires when the editor switches edit modes
23480              * @param {HtmlEditor} this
23481              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23482              */
23483             editmodechange: true,
23484             /**
23485              * @event editorevent
23486              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23487              * @param {HtmlEditor} this
23488              */
23489             editorevent: true,
23490             /**
23491              * @event firstfocus
23492              * Fires when on first focus - needed by toolbars..
23493              * @param {HtmlEditor} this
23494              */
23495             firstfocus: true,
23496             /**
23497              * @event autosave
23498              * Auto save the htmlEditor value as a file into Events
23499              * @param {HtmlEditor} this
23500              */
23501             autosave: true,
23502             /**
23503              * @event savedpreview
23504              * preview the saved version of htmlEditor
23505              * @param {HtmlEditor} this
23506              */
23507             savedpreview: true
23508         });
23509 };
23510
23511
23512 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23513     
23514     
23515       /**
23516      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23517      */
23518     toolbars : false,
23519     
23520      /**
23521     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23522     */
23523     btns : [],
23524    
23525      /**
23526      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23527      *                        Roo.resizable.
23528      */
23529     resizable : false,
23530      /**
23531      * @cfg {Number} height (in pixels)
23532      */   
23533     height: 300,
23534    /**
23535      * @cfg {Number} width (in pixels)
23536      */   
23537     width: false,
23538     
23539     /**
23540      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23541      * 
23542      */
23543     stylesheets: false,
23544     
23545     // id of frame..
23546     frameId: false,
23547     
23548     // private properties
23549     validationEvent : false,
23550     deferHeight: true,
23551     initialized : false,
23552     activated : false,
23553     
23554     onFocus : Roo.emptyFn,
23555     iframePad:3,
23556     hideMode:'offsets',
23557     
23558     tbContainer : false,
23559     
23560     bodyCls : '',
23561     
23562     toolbarContainer :function() {
23563         return this.wrap.select('.x-html-editor-tb',true).first();
23564     },
23565
23566     /**
23567      * Protected method that will not generally be called directly. It
23568      * is called when the editor creates its toolbar. Override this method if you need to
23569      * add custom toolbar buttons.
23570      * @param {HtmlEditor} editor
23571      */
23572     createToolbar : function(){
23573         Roo.log('renewing');
23574         Roo.log("create toolbars");
23575         
23576         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23577         this.toolbars[0].render(this.toolbarContainer());
23578         
23579         return;
23580         
23581 //        if (!editor.toolbars || !editor.toolbars.length) {
23582 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23583 //        }
23584 //        
23585 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23586 //            editor.toolbars[i] = Roo.factory(
23587 //                    typeof(editor.toolbars[i]) == 'string' ?
23588 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23589 //                Roo.bootstrap.HtmlEditor);
23590 //            editor.toolbars[i].init(editor);
23591 //        }
23592     },
23593
23594      
23595     // private
23596     onRender : function(ct, position)
23597     {
23598        // Roo.log("Call onRender: " + this.xtype);
23599         var _t = this;
23600         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23601       
23602         this.wrap = this.inputEl().wrap({
23603             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23604         });
23605         
23606         this.editorcore.onRender(ct, position);
23607          
23608         if (this.resizable) {
23609             this.resizeEl = new Roo.Resizable(this.wrap, {
23610                 pinned : true,
23611                 wrap: true,
23612                 dynamic : true,
23613                 minHeight : this.height,
23614                 height: this.height,
23615                 handles : this.resizable,
23616                 width: this.width,
23617                 listeners : {
23618                     resize : function(r, w, h) {
23619                         _t.onResize(w,h); // -something
23620                     }
23621                 }
23622             });
23623             
23624         }
23625         this.createToolbar(this);
23626        
23627         
23628         if(!this.width && this.resizable){
23629             this.setSize(this.wrap.getSize());
23630         }
23631         if (this.resizeEl) {
23632             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23633             // should trigger onReize..
23634         }
23635         
23636     },
23637
23638     // private
23639     onResize : function(w, h)
23640     {
23641         Roo.log('resize: ' +w + ',' + h );
23642         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23643         var ew = false;
23644         var eh = false;
23645         
23646         if(this.inputEl() ){
23647             if(typeof w == 'number'){
23648                 var aw = w - this.wrap.getFrameWidth('lr');
23649                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23650                 ew = aw;
23651             }
23652             if(typeof h == 'number'){
23653                  var tbh = -11;  // fixme it needs to tool bar size!
23654                 for (var i =0; i < this.toolbars.length;i++) {
23655                     // fixme - ask toolbars for heights?
23656                     tbh += this.toolbars[i].el.getHeight();
23657                     //if (this.toolbars[i].footer) {
23658                     //    tbh += this.toolbars[i].footer.el.getHeight();
23659                     //}
23660                 }
23661               
23662                 
23663                 
23664                 
23665                 
23666                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23667                 ah -= 5; // knock a few pixes off for look..
23668                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23669                 var eh = ah;
23670             }
23671         }
23672         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23673         this.editorcore.onResize(ew,eh);
23674         
23675     },
23676
23677     /**
23678      * Toggles the editor between standard and source edit mode.
23679      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23680      */
23681     toggleSourceEdit : function(sourceEditMode)
23682     {
23683         this.editorcore.toggleSourceEdit(sourceEditMode);
23684         
23685         if(this.editorcore.sourceEditMode){
23686             Roo.log('editor - showing textarea');
23687             
23688 //            Roo.log('in');
23689 //            Roo.log(this.syncValue());
23690             this.syncValue();
23691             this.inputEl().removeClass(['hide', 'x-hidden']);
23692             this.inputEl().dom.removeAttribute('tabIndex');
23693             this.inputEl().focus();
23694         }else{
23695             Roo.log('editor - hiding textarea');
23696 //            Roo.log('out')
23697 //            Roo.log(this.pushValue()); 
23698             this.pushValue();
23699             
23700             this.inputEl().addClass(['hide', 'x-hidden']);
23701             this.inputEl().dom.setAttribute('tabIndex', -1);
23702             //this.deferFocus();
23703         }
23704          
23705         if(this.resizable){
23706             this.setSize(this.wrap.getSize());
23707         }
23708         
23709         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23710     },
23711  
23712     // private (for BoxComponent)
23713     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23714
23715     // private (for BoxComponent)
23716     getResizeEl : function(){
23717         return this.wrap;
23718     },
23719
23720     // private (for BoxComponent)
23721     getPositionEl : function(){
23722         return this.wrap;
23723     },
23724
23725     // private
23726     initEvents : function(){
23727         this.originalValue = this.getValue();
23728     },
23729
23730 //    /**
23731 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23732 //     * @method
23733 //     */
23734 //    markInvalid : Roo.emptyFn,
23735 //    /**
23736 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23737 //     * @method
23738 //     */
23739 //    clearInvalid : Roo.emptyFn,
23740
23741     setValue : function(v){
23742         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23743         this.editorcore.pushValue();
23744     },
23745
23746      
23747     // private
23748     deferFocus : function(){
23749         this.focus.defer(10, this);
23750     },
23751
23752     // doc'ed in Field
23753     focus : function(){
23754         this.editorcore.focus();
23755         
23756     },
23757       
23758
23759     // private
23760     onDestroy : function(){
23761         
23762         
23763         
23764         if(this.rendered){
23765             
23766             for (var i =0; i < this.toolbars.length;i++) {
23767                 // fixme - ask toolbars for heights?
23768                 this.toolbars[i].onDestroy();
23769             }
23770             
23771             this.wrap.dom.innerHTML = '';
23772             this.wrap.remove();
23773         }
23774     },
23775
23776     // private
23777     onFirstFocus : function(){
23778         //Roo.log("onFirstFocus");
23779         this.editorcore.onFirstFocus();
23780          for (var i =0; i < this.toolbars.length;i++) {
23781             this.toolbars[i].onFirstFocus();
23782         }
23783         
23784     },
23785     
23786     // private
23787     syncValue : function()
23788     {   
23789         this.editorcore.syncValue();
23790     },
23791     
23792     pushValue : function()
23793     {   
23794         this.editorcore.pushValue();
23795     }
23796      
23797     
23798     // hide stuff that is not compatible
23799     /**
23800      * @event blur
23801      * @hide
23802      */
23803     /**
23804      * @event change
23805      * @hide
23806      */
23807     /**
23808      * @event focus
23809      * @hide
23810      */
23811     /**
23812      * @event specialkey
23813      * @hide
23814      */
23815     /**
23816      * @cfg {String} fieldClass @hide
23817      */
23818     /**
23819      * @cfg {String} focusClass @hide
23820      */
23821     /**
23822      * @cfg {String} autoCreate @hide
23823      */
23824     /**
23825      * @cfg {String} inputType @hide
23826      */
23827     /**
23828      * @cfg {String} invalidClass @hide
23829      */
23830     /**
23831      * @cfg {String} invalidText @hide
23832      */
23833     /**
23834      * @cfg {String} msgFx @hide
23835      */
23836     /**
23837      * @cfg {String} validateOnBlur @hide
23838      */
23839 });
23840  
23841     
23842    
23843    
23844    
23845       
23846 Roo.namespace('Roo.bootstrap.htmleditor');
23847 /**
23848  * @class Roo.bootstrap.HtmlEditorToolbar1
23849  * Basic Toolbar
23850  * 
23851  * Usage:
23852  *
23853  new Roo.bootstrap.HtmlEditor({
23854     ....
23855     toolbars : [
23856         new Roo.bootstrap.HtmlEditorToolbar1({
23857             disable : { fonts: 1 , format: 1, ..., ... , ...],
23858             btns : [ .... ]
23859         })
23860     }
23861      
23862  * 
23863  * @cfg {Object} disable List of elements to disable..
23864  * @cfg {Array} btns List of additional buttons.
23865  * 
23866  * 
23867  * NEEDS Extra CSS? 
23868  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23869  */
23870  
23871 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23872 {
23873     
23874     Roo.apply(this, config);
23875     
23876     // default disabled, based on 'good practice'..
23877     this.disable = this.disable || {};
23878     Roo.applyIf(this.disable, {
23879         fontSize : true,
23880         colors : true,
23881         specialElements : true
23882     });
23883     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23884     
23885     this.editor = config.editor;
23886     this.editorcore = config.editor.editorcore;
23887     
23888     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23889     
23890     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23891     // dont call parent... till later.
23892 }
23893 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23894      
23895     bar : true,
23896     
23897     editor : false,
23898     editorcore : false,
23899     
23900     
23901     formats : [
23902         "p" ,  
23903         "h1","h2","h3","h4","h5","h6", 
23904         "pre", "code", 
23905         "abbr", "acronym", "address", "cite", "samp", "var",
23906         'div','span'
23907     ],
23908     
23909     onRender : function(ct, position)
23910     {
23911        // Roo.log("Call onRender: " + this.xtype);
23912         
23913        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23914        Roo.log(this.el);
23915        this.el.dom.style.marginBottom = '0';
23916        var _this = this;
23917        var editorcore = this.editorcore;
23918        var editor= this.editor;
23919        
23920        var children = [];
23921        var btn = function(id,cmd , toggle, handler, html){
23922        
23923             var  event = toggle ? 'toggle' : 'click';
23924        
23925             var a = {
23926                 size : 'sm',
23927                 xtype: 'Button',
23928                 xns: Roo.bootstrap,
23929                 glyphicon : id,
23930                 cmd : id || cmd,
23931                 enableToggle:toggle !== false,
23932                 html : html || '',
23933                 pressed : toggle ? false : null,
23934                 listeners : {}
23935             };
23936             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23937                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23938             };
23939             children.push(a);
23940             return a;
23941        }
23942        
23943     //    var cb_box = function...
23944         
23945         var style = {
23946                 xtype: 'Button',
23947                 size : 'sm',
23948                 xns: Roo.bootstrap,
23949                 glyphicon : 'font',
23950                 //html : 'submit'
23951                 menu : {
23952                     xtype: 'Menu',
23953                     xns: Roo.bootstrap,
23954                     items:  []
23955                 }
23956         };
23957         Roo.each(this.formats, function(f) {
23958             style.menu.items.push({
23959                 xtype :'MenuItem',
23960                 xns: Roo.bootstrap,
23961                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23962                 tagname : f,
23963                 listeners : {
23964                     click : function()
23965                     {
23966                         editorcore.insertTag(this.tagname);
23967                         editor.focus();
23968                     }
23969                 }
23970                 
23971             });
23972         });
23973         children.push(style);   
23974         
23975         btn('bold',false,true);
23976         btn('italic',false,true);
23977         btn('align-left', 'justifyleft',true);
23978         btn('align-center', 'justifycenter',true);
23979         btn('align-right' , 'justifyright',true);
23980         btn('link', false, false, function(btn) {
23981             //Roo.log("create link?");
23982             var url = prompt(this.createLinkText, this.defaultLinkValue);
23983             if(url && url != 'http:/'+'/'){
23984                 this.editorcore.relayCmd('createlink', url);
23985             }
23986         }),
23987         btn('list','insertunorderedlist',true);
23988         btn('pencil', false,true, function(btn){
23989                 Roo.log(this);
23990                 this.toggleSourceEdit(btn.pressed);
23991         });
23992         
23993         if (this.editor.btns.length > 0) {
23994             for (var i = 0; i<this.editor.btns.length; i++) {
23995                 children.push(this.editor.btns[i]);
23996             }
23997         }
23998         
23999         /*
24000         var cog = {
24001                 xtype: 'Button',
24002                 size : 'sm',
24003                 xns: Roo.bootstrap,
24004                 glyphicon : 'cog',
24005                 //html : 'submit'
24006                 menu : {
24007                     xtype: 'Menu',
24008                     xns: Roo.bootstrap,
24009                     items:  []
24010                 }
24011         };
24012         
24013         cog.menu.items.push({
24014             xtype :'MenuItem',
24015             xns: Roo.bootstrap,
24016             html : Clean styles,
24017             tagname : f,
24018             listeners : {
24019                 click : function()
24020                 {
24021                     editorcore.insertTag(this.tagname);
24022                     editor.focus();
24023                 }
24024             }
24025             
24026         });
24027        */
24028         
24029          
24030        this.xtype = 'NavSimplebar';
24031         
24032         for(var i=0;i< children.length;i++) {
24033             
24034             this.buttons.add(this.addxtypeChild(children[i]));
24035             
24036         }
24037         
24038         editor.on('editorevent', this.updateToolbar, this);
24039     },
24040     onBtnClick : function(id)
24041     {
24042        this.editorcore.relayCmd(id);
24043        this.editorcore.focus();
24044     },
24045     
24046     /**
24047      * Protected method that will not generally be called directly. It triggers
24048      * a toolbar update by reading the markup state of the current selection in the editor.
24049      */
24050     updateToolbar: function(){
24051
24052         if(!this.editorcore.activated){
24053             this.editor.onFirstFocus(); // is this neeed?
24054             return;
24055         }
24056
24057         var btns = this.buttons; 
24058         var doc = this.editorcore.doc;
24059         btns.get('bold').setActive(doc.queryCommandState('bold'));
24060         btns.get('italic').setActive(doc.queryCommandState('italic'));
24061         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24062         
24063         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24064         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24065         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24066         
24067         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24068         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24069          /*
24070         
24071         var ans = this.editorcore.getAllAncestors();
24072         if (this.formatCombo) {
24073             
24074             
24075             var store = this.formatCombo.store;
24076             this.formatCombo.setValue("");
24077             for (var i =0; i < ans.length;i++) {
24078                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24079                     // select it..
24080                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24081                     break;
24082                 }
24083             }
24084         }
24085         
24086         
24087         
24088         // hides menus... - so this cant be on a menu...
24089         Roo.bootstrap.MenuMgr.hideAll();
24090         */
24091         Roo.bootstrap.MenuMgr.hideAll();
24092         //this.editorsyncValue();
24093     },
24094     onFirstFocus: function() {
24095         this.buttons.each(function(item){
24096            item.enable();
24097         });
24098     },
24099     toggleSourceEdit : function(sourceEditMode){
24100         
24101           
24102         if(sourceEditMode){
24103             Roo.log("disabling buttons");
24104            this.buttons.each( function(item){
24105                 if(item.cmd != 'pencil'){
24106                     item.disable();
24107                 }
24108             });
24109           
24110         }else{
24111             Roo.log("enabling buttons");
24112             if(this.editorcore.initialized){
24113                 this.buttons.each( function(item){
24114                     item.enable();
24115                 });
24116             }
24117             
24118         }
24119         Roo.log("calling toggole on editor");
24120         // tell the editor that it's been pressed..
24121         this.editor.toggleSourceEdit(sourceEditMode);
24122        
24123     }
24124 });
24125
24126
24127
24128
24129
24130 /**
24131  * @class Roo.bootstrap.Table.AbstractSelectionModel
24132  * @extends Roo.util.Observable
24133  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24134  * implemented by descendant classes.  This class should not be directly instantiated.
24135  * @constructor
24136  */
24137 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24138     this.locked = false;
24139     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24140 };
24141
24142
24143 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24144     /** @ignore Called by the grid automatically. Do not call directly. */
24145     init : function(grid){
24146         this.grid = grid;
24147         this.initEvents();
24148     },
24149
24150     /**
24151      * Locks the selections.
24152      */
24153     lock : function(){
24154         this.locked = true;
24155     },
24156
24157     /**
24158      * Unlocks the selections.
24159      */
24160     unlock : function(){
24161         this.locked = false;
24162     },
24163
24164     /**
24165      * Returns true if the selections are locked.
24166      * @return {Boolean}
24167      */
24168     isLocked : function(){
24169         return this.locked;
24170     }
24171 });
24172 /**
24173  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24174  * @class Roo.bootstrap.Table.RowSelectionModel
24175  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24176  * It supports multiple selections and keyboard selection/navigation. 
24177  * @constructor
24178  * @param {Object} config
24179  */
24180
24181 Roo.bootstrap.Table.RowSelectionModel = function(config){
24182     Roo.apply(this, config);
24183     this.selections = new Roo.util.MixedCollection(false, function(o){
24184         return o.id;
24185     });
24186
24187     this.last = false;
24188     this.lastActive = false;
24189
24190     this.addEvents({
24191         /**
24192              * @event selectionchange
24193              * Fires when the selection changes
24194              * @param {SelectionModel} this
24195              */
24196             "selectionchange" : true,
24197         /**
24198              * @event afterselectionchange
24199              * Fires after the selection changes (eg. by key press or clicking)
24200              * @param {SelectionModel} this
24201              */
24202             "afterselectionchange" : true,
24203         /**
24204              * @event beforerowselect
24205              * Fires when a row is selected being selected, return false to cancel.
24206              * @param {SelectionModel} this
24207              * @param {Number} rowIndex The selected index
24208              * @param {Boolean} keepExisting False if other selections will be cleared
24209              */
24210             "beforerowselect" : true,
24211         /**
24212              * @event rowselect
24213              * Fires when a row is selected.
24214              * @param {SelectionModel} this
24215              * @param {Number} rowIndex The selected index
24216              * @param {Roo.data.Record} r The record
24217              */
24218             "rowselect" : true,
24219         /**
24220              * @event rowdeselect
24221              * Fires when a row is deselected.
24222              * @param {SelectionModel} this
24223              * @param {Number} rowIndex The selected index
24224              */
24225         "rowdeselect" : true
24226     });
24227     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24228     this.locked = false;
24229  };
24230
24231 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24232     /**
24233      * @cfg {Boolean} singleSelect
24234      * True to allow selection of only one row at a time (defaults to false)
24235      */
24236     singleSelect : false,
24237
24238     // private
24239     initEvents : function()
24240     {
24241
24242         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24243         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24244         //}else{ // allow click to work like normal
24245          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24246         //}
24247         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24248         this.grid.on("rowclick", this.handleMouseDown, this);
24249         
24250         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24251             "up" : function(e){
24252                 if(!e.shiftKey){
24253                     this.selectPrevious(e.shiftKey);
24254                 }else if(this.last !== false && this.lastActive !== false){
24255                     var last = this.last;
24256                     this.selectRange(this.last,  this.lastActive-1);
24257                     this.grid.getView().focusRow(this.lastActive);
24258                     if(last !== false){
24259                         this.last = last;
24260                     }
24261                 }else{
24262                     this.selectFirstRow();
24263                 }
24264                 this.fireEvent("afterselectionchange", this);
24265             },
24266             "down" : function(e){
24267                 if(!e.shiftKey){
24268                     this.selectNext(e.shiftKey);
24269                 }else if(this.last !== false && this.lastActive !== false){
24270                     var last = this.last;
24271                     this.selectRange(this.last,  this.lastActive+1);
24272                     this.grid.getView().focusRow(this.lastActive);
24273                     if(last !== false){
24274                         this.last = last;
24275                     }
24276                 }else{
24277                     this.selectFirstRow();
24278                 }
24279                 this.fireEvent("afterselectionchange", this);
24280             },
24281             scope: this
24282         });
24283         this.grid.store.on('load', function(){
24284             this.selections.clear();
24285         },this);
24286         /*
24287         var view = this.grid.view;
24288         view.on("refresh", this.onRefresh, this);
24289         view.on("rowupdated", this.onRowUpdated, this);
24290         view.on("rowremoved", this.onRemove, this);
24291         */
24292     },
24293
24294     // private
24295     onRefresh : function()
24296     {
24297         var ds = this.grid.store, i, v = this.grid.view;
24298         var s = this.selections;
24299         s.each(function(r){
24300             if((i = ds.indexOfId(r.id)) != -1){
24301                 v.onRowSelect(i);
24302             }else{
24303                 s.remove(r);
24304             }
24305         });
24306     },
24307
24308     // private
24309     onRemove : function(v, index, r){
24310         this.selections.remove(r);
24311     },
24312
24313     // private
24314     onRowUpdated : function(v, index, r){
24315         if(this.isSelected(r)){
24316             v.onRowSelect(index);
24317         }
24318     },
24319
24320     /**
24321      * Select records.
24322      * @param {Array} records The records to select
24323      * @param {Boolean} keepExisting (optional) True to keep existing selections
24324      */
24325     selectRecords : function(records, keepExisting)
24326     {
24327         if(!keepExisting){
24328             this.clearSelections();
24329         }
24330             var ds = this.grid.store;
24331         for(var i = 0, len = records.length; i < len; i++){
24332             this.selectRow(ds.indexOf(records[i]), true);
24333         }
24334     },
24335
24336     /**
24337      * Gets the number of selected rows.
24338      * @return {Number}
24339      */
24340     getCount : function(){
24341         return this.selections.length;
24342     },
24343
24344     /**
24345      * Selects the first row in the grid.
24346      */
24347     selectFirstRow : function(){
24348         this.selectRow(0);
24349     },
24350
24351     /**
24352      * Select the last row.
24353      * @param {Boolean} keepExisting (optional) True to keep existing selections
24354      */
24355     selectLastRow : function(keepExisting){
24356         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24357         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24358     },
24359
24360     /**
24361      * Selects the row immediately following the last selected row.
24362      * @param {Boolean} keepExisting (optional) True to keep existing selections
24363      */
24364     selectNext : function(keepExisting)
24365     {
24366             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24367             this.selectRow(this.last+1, keepExisting);
24368             this.grid.getView().focusRow(this.last);
24369         }
24370     },
24371
24372     /**
24373      * Selects the row that precedes the last selected row.
24374      * @param {Boolean} keepExisting (optional) True to keep existing selections
24375      */
24376     selectPrevious : function(keepExisting){
24377         if(this.last){
24378             this.selectRow(this.last-1, keepExisting);
24379             this.grid.getView().focusRow(this.last);
24380         }
24381     },
24382
24383     /**
24384      * Returns the selected records
24385      * @return {Array} Array of selected records
24386      */
24387     getSelections : function(){
24388         return [].concat(this.selections.items);
24389     },
24390
24391     /**
24392      * Returns the first selected record.
24393      * @return {Record}
24394      */
24395     getSelected : function(){
24396         return this.selections.itemAt(0);
24397     },
24398
24399
24400     /**
24401      * Clears all selections.
24402      */
24403     clearSelections : function(fast)
24404     {
24405         if(this.locked) {
24406             return;
24407         }
24408         if(fast !== true){
24409                 var ds = this.grid.store;
24410             var s = this.selections;
24411             s.each(function(r){
24412                 this.deselectRow(ds.indexOfId(r.id));
24413             }, this);
24414             s.clear();
24415         }else{
24416             this.selections.clear();
24417         }
24418         this.last = false;
24419     },
24420
24421
24422     /**
24423      * Selects all rows.
24424      */
24425     selectAll : function(){
24426         if(this.locked) {
24427             return;
24428         }
24429         this.selections.clear();
24430         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24431             this.selectRow(i, true);
24432         }
24433     },
24434
24435     /**
24436      * Returns True if there is a selection.
24437      * @return {Boolean}
24438      */
24439     hasSelection : function(){
24440         return this.selections.length > 0;
24441     },
24442
24443     /**
24444      * Returns True if the specified row is selected.
24445      * @param {Number/Record} record The record or index of the record to check
24446      * @return {Boolean}
24447      */
24448     isSelected : function(index){
24449             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24450         return (r && this.selections.key(r.id) ? true : false);
24451     },
24452
24453     /**
24454      * Returns True if the specified record id is selected.
24455      * @param {String} id The id of record to check
24456      * @return {Boolean}
24457      */
24458     isIdSelected : function(id){
24459         return (this.selections.key(id) ? true : false);
24460     },
24461
24462
24463     // private
24464     handleMouseDBClick : function(e, t){
24465         
24466     },
24467     // private
24468     handleMouseDown : function(e, t)
24469     {
24470             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24471         if(this.isLocked() || rowIndex < 0 ){
24472             return;
24473         };
24474         if(e.shiftKey && this.last !== false){
24475             var last = this.last;
24476             this.selectRange(last, rowIndex, e.ctrlKey);
24477             this.last = last; // reset the last
24478             t.focus();
24479     
24480         }else{
24481             var isSelected = this.isSelected(rowIndex);
24482             //Roo.log("select row:" + rowIndex);
24483             if(isSelected){
24484                 this.deselectRow(rowIndex);
24485             } else {
24486                         this.selectRow(rowIndex, true);
24487             }
24488     
24489             /*
24490                 if(e.button !== 0 && isSelected){
24491                 alert('rowIndex 2: ' + rowIndex);
24492                     view.focusRow(rowIndex);
24493                 }else if(e.ctrlKey && isSelected){
24494                     this.deselectRow(rowIndex);
24495                 }else if(!isSelected){
24496                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24497                     view.focusRow(rowIndex);
24498                 }
24499             */
24500         }
24501         this.fireEvent("afterselectionchange", this);
24502     },
24503     // private
24504     handleDragableRowClick :  function(grid, rowIndex, e) 
24505     {
24506         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24507             this.selectRow(rowIndex, false);
24508             grid.view.focusRow(rowIndex);
24509              this.fireEvent("afterselectionchange", this);
24510         }
24511     },
24512     
24513     /**
24514      * Selects multiple rows.
24515      * @param {Array} rows Array of the indexes of the row to select
24516      * @param {Boolean} keepExisting (optional) True to keep existing selections
24517      */
24518     selectRows : function(rows, keepExisting){
24519         if(!keepExisting){
24520             this.clearSelections();
24521         }
24522         for(var i = 0, len = rows.length; i < len; i++){
24523             this.selectRow(rows[i], true);
24524         }
24525     },
24526
24527     /**
24528      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24529      * @param {Number} startRow The index of the first row in the range
24530      * @param {Number} endRow The index of the last row in the range
24531      * @param {Boolean} keepExisting (optional) True to retain existing selections
24532      */
24533     selectRange : function(startRow, endRow, keepExisting){
24534         if(this.locked) {
24535             return;
24536         }
24537         if(!keepExisting){
24538             this.clearSelections();
24539         }
24540         if(startRow <= endRow){
24541             for(var i = startRow; i <= endRow; i++){
24542                 this.selectRow(i, true);
24543             }
24544         }else{
24545             for(var i = startRow; i >= endRow; i--){
24546                 this.selectRow(i, true);
24547             }
24548         }
24549     },
24550
24551     /**
24552      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24553      * @param {Number} startRow The index of the first row in the range
24554      * @param {Number} endRow The index of the last row in the range
24555      */
24556     deselectRange : function(startRow, endRow, preventViewNotify){
24557         if(this.locked) {
24558             return;
24559         }
24560         for(var i = startRow; i <= endRow; i++){
24561             this.deselectRow(i, preventViewNotify);
24562         }
24563     },
24564
24565     /**
24566      * Selects a row.
24567      * @param {Number} row The index of the row to select
24568      * @param {Boolean} keepExisting (optional) True to keep existing selections
24569      */
24570     selectRow : function(index, keepExisting, preventViewNotify)
24571     {
24572             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24573             return;
24574         }
24575         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24576             if(!keepExisting || this.singleSelect){
24577                 this.clearSelections();
24578             }
24579             
24580             var r = this.grid.store.getAt(index);
24581             //console.log('selectRow - record id :' + r.id);
24582             
24583             this.selections.add(r);
24584             this.last = this.lastActive = index;
24585             if(!preventViewNotify){
24586                 var proxy = new Roo.Element(
24587                                 this.grid.getRowDom(index)
24588                 );
24589                 proxy.addClass('bg-info info');
24590             }
24591             this.fireEvent("rowselect", this, index, r);
24592             this.fireEvent("selectionchange", this);
24593         }
24594     },
24595
24596     /**
24597      * Deselects a row.
24598      * @param {Number} row The index of the row to deselect
24599      */
24600     deselectRow : function(index, preventViewNotify)
24601     {
24602         if(this.locked) {
24603             return;
24604         }
24605         if(this.last == index){
24606             this.last = false;
24607         }
24608         if(this.lastActive == index){
24609             this.lastActive = false;
24610         }
24611         
24612         var r = this.grid.store.getAt(index);
24613         if (!r) {
24614             return;
24615         }
24616         
24617         this.selections.remove(r);
24618         //.console.log('deselectRow - record id :' + r.id);
24619         if(!preventViewNotify){
24620         
24621             var proxy = new Roo.Element(
24622                 this.grid.getRowDom(index)
24623             );
24624             proxy.removeClass('bg-info info');
24625         }
24626         this.fireEvent("rowdeselect", this, index);
24627         this.fireEvent("selectionchange", this);
24628     },
24629
24630     // private
24631     restoreLast : function(){
24632         if(this._last){
24633             this.last = this._last;
24634         }
24635     },
24636
24637     // private
24638     acceptsNav : function(row, col, cm){
24639         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24640     },
24641
24642     // private
24643     onEditorKey : function(field, e){
24644         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24645         if(k == e.TAB){
24646             e.stopEvent();
24647             ed.completeEdit();
24648             if(e.shiftKey){
24649                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24650             }else{
24651                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24652             }
24653         }else if(k == e.ENTER && !e.ctrlKey){
24654             e.stopEvent();
24655             ed.completeEdit();
24656             if(e.shiftKey){
24657                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24658             }else{
24659                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24660             }
24661         }else if(k == e.ESC){
24662             ed.cancelEdit();
24663         }
24664         if(newCell){
24665             g.startEditing(newCell[0], newCell[1]);
24666         }
24667     }
24668 });
24669 /*
24670  * Based on:
24671  * Ext JS Library 1.1.1
24672  * Copyright(c) 2006-2007, Ext JS, LLC.
24673  *
24674  * Originally Released Under LGPL - original licence link has changed is not relivant.
24675  *
24676  * Fork - LGPL
24677  * <script type="text/javascript">
24678  */
24679  
24680 /**
24681  * @class Roo.bootstrap.PagingToolbar
24682  * @extends Roo.bootstrap.NavSimplebar
24683  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24684  * @constructor
24685  * Create a new PagingToolbar
24686  * @param {Object} config The config object
24687  * @param {Roo.data.Store} store
24688  */
24689 Roo.bootstrap.PagingToolbar = function(config)
24690 {
24691     // old args format still supported... - xtype is prefered..
24692         // created from xtype...
24693     
24694     this.ds = config.dataSource;
24695     
24696     if (config.store && !this.ds) {
24697         this.store= Roo.factory(config.store, Roo.data);
24698         this.ds = this.store;
24699         this.ds.xmodule = this.xmodule || false;
24700     }
24701     
24702     this.toolbarItems = [];
24703     if (config.items) {
24704         this.toolbarItems = config.items;
24705     }
24706     
24707     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24708     
24709     this.cursor = 0;
24710     
24711     if (this.ds) { 
24712         this.bind(this.ds);
24713     }
24714     
24715     if (Roo.bootstrap.version == 4) {
24716         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24717     } else {
24718         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24719     }
24720     
24721 };
24722
24723 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24724     /**
24725      * @cfg {Roo.data.Store} dataSource
24726      * The underlying data store providing the paged data
24727      */
24728     /**
24729      * @cfg {String/HTMLElement/Element} container
24730      * container The id or element that will contain the toolbar
24731      */
24732     /**
24733      * @cfg {Boolean} displayInfo
24734      * True to display the displayMsg (defaults to false)
24735      */
24736     /**
24737      * @cfg {Number} pageSize
24738      * The number of records to display per page (defaults to 20)
24739      */
24740     pageSize: 20,
24741     /**
24742      * @cfg {String} displayMsg
24743      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24744      */
24745     displayMsg : 'Displaying {0} - {1} of {2}',
24746     /**
24747      * @cfg {String} emptyMsg
24748      * The message to display when no records are found (defaults to "No data to display")
24749      */
24750     emptyMsg : 'No data to display',
24751     /**
24752      * Customizable piece of the default paging text (defaults to "Page")
24753      * @type String
24754      */
24755     beforePageText : "Page",
24756     /**
24757      * Customizable piece of the default paging text (defaults to "of %0")
24758      * @type String
24759      */
24760     afterPageText : "of {0}",
24761     /**
24762      * Customizable piece of the default paging text (defaults to "First Page")
24763      * @type String
24764      */
24765     firstText : "First Page",
24766     /**
24767      * Customizable piece of the default paging text (defaults to "Previous Page")
24768      * @type String
24769      */
24770     prevText : "Previous Page",
24771     /**
24772      * Customizable piece of the default paging text (defaults to "Next Page")
24773      * @type String
24774      */
24775     nextText : "Next Page",
24776     /**
24777      * Customizable piece of the default paging text (defaults to "Last Page")
24778      * @type String
24779      */
24780     lastText : "Last Page",
24781     /**
24782      * Customizable piece of the default paging text (defaults to "Refresh")
24783      * @type String
24784      */
24785     refreshText : "Refresh",
24786
24787     buttons : false,
24788     // private
24789     onRender : function(ct, position) 
24790     {
24791         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24792         this.navgroup.parentId = this.id;
24793         this.navgroup.onRender(this.el, null);
24794         // add the buttons to the navgroup
24795         
24796         if(this.displayInfo){
24797             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24798             this.displayEl = this.el.select('.x-paging-info', true).first();
24799 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24800 //            this.displayEl = navel.el.select('span',true).first();
24801         }
24802         
24803         var _this = this;
24804         
24805         if(this.buttons){
24806             Roo.each(_this.buttons, function(e){ // this might need to use render????
24807                Roo.factory(e).render(_this.el);
24808             });
24809         }
24810             
24811         Roo.each(_this.toolbarItems, function(e) {
24812             _this.navgroup.addItem(e);
24813         });
24814         
24815         
24816         this.first = this.navgroup.addItem({
24817             tooltip: this.firstText,
24818             cls: "prev btn-outline-secondary",
24819             html : ' <i class="fa fa-step-backward"></i>',
24820             disabled: true,
24821             preventDefault: true,
24822             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24823         });
24824         
24825         this.prev =  this.navgroup.addItem({
24826             tooltip: this.prevText,
24827             cls: "prev btn-outline-secondary",
24828             html : ' <i class="fa fa-backward"></i>',
24829             disabled: true,
24830             preventDefault: true,
24831             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24832         });
24833     //this.addSeparator();
24834         
24835         
24836         var field = this.navgroup.addItem( {
24837             tagtype : 'span',
24838             cls : 'x-paging-position  btn-outline-secondary',
24839              disabled: true,
24840             html : this.beforePageText  +
24841                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24842                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24843          } ); //?? escaped?
24844         
24845         this.field = field.el.select('input', true).first();
24846         this.field.on("keydown", this.onPagingKeydown, this);
24847         this.field.on("focus", function(){this.dom.select();});
24848     
24849     
24850         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24851         //this.field.setHeight(18);
24852         //this.addSeparator();
24853         this.next = this.navgroup.addItem({
24854             tooltip: this.nextText,
24855             cls: "next btn-outline-secondary",
24856             html : ' <i class="fa fa-forward"></i>',
24857             disabled: true,
24858             preventDefault: true,
24859             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24860         });
24861         this.last = this.navgroup.addItem({
24862             tooltip: this.lastText,
24863             html : ' <i class="fa fa-step-forward"></i>',
24864             cls: "next btn-outline-secondary",
24865             disabled: true,
24866             preventDefault: true,
24867             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24868         });
24869     //this.addSeparator();
24870         this.loading = this.navgroup.addItem({
24871             tooltip: this.refreshText,
24872             cls: "btn-outline-secondary",
24873             html : ' <i class="fa fa-refresh"></i>',
24874             preventDefault: true,
24875             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24876         });
24877         
24878     },
24879
24880     // private
24881     updateInfo : function(){
24882         if(this.displayEl){
24883             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24884             var msg = count == 0 ?
24885                 this.emptyMsg :
24886                 String.format(
24887                     this.displayMsg,
24888                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24889                 );
24890             this.displayEl.update(msg);
24891         }
24892     },
24893
24894     // private
24895     onLoad : function(ds, r, o)
24896     {
24897         this.cursor = o.params.start ? o.params.start : 0;
24898         
24899         var d = this.getPageData(),
24900             ap = d.activePage,
24901             ps = d.pages;
24902         
24903         
24904         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24905         this.field.dom.value = ap;
24906         this.first.setDisabled(ap == 1);
24907         this.prev.setDisabled(ap == 1);
24908         this.next.setDisabled(ap == ps);
24909         this.last.setDisabled(ap == ps);
24910         this.loading.enable();
24911         this.updateInfo();
24912     },
24913
24914     // private
24915     getPageData : function(){
24916         var total = this.ds.getTotalCount();
24917         return {
24918             total : total,
24919             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24920             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24921         };
24922     },
24923
24924     // private
24925     onLoadError : function(){
24926         this.loading.enable();
24927     },
24928
24929     // private
24930     onPagingKeydown : function(e){
24931         var k = e.getKey();
24932         var d = this.getPageData();
24933         if(k == e.RETURN){
24934             var v = this.field.dom.value, pageNum;
24935             if(!v || isNaN(pageNum = parseInt(v, 10))){
24936                 this.field.dom.value = d.activePage;
24937                 return;
24938             }
24939             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24940             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24941             e.stopEvent();
24942         }
24943         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))
24944         {
24945           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24946           this.field.dom.value = pageNum;
24947           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24948           e.stopEvent();
24949         }
24950         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24951         {
24952           var v = this.field.dom.value, pageNum; 
24953           var increment = (e.shiftKey) ? 10 : 1;
24954           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24955                 increment *= -1;
24956           }
24957           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24958             this.field.dom.value = d.activePage;
24959             return;
24960           }
24961           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24962           {
24963             this.field.dom.value = parseInt(v, 10) + increment;
24964             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24965             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24966           }
24967           e.stopEvent();
24968         }
24969     },
24970
24971     // private
24972     beforeLoad : function(){
24973         if(this.loading){
24974             this.loading.disable();
24975         }
24976     },
24977
24978     // private
24979     onClick : function(which){
24980         
24981         var ds = this.ds;
24982         if (!ds) {
24983             return;
24984         }
24985         
24986         switch(which){
24987             case "first":
24988                 ds.load({params:{start: 0, limit: this.pageSize}});
24989             break;
24990             case "prev":
24991                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24992             break;
24993             case "next":
24994                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24995             break;
24996             case "last":
24997                 var total = ds.getTotalCount();
24998                 var extra = total % this.pageSize;
24999                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25000                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25001             break;
25002             case "refresh":
25003                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25004             break;
25005         }
25006     },
25007
25008     /**
25009      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25010      * @param {Roo.data.Store} store The data store to unbind
25011      */
25012     unbind : function(ds){
25013         ds.un("beforeload", this.beforeLoad, this);
25014         ds.un("load", this.onLoad, this);
25015         ds.un("loadexception", this.onLoadError, this);
25016         ds.un("remove", this.updateInfo, this);
25017         ds.un("add", this.updateInfo, this);
25018         this.ds = undefined;
25019     },
25020
25021     /**
25022      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25023      * @param {Roo.data.Store} store The data store to bind
25024      */
25025     bind : function(ds){
25026         ds.on("beforeload", this.beforeLoad, this);
25027         ds.on("load", this.onLoad, this);
25028         ds.on("loadexception", this.onLoadError, this);
25029         ds.on("remove", this.updateInfo, this);
25030         ds.on("add", this.updateInfo, this);
25031         this.ds = ds;
25032     }
25033 });/*
25034  * - LGPL
25035  *
25036  * element
25037  * 
25038  */
25039
25040 /**
25041  * @class Roo.bootstrap.MessageBar
25042  * @extends Roo.bootstrap.Component
25043  * Bootstrap MessageBar class
25044  * @cfg {String} html contents of the MessageBar
25045  * @cfg {String} weight (info | success | warning | danger) default info
25046  * @cfg {String} beforeClass insert the bar before the given class
25047  * @cfg {Boolean} closable (true | false) default false
25048  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25049  * 
25050  * @constructor
25051  * Create a new Element
25052  * @param {Object} config The config object
25053  */
25054
25055 Roo.bootstrap.MessageBar = function(config){
25056     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25057 };
25058
25059 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25060     
25061     html: '',
25062     weight: 'info',
25063     closable: false,
25064     fixed: false,
25065     beforeClass: 'bootstrap-sticky-wrap',
25066     
25067     getAutoCreate : function(){
25068         
25069         var cfg = {
25070             tag: 'div',
25071             cls: 'alert alert-dismissable alert-' + this.weight,
25072             cn: [
25073                 {
25074                     tag: 'span',
25075                     cls: 'message',
25076                     html: this.html || ''
25077                 }
25078             ]
25079         };
25080         
25081         if(this.fixed){
25082             cfg.cls += ' alert-messages-fixed';
25083         }
25084         
25085         if(this.closable){
25086             cfg.cn.push({
25087                 tag: 'button',
25088                 cls: 'close',
25089                 html: 'x'
25090             });
25091         }
25092         
25093         return cfg;
25094     },
25095     
25096     onRender : function(ct, position)
25097     {
25098         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25099         
25100         if(!this.el){
25101             var cfg = Roo.apply({},  this.getAutoCreate());
25102             cfg.id = Roo.id();
25103             
25104             if (this.cls) {
25105                 cfg.cls += ' ' + this.cls;
25106             }
25107             if (this.style) {
25108                 cfg.style = this.style;
25109             }
25110             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25111             
25112             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25113         }
25114         
25115         this.el.select('>button.close').on('click', this.hide, this);
25116         
25117     },
25118     
25119     show : function()
25120     {
25121         if (!this.rendered) {
25122             this.render();
25123         }
25124         
25125         this.el.show();
25126         
25127         this.fireEvent('show', this);
25128         
25129     },
25130     
25131     hide : function()
25132     {
25133         if (!this.rendered) {
25134             this.render();
25135         }
25136         
25137         this.el.hide();
25138         
25139         this.fireEvent('hide', this);
25140     },
25141     
25142     update : function()
25143     {
25144 //        var e = this.el.dom.firstChild;
25145 //        
25146 //        if(this.closable){
25147 //            e = e.nextSibling;
25148 //        }
25149 //        
25150 //        e.data = this.html || '';
25151
25152         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25153     }
25154    
25155 });
25156
25157  
25158
25159      /*
25160  * - LGPL
25161  *
25162  * Graph
25163  * 
25164  */
25165
25166
25167 /**
25168  * @class Roo.bootstrap.Graph
25169  * @extends Roo.bootstrap.Component
25170  * Bootstrap Graph class
25171 > Prameters
25172  -sm {number} sm 4
25173  -md {number} md 5
25174  @cfg {String} graphtype  bar | vbar | pie
25175  @cfg {number} g_x coodinator | centre x (pie)
25176  @cfg {number} g_y coodinator | centre y (pie)
25177  @cfg {number} g_r radius (pie)
25178  @cfg {number} g_height height of the chart (respected by all elements in the set)
25179  @cfg {number} g_width width of the chart (respected by all elements in the set)
25180  @cfg {Object} title The title of the chart
25181     
25182  -{Array}  values
25183  -opts (object) options for the chart 
25184      o {
25185      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25186      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25187      o vgutter (number)
25188      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.
25189      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25190      o to
25191      o stretch (boolean)
25192      o }
25193  -opts (object) options for the pie
25194      o{
25195      o cut
25196      o startAngle (number)
25197      o endAngle (number)
25198      } 
25199  *
25200  * @constructor
25201  * Create a new Input
25202  * @param {Object} config The config object
25203  */
25204
25205 Roo.bootstrap.Graph = function(config){
25206     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25207     
25208     this.addEvents({
25209         // img events
25210         /**
25211          * @event click
25212          * The img click event for the img.
25213          * @param {Roo.EventObject} e
25214          */
25215         "click" : true
25216     });
25217 };
25218
25219 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25220     
25221     sm: 4,
25222     md: 5,
25223     graphtype: 'bar',
25224     g_height: 250,
25225     g_width: 400,
25226     g_x: 50,
25227     g_y: 50,
25228     g_r: 30,
25229     opts:{
25230         //g_colors: this.colors,
25231         g_type: 'soft',
25232         g_gutter: '20%'
25233
25234     },
25235     title : false,
25236
25237     getAutoCreate : function(){
25238         
25239         var cfg = {
25240             tag: 'div',
25241             html : null
25242         };
25243         
25244         
25245         return  cfg;
25246     },
25247
25248     onRender : function(ct,position){
25249         
25250         
25251         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25252         
25253         if (typeof(Raphael) == 'undefined') {
25254             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25255             return;
25256         }
25257         
25258         this.raphael = Raphael(this.el.dom);
25259         
25260                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25261                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25262                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25263                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25264                 /*
25265                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25266                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25267                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25268                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25269                 
25270                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25271                 r.barchart(330, 10, 300, 220, data1);
25272                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25273                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25274                 */
25275                 
25276                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25277                 // r.barchart(30, 30, 560, 250,  xdata, {
25278                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25279                 //     axis : "0 0 1 1",
25280                 //     axisxlabels :  xdata
25281                 //     //yvalues : cols,
25282                    
25283                 // });
25284 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25285 //        
25286 //        this.load(null,xdata,{
25287 //                axis : "0 0 1 1",
25288 //                axisxlabels :  xdata
25289 //                });
25290
25291     },
25292
25293     load : function(graphtype,xdata,opts)
25294     {
25295         this.raphael.clear();
25296         if(!graphtype) {
25297             graphtype = this.graphtype;
25298         }
25299         if(!opts){
25300             opts = this.opts;
25301         }
25302         var r = this.raphael,
25303             fin = function () {
25304                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25305             },
25306             fout = function () {
25307                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25308             },
25309             pfin = function() {
25310                 this.sector.stop();
25311                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25312
25313                 if (this.label) {
25314                     this.label[0].stop();
25315                     this.label[0].attr({ r: 7.5 });
25316                     this.label[1].attr({ "font-weight": 800 });
25317                 }
25318             },
25319             pfout = function() {
25320                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25321
25322                 if (this.label) {
25323                     this.label[0].animate({ r: 5 }, 500, "bounce");
25324                     this.label[1].attr({ "font-weight": 400 });
25325                 }
25326             };
25327
25328         switch(graphtype){
25329             case 'bar':
25330                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25331                 break;
25332             case 'hbar':
25333                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25334                 break;
25335             case 'pie':
25336 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25337 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25338 //            
25339                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25340                 
25341                 break;
25342
25343         }
25344         
25345         if(this.title){
25346             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25347         }
25348         
25349     },
25350     
25351     setTitle: function(o)
25352     {
25353         this.title = o;
25354     },
25355     
25356     initEvents: function() {
25357         
25358         if(!this.href){
25359             this.el.on('click', this.onClick, this);
25360         }
25361     },
25362     
25363     onClick : function(e)
25364     {
25365         Roo.log('img onclick');
25366         this.fireEvent('click', this, e);
25367     }
25368    
25369 });
25370
25371  
25372 /*
25373  * - LGPL
25374  *
25375  * numberBox
25376  * 
25377  */
25378 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25379
25380 /**
25381  * @class Roo.bootstrap.dash.NumberBox
25382  * @extends Roo.bootstrap.Component
25383  * Bootstrap NumberBox class
25384  * @cfg {String} headline Box headline
25385  * @cfg {String} content Box content
25386  * @cfg {String} icon Box icon
25387  * @cfg {String} footer Footer text
25388  * @cfg {String} fhref Footer href
25389  * 
25390  * @constructor
25391  * Create a new NumberBox
25392  * @param {Object} config The config object
25393  */
25394
25395
25396 Roo.bootstrap.dash.NumberBox = function(config){
25397     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25398     
25399 };
25400
25401 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25402     
25403     headline : '',
25404     content : '',
25405     icon : '',
25406     footer : '',
25407     fhref : '',
25408     ficon : '',
25409     
25410     getAutoCreate : function(){
25411         
25412         var cfg = {
25413             tag : 'div',
25414             cls : 'small-box ',
25415             cn : [
25416                 {
25417                     tag : 'div',
25418                     cls : 'inner',
25419                     cn :[
25420                         {
25421                             tag : 'h3',
25422                             cls : 'roo-headline',
25423                             html : this.headline
25424                         },
25425                         {
25426                             tag : 'p',
25427                             cls : 'roo-content',
25428                             html : this.content
25429                         }
25430                     ]
25431                 }
25432             ]
25433         };
25434         
25435         if(this.icon){
25436             cfg.cn.push({
25437                 tag : 'div',
25438                 cls : 'icon',
25439                 cn :[
25440                     {
25441                         tag : 'i',
25442                         cls : 'ion ' + this.icon
25443                     }
25444                 ]
25445             });
25446         }
25447         
25448         if(this.footer){
25449             var footer = {
25450                 tag : 'a',
25451                 cls : 'small-box-footer',
25452                 href : this.fhref || '#',
25453                 html : this.footer
25454             };
25455             
25456             cfg.cn.push(footer);
25457             
25458         }
25459         
25460         return  cfg;
25461     },
25462
25463     onRender : function(ct,position){
25464         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25465
25466
25467        
25468                 
25469     },
25470
25471     setHeadline: function (value)
25472     {
25473         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25474     },
25475     
25476     setFooter: function (value, href)
25477     {
25478         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25479         
25480         if(href){
25481             this.el.select('a.small-box-footer',true).first().attr('href', href);
25482         }
25483         
25484     },
25485
25486     setContent: function (value)
25487     {
25488         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25489     },
25490
25491     initEvents: function() 
25492     {   
25493         
25494     }
25495     
25496 });
25497
25498  
25499 /*
25500  * - LGPL
25501  *
25502  * TabBox
25503  * 
25504  */
25505 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25506
25507 /**
25508  * @class Roo.bootstrap.dash.TabBox
25509  * @extends Roo.bootstrap.Component
25510  * Bootstrap TabBox class
25511  * @cfg {String} title Title of the TabBox
25512  * @cfg {String} icon Icon of the TabBox
25513  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25514  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25515  * 
25516  * @constructor
25517  * Create a new TabBox
25518  * @param {Object} config The config object
25519  */
25520
25521
25522 Roo.bootstrap.dash.TabBox = function(config){
25523     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25524     this.addEvents({
25525         // raw events
25526         /**
25527          * @event addpane
25528          * When a pane is added
25529          * @param {Roo.bootstrap.dash.TabPane} pane
25530          */
25531         "addpane" : true,
25532         /**
25533          * @event activatepane
25534          * When a pane is activated
25535          * @param {Roo.bootstrap.dash.TabPane} pane
25536          */
25537         "activatepane" : true
25538         
25539          
25540     });
25541     
25542     this.panes = [];
25543 };
25544
25545 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25546
25547     title : '',
25548     icon : false,
25549     showtabs : true,
25550     tabScrollable : false,
25551     
25552     getChildContainer : function()
25553     {
25554         return this.el.select('.tab-content', true).first();
25555     },
25556     
25557     getAutoCreate : function(){
25558         
25559         var header = {
25560             tag: 'li',
25561             cls: 'pull-left header',
25562             html: this.title,
25563             cn : []
25564         };
25565         
25566         if(this.icon){
25567             header.cn.push({
25568                 tag: 'i',
25569                 cls: 'fa ' + this.icon
25570             });
25571         }
25572         
25573         var h = {
25574             tag: 'ul',
25575             cls: 'nav nav-tabs pull-right',
25576             cn: [
25577                 header
25578             ]
25579         };
25580         
25581         if(this.tabScrollable){
25582             h = {
25583                 tag: 'div',
25584                 cls: 'tab-header',
25585                 cn: [
25586                     {
25587                         tag: 'ul',
25588                         cls: 'nav nav-tabs pull-right',
25589                         cn: [
25590                             header
25591                         ]
25592                     }
25593                 ]
25594             };
25595         }
25596         
25597         var cfg = {
25598             tag: 'div',
25599             cls: 'nav-tabs-custom',
25600             cn: [
25601                 h,
25602                 {
25603                     tag: 'div',
25604                     cls: 'tab-content no-padding',
25605                     cn: []
25606                 }
25607             ]
25608         };
25609
25610         return  cfg;
25611     },
25612     initEvents : function()
25613     {
25614         //Roo.log('add add pane handler');
25615         this.on('addpane', this.onAddPane, this);
25616     },
25617      /**
25618      * Updates the box title
25619      * @param {String} html to set the title to.
25620      */
25621     setTitle : function(value)
25622     {
25623         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25624     },
25625     onAddPane : function(pane)
25626     {
25627         this.panes.push(pane);
25628         //Roo.log('addpane');
25629         //Roo.log(pane);
25630         // tabs are rendere left to right..
25631         if(!this.showtabs){
25632             return;
25633         }
25634         
25635         var ctr = this.el.select('.nav-tabs', true).first();
25636          
25637          
25638         var existing = ctr.select('.nav-tab',true);
25639         var qty = existing.getCount();;
25640         
25641         
25642         var tab = ctr.createChild({
25643             tag : 'li',
25644             cls : 'nav-tab' + (qty ? '' : ' active'),
25645             cn : [
25646                 {
25647                     tag : 'a',
25648                     href:'#',
25649                     html : pane.title
25650                 }
25651             ]
25652         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25653         pane.tab = tab;
25654         
25655         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25656         if (!qty) {
25657             pane.el.addClass('active');
25658         }
25659         
25660                 
25661     },
25662     onTabClick : function(ev,un,ob,pane)
25663     {
25664         //Roo.log('tab - prev default');
25665         ev.preventDefault();
25666         
25667         
25668         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25669         pane.tab.addClass('active');
25670         //Roo.log(pane.title);
25671         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25672         // technically we should have a deactivate event.. but maybe add later.
25673         // and it should not de-activate the selected tab...
25674         this.fireEvent('activatepane', pane);
25675         pane.el.addClass('active');
25676         pane.fireEvent('activate');
25677         
25678         
25679     },
25680     
25681     getActivePane : function()
25682     {
25683         var r = false;
25684         Roo.each(this.panes, function(p) {
25685             if(p.el.hasClass('active')){
25686                 r = p;
25687                 return false;
25688             }
25689             
25690             return;
25691         });
25692         
25693         return r;
25694     }
25695     
25696     
25697 });
25698
25699  
25700 /*
25701  * - LGPL
25702  *
25703  * Tab pane
25704  * 
25705  */
25706 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25707 /**
25708  * @class Roo.bootstrap.TabPane
25709  * @extends Roo.bootstrap.Component
25710  * Bootstrap TabPane class
25711  * @cfg {Boolean} active (false | true) Default false
25712  * @cfg {String} title title of panel
25713
25714  * 
25715  * @constructor
25716  * Create a new TabPane
25717  * @param {Object} config The config object
25718  */
25719
25720 Roo.bootstrap.dash.TabPane = function(config){
25721     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25722     
25723     this.addEvents({
25724         // raw events
25725         /**
25726          * @event activate
25727          * When a pane is activated
25728          * @param {Roo.bootstrap.dash.TabPane} pane
25729          */
25730         "activate" : true
25731          
25732     });
25733 };
25734
25735 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25736     
25737     active : false,
25738     title : '',
25739     
25740     // the tabBox that this is attached to.
25741     tab : false,
25742      
25743     getAutoCreate : function() 
25744     {
25745         var cfg = {
25746             tag: 'div',
25747             cls: 'tab-pane'
25748         };
25749         
25750         if(this.active){
25751             cfg.cls += ' active';
25752         }
25753         
25754         return cfg;
25755     },
25756     initEvents  : function()
25757     {
25758         //Roo.log('trigger add pane handler');
25759         this.parent().fireEvent('addpane', this)
25760     },
25761     
25762      /**
25763      * Updates the tab title 
25764      * @param {String} html to set the title to.
25765      */
25766     setTitle: function(str)
25767     {
25768         if (!this.tab) {
25769             return;
25770         }
25771         this.title = str;
25772         this.tab.select('a', true).first().dom.innerHTML = str;
25773         
25774     }
25775     
25776     
25777     
25778 });
25779
25780  
25781
25782
25783  /*
25784  * - LGPL
25785  *
25786  * menu
25787  * 
25788  */
25789 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25790
25791 /**
25792  * @class Roo.bootstrap.menu.Menu
25793  * @extends Roo.bootstrap.Component
25794  * Bootstrap Menu class - container for Menu
25795  * @cfg {String} html Text of the menu
25796  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25797  * @cfg {String} icon Font awesome icon
25798  * @cfg {String} pos Menu align to (top | bottom) default bottom
25799  * 
25800  * 
25801  * @constructor
25802  * Create a new Menu
25803  * @param {Object} config The config object
25804  */
25805
25806
25807 Roo.bootstrap.menu.Menu = function(config){
25808     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25809     
25810     this.addEvents({
25811         /**
25812          * @event beforeshow
25813          * Fires before this menu is displayed
25814          * @param {Roo.bootstrap.menu.Menu} this
25815          */
25816         beforeshow : true,
25817         /**
25818          * @event beforehide
25819          * Fires before this menu is hidden
25820          * @param {Roo.bootstrap.menu.Menu} this
25821          */
25822         beforehide : true,
25823         /**
25824          * @event show
25825          * Fires after this menu is displayed
25826          * @param {Roo.bootstrap.menu.Menu} this
25827          */
25828         show : true,
25829         /**
25830          * @event hide
25831          * Fires after this menu is hidden
25832          * @param {Roo.bootstrap.menu.Menu} this
25833          */
25834         hide : true,
25835         /**
25836          * @event click
25837          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25838          * @param {Roo.bootstrap.menu.Menu} this
25839          * @param {Roo.EventObject} e
25840          */
25841         click : true
25842     });
25843     
25844 };
25845
25846 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25847     
25848     submenu : false,
25849     html : '',
25850     weight : 'default',
25851     icon : false,
25852     pos : 'bottom',
25853     
25854     
25855     getChildContainer : function() {
25856         if(this.isSubMenu){
25857             return this.el;
25858         }
25859         
25860         return this.el.select('ul.dropdown-menu', true).first();  
25861     },
25862     
25863     getAutoCreate : function()
25864     {
25865         var text = [
25866             {
25867                 tag : 'span',
25868                 cls : 'roo-menu-text',
25869                 html : this.html
25870             }
25871         ];
25872         
25873         if(this.icon){
25874             text.unshift({
25875                 tag : 'i',
25876                 cls : 'fa ' + this.icon
25877             })
25878         }
25879         
25880         
25881         var cfg = {
25882             tag : 'div',
25883             cls : 'btn-group',
25884             cn : [
25885                 {
25886                     tag : 'button',
25887                     cls : 'dropdown-button btn btn-' + this.weight,
25888                     cn : text
25889                 },
25890                 {
25891                     tag : 'button',
25892                     cls : 'dropdown-toggle btn btn-' + this.weight,
25893                     cn : [
25894                         {
25895                             tag : 'span',
25896                             cls : 'caret'
25897                         }
25898                     ]
25899                 },
25900                 {
25901                     tag : 'ul',
25902                     cls : 'dropdown-menu'
25903                 }
25904             ]
25905             
25906         };
25907         
25908         if(this.pos == 'top'){
25909             cfg.cls += ' dropup';
25910         }
25911         
25912         if(this.isSubMenu){
25913             cfg = {
25914                 tag : 'ul',
25915                 cls : 'dropdown-menu'
25916             }
25917         }
25918         
25919         return cfg;
25920     },
25921     
25922     onRender : function(ct, position)
25923     {
25924         this.isSubMenu = ct.hasClass('dropdown-submenu');
25925         
25926         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25927     },
25928     
25929     initEvents : function() 
25930     {
25931         if(this.isSubMenu){
25932             return;
25933         }
25934         
25935         this.hidden = true;
25936         
25937         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25938         this.triggerEl.on('click', this.onTriggerPress, this);
25939         
25940         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25941         this.buttonEl.on('click', this.onClick, this);
25942         
25943     },
25944     
25945     list : function()
25946     {
25947         if(this.isSubMenu){
25948             return this.el;
25949         }
25950         
25951         return this.el.select('ul.dropdown-menu', true).first();
25952     },
25953     
25954     onClick : function(e)
25955     {
25956         this.fireEvent("click", this, e);
25957     },
25958     
25959     onTriggerPress  : function(e)
25960     {   
25961         if (this.isVisible()) {
25962             this.hide();
25963         } else {
25964             this.show();
25965         }
25966     },
25967     
25968     isVisible : function(){
25969         return !this.hidden;
25970     },
25971     
25972     show : function()
25973     {
25974         this.fireEvent("beforeshow", this);
25975         
25976         this.hidden = false;
25977         this.el.addClass('open');
25978         
25979         Roo.get(document).on("mouseup", this.onMouseUp, this);
25980         
25981         this.fireEvent("show", this);
25982         
25983         
25984     },
25985     
25986     hide : function()
25987     {
25988         this.fireEvent("beforehide", this);
25989         
25990         this.hidden = true;
25991         this.el.removeClass('open');
25992         
25993         Roo.get(document).un("mouseup", this.onMouseUp);
25994         
25995         this.fireEvent("hide", this);
25996     },
25997     
25998     onMouseUp : function()
25999     {
26000         this.hide();
26001     }
26002     
26003 });
26004
26005  
26006  /*
26007  * - LGPL
26008  *
26009  * menu item
26010  * 
26011  */
26012 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26013
26014 /**
26015  * @class Roo.bootstrap.menu.Item
26016  * @extends Roo.bootstrap.Component
26017  * Bootstrap MenuItem class
26018  * @cfg {Boolean} submenu (true | false) default false
26019  * @cfg {String} html text of the item
26020  * @cfg {String} href the link
26021  * @cfg {Boolean} disable (true | false) default false
26022  * @cfg {Boolean} preventDefault (true | false) default true
26023  * @cfg {String} icon Font awesome icon
26024  * @cfg {String} pos Submenu align to (left | right) default right 
26025  * 
26026  * 
26027  * @constructor
26028  * Create a new Item
26029  * @param {Object} config The config object
26030  */
26031
26032
26033 Roo.bootstrap.menu.Item = function(config){
26034     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26035     this.addEvents({
26036         /**
26037          * @event mouseover
26038          * Fires when the mouse is hovering over this menu
26039          * @param {Roo.bootstrap.menu.Item} this
26040          * @param {Roo.EventObject} e
26041          */
26042         mouseover : true,
26043         /**
26044          * @event mouseout
26045          * Fires when the mouse exits this menu
26046          * @param {Roo.bootstrap.menu.Item} this
26047          * @param {Roo.EventObject} e
26048          */
26049         mouseout : true,
26050         // raw events
26051         /**
26052          * @event click
26053          * The raw click event for the entire grid.
26054          * @param {Roo.EventObject} e
26055          */
26056         click : true
26057     });
26058 };
26059
26060 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26061     
26062     submenu : false,
26063     href : '',
26064     html : '',
26065     preventDefault: true,
26066     disable : false,
26067     icon : false,
26068     pos : 'right',
26069     
26070     getAutoCreate : function()
26071     {
26072         var text = [
26073             {
26074                 tag : 'span',
26075                 cls : 'roo-menu-item-text',
26076                 html : this.html
26077             }
26078         ];
26079         
26080         if(this.icon){
26081             text.unshift({
26082                 tag : 'i',
26083                 cls : 'fa ' + this.icon
26084             })
26085         }
26086         
26087         var cfg = {
26088             tag : 'li',
26089             cn : [
26090                 {
26091                     tag : 'a',
26092                     href : this.href || '#',
26093                     cn : text
26094                 }
26095             ]
26096         };
26097         
26098         if(this.disable){
26099             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26100         }
26101         
26102         if(this.submenu){
26103             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26104             
26105             if(this.pos == 'left'){
26106                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26107             }
26108         }
26109         
26110         return cfg;
26111     },
26112     
26113     initEvents : function() 
26114     {
26115         this.el.on('mouseover', this.onMouseOver, this);
26116         this.el.on('mouseout', this.onMouseOut, this);
26117         
26118         this.el.select('a', true).first().on('click', this.onClick, this);
26119         
26120     },
26121     
26122     onClick : function(e)
26123     {
26124         if(this.preventDefault){
26125             e.preventDefault();
26126         }
26127         
26128         this.fireEvent("click", this, e);
26129     },
26130     
26131     onMouseOver : function(e)
26132     {
26133         if(this.submenu && this.pos == 'left'){
26134             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26135         }
26136         
26137         this.fireEvent("mouseover", this, e);
26138     },
26139     
26140     onMouseOut : function(e)
26141     {
26142         this.fireEvent("mouseout", this, e);
26143     }
26144 });
26145
26146  
26147
26148  /*
26149  * - LGPL
26150  *
26151  * menu separator
26152  * 
26153  */
26154 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26155
26156 /**
26157  * @class Roo.bootstrap.menu.Separator
26158  * @extends Roo.bootstrap.Component
26159  * Bootstrap Separator class
26160  * 
26161  * @constructor
26162  * Create a new Separator
26163  * @param {Object} config The config object
26164  */
26165
26166
26167 Roo.bootstrap.menu.Separator = function(config){
26168     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26169 };
26170
26171 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26172     
26173     getAutoCreate : function(){
26174         var cfg = {
26175             tag : 'li',
26176             cls: 'divider'
26177         };
26178         
26179         return cfg;
26180     }
26181    
26182 });
26183
26184  
26185
26186  /*
26187  * - LGPL
26188  *
26189  * Tooltip
26190  * 
26191  */
26192
26193 /**
26194  * @class Roo.bootstrap.Tooltip
26195  * Bootstrap Tooltip class
26196  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26197  * to determine which dom element triggers the tooltip.
26198  * 
26199  * It needs to add support for additional attributes like tooltip-position
26200  * 
26201  * @constructor
26202  * Create a new Toolti
26203  * @param {Object} config The config object
26204  */
26205
26206 Roo.bootstrap.Tooltip = function(config){
26207     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26208     
26209     this.alignment = Roo.bootstrap.Tooltip.alignment;
26210     
26211     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26212         this.alignment = config.alignment;
26213     }
26214     
26215 };
26216
26217 Roo.apply(Roo.bootstrap.Tooltip, {
26218     /**
26219      * @function init initialize tooltip monitoring.
26220      * @static
26221      */
26222     currentEl : false,
26223     currentTip : false,
26224     currentRegion : false,
26225     
26226     //  init : delay?
26227     
26228     init : function()
26229     {
26230         Roo.get(document).on('mouseover', this.enter ,this);
26231         Roo.get(document).on('mouseout', this.leave, this);
26232          
26233         
26234         this.currentTip = new Roo.bootstrap.Tooltip();
26235     },
26236     
26237     enter : function(ev)
26238     {
26239         var dom = ev.getTarget();
26240         
26241         //Roo.log(['enter',dom]);
26242         var el = Roo.fly(dom);
26243         if (this.currentEl) {
26244             //Roo.log(dom);
26245             //Roo.log(this.currentEl);
26246             //Roo.log(this.currentEl.contains(dom));
26247             if (this.currentEl == el) {
26248                 return;
26249             }
26250             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26251                 return;
26252             }
26253
26254         }
26255         
26256         if (this.currentTip.el) {
26257             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26258         }    
26259         //Roo.log(ev);
26260         
26261         if(!el || el.dom == document){
26262             return;
26263         }
26264         
26265         var bindEl = el;
26266         
26267         // you can not look for children, as if el is the body.. then everythign is the child..
26268         if (!el.attr('tooltip')) { //
26269             if (!el.select("[tooltip]").elements.length) {
26270                 return;
26271             }
26272             // is the mouse over this child...?
26273             bindEl = el.select("[tooltip]").first();
26274             var xy = ev.getXY();
26275             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26276                 //Roo.log("not in region.");
26277                 return;
26278             }
26279             //Roo.log("child element over..");
26280             
26281         }
26282         this.currentEl = bindEl;
26283         this.currentTip.bind(bindEl);
26284         this.currentRegion = Roo.lib.Region.getRegion(dom);
26285         this.currentTip.enter();
26286         
26287     },
26288     leave : function(ev)
26289     {
26290         var dom = ev.getTarget();
26291         //Roo.log(['leave',dom]);
26292         if (!this.currentEl) {
26293             return;
26294         }
26295         
26296         
26297         if (dom != this.currentEl.dom) {
26298             return;
26299         }
26300         var xy = ev.getXY();
26301         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26302             return;
26303         }
26304         // only activate leave if mouse cursor is outside... bounding box..
26305         
26306         
26307         
26308         
26309         if (this.currentTip) {
26310             this.currentTip.leave();
26311         }
26312         //Roo.log('clear currentEl');
26313         this.currentEl = false;
26314         
26315         
26316     },
26317     alignment : {
26318         'left' : ['r-l', [-2,0], 'right'],
26319         'right' : ['l-r', [2,0], 'left'],
26320         'bottom' : ['t-b', [0,2], 'top'],
26321         'top' : [ 'b-t', [0,-2], 'bottom']
26322     }
26323     
26324 });
26325
26326
26327 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26328     
26329     
26330     bindEl : false,
26331     
26332     delay : null, // can be { show : 300 , hide: 500}
26333     
26334     timeout : null,
26335     
26336     hoverState : null, //???
26337     
26338     placement : 'bottom', 
26339     
26340     alignment : false,
26341     
26342     getAutoCreate : function(){
26343     
26344         var cfg = {
26345            cls : 'tooltip',
26346            role : 'tooltip',
26347            cn : [
26348                 {
26349                     cls : 'tooltip-arrow'
26350                 },
26351                 {
26352                     cls : 'tooltip-inner'
26353                 }
26354            ]
26355         };
26356         
26357         return cfg;
26358     },
26359     bind : function(el)
26360     {
26361         this.bindEl = el;
26362     },
26363       
26364     
26365     enter : function () {
26366        
26367         if (this.timeout != null) {
26368             clearTimeout(this.timeout);
26369         }
26370         
26371         this.hoverState = 'in';
26372          //Roo.log("enter - show");
26373         if (!this.delay || !this.delay.show) {
26374             this.show();
26375             return;
26376         }
26377         var _t = this;
26378         this.timeout = setTimeout(function () {
26379             if (_t.hoverState == 'in') {
26380                 _t.show();
26381             }
26382         }, this.delay.show);
26383     },
26384     leave : function()
26385     {
26386         clearTimeout(this.timeout);
26387     
26388         this.hoverState = 'out';
26389          if (!this.delay || !this.delay.hide) {
26390             this.hide();
26391             return;
26392         }
26393        
26394         var _t = this;
26395         this.timeout = setTimeout(function () {
26396             //Roo.log("leave - timeout");
26397             
26398             if (_t.hoverState == 'out') {
26399                 _t.hide();
26400                 Roo.bootstrap.Tooltip.currentEl = false;
26401             }
26402         }, delay);
26403     },
26404     
26405     show : function (msg)
26406     {
26407         if (!this.el) {
26408             this.render(document.body);
26409         }
26410         // set content.
26411         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26412         
26413         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26414         
26415         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26416         
26417         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26418         
26419         var placement = typeof this.placement == 'function' ?
26420             this.placement.call(this, this.el, on_el) :
26421             this.placement;
26422             
26423         var autoToken = /\s?auto?\s?/i;
26424         var autoPlace = autoToken.test(placement);
26425         if (autoPlace) {
26426             placement = placement.replace(autoToken, '') || 'top';
26427         }
26428         
26429         //this.el.detach()
26430         //this.el.setXY([0,0]);
26431         this.el.show();
26432         //this.el.dom.style.display='block';
26433         
26434         //this.el.appendTo(on_el);
26435         
26436         var p = this.getPosition();
26437         var box = this.el.getBox();
26438         
26439         if (autoPlace) {
26440             // fixme..
26441         }
26442         
26443         var align = this.alignment[placement];
26444         
26445         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26446         
26447         if(placement == 'top' || placement == 'bottom'){
26448             if(xy[0] < 0){
26449                 placement = 'right';
26450             }
26451             
26452             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26453                 placement = 'left';
26454             }
26455             
26456             var scroll = Roo.select('body', true).first().getScroll();
26457             
26458             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26459                 placement = 'top';
26460             }
26461             
26462             align = this.alignment[placement];
26463         }
26464         
26465         this.el.alignTo(this.bindEl, align[0],align[1]);
26466         //var arrow = this.el.select('.arrow',true).first();
26467         //arrow.set(align[2], 
26468         
26469         this.el.addClass(placement);
26470         
26471         this.el.addClass('in fade');
26472         
26473         this.hoverState = null;
26474         
26475         if (this.el.hasClass('fade')) {
26476             // fade it?
26477         }
26478         
26479     },
26480     hide : function()
26481     {
26482          
26483         if (!this.el) {
26484             return;
26485         }
26486         //this.el.setXY([0,0]);
26487         this.el.removeClass('in');
26488         //this.el.hide();
26489         
26490     }
26491     
26492 });
26493  
26494
26495  /*
26496  * - LGPL
26497  *
26498  * Location Picker
26499  * 
26500  */
26501
26502 /**
26503  * @class Roo.bootstrap.LocationPicker
26504  * @extends Roo.bootstrap.Component
26505  * Bootstrap LocationPicker class
26506  * @cfg {Number} latitude Position when init default 0
26507  * @cfg {Number} longitude Position when init default 0
26508  * @cfg {Number} zoom default 15
26509  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26510  * @cfg {Boolean} mapTypeControl default false
26511  * @cfg {Boolean} disableDoubleClickZoom default false
26512  * @cfg {Boolean} scrollwheel default true
26513  * @cfg {Boolean} streetViewControl default false
26514  * @cfg {Number} radius default 0
26515  * @cfg {String} locationName
26516  * @cfg {Boolean} draggable default true
26517  * @cfg {Boolean} enableAutocomplete default false
26518  * @cfg {Boolean} enableReverseGeocode default true
26519  * @cfg {String} markerTitle
26520  * 
26521  * @constructor
26522  * Create a new LocationPicker
26523  * @param {Object} config The config object
26524  */
26525
26526
26527 Roo.bootstrap.LocationPicker = function(config){
26528     
26529     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26530     
26531     this.addEvents({
26532         /**
26533          * @event initial
26534          * Fires when the picker initialized.
26535          * @param {Roo.bootstrap.LocationPicker} this
26536          * @param {Google Location} location
26537          */
26538         initial : true,
26539         /**
26540          * @event positionchanged
26541          * Fires when the picker position changed.
26542          * @param {Roo.bootstrap.LocationPicker} this
26543          * @param {Google Location} location
26544          */
26545         positionchanged : true,
26546         /**
26547          * @event resize
26548          * Fires when the map resize.
26549          * @param {Roo.bootstrap.LocationPicker} this
26550          */
26551         resize : true,
26552         /**
26553          * @event show
26554          * Fires when the map show.
26555          * @param {Roo.bootstrap.LocationPicker} this
26556          */
26557         show : true,
26558         /**
26559          * @event hide
26560          * Fires when the map hide.
26561          * @param {Roo.bootstrap.LocationPicker} this
26562          */
26563         hide : true,
26564         /**
26565          * @event mapClick
26566          * Fires when click the map.
26567          * @param {Roo.bootstrap.LocationPicker} this
26568          * @param {Map event} e
26569          */
26570         mapClick : true,
26571         /**
26572          * @event mapRightClick
26573          * Fires when right click the map.
26574          * @param {Roo.bootstrap.LocationPicker} this
26575          * @param {Map event} e
26576          */
26577         mapRightClick : true,
26578         /**
26579          * @event markerClick
26580          * Fires when click the marker.
26581          * @param {Roo.bootstrap.LocationPicker} this
26582          * @param {Map event} e
26583          */
26584         markerClick : true,
26585         /**
26586          * @event markerRightClick
26587          * Fires when right click the marker.
26588          * @param {Roo.bootstrap.LocationPicker} this
26589          * @param {Map event} e
26590          */
26591         markerRightClick : true,
26592         /**
26593          * @event OverlayViewDraw
26594          * Fires when OverlayView Draw
26595          * @param {Roo.bootstrap.LocationPicker} this
26596          */
26597         OverlayViewDraw : true,
26598         /**
26599          * @event OverlayViewOnAdd
26600          * Fires when OverlayView Draw
26601          * @param {Roo.bootstrap.LocationPicker} this
26602          */
26603         OverlayViewOnAdd : true,
26604         /**
26605          * @event OverlayViewOnRemove
26606          * Fires when OverlayView Draw
26607          * @param {Roo.bootstrap.LocationPicker} this
26608          */
26609         OverlayViewOnRemove : true,
26610         /**
26611          * @event OverlayViewShow
26612          * Fires when OverlayView Draw
26613          * @param {Roo.bootstrap.LocationPicker} this
26614          * @param {Pixel} cpx
26615          */
26616         OverlayViewShow : true,
26617         /**
26618          * @event OverlayViewHide
26619          * Fires when OverlayView Draw
26620          * @param {Roo.bootstrap.LocationPicker} this
26621          */
26622         OverlayViewHide : true,
26623         /**
26624          * @event loadexception
26625          * Fires when load google lib failed.
26626          * @param {Roo.bootstrap.LocationPicker} this
26627          */
26628         loadexception : true
26629     });
26630         
26631 };
26632
26633 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26634     
26635     gMapContext: false,
26636     
26637     latitude: 0,
26638     longitude: 0,
26639     zoom: 15,
26640     mapTypeId: false,
26641     mapTypeControl: false,
26642     disableDoubleClickZoom: false,
26643     scrollwheel: true,
26644     streetViewControl: false,
26645     radius: 0,
26646     locationName: '',
26647     draggable: true,
26648     enableAutocomplete: false,
26649     enableReverseGeocode: true,
26650     markerTitle: '',
26651     
26652     getAutoCreate: function()
26653     {
26654
26655         var cfg = {
26656             tag: 'div',
26657             cls: 'roo-location-picker'
26658         };
26659         
26660         return cfg
26661     },
26662     
26663     initEvents: function(ct, position)
26664     {       
26665         if(!this.el.getWidth() || this.isApplied()){
26666             return;
26667         }
26668         
26669         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26670         
26671         this.initial();
26672     },
26673     
26674     initial: function()
26675     {
26676         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26677             this.fireEvent('loadexception', this);
26678             return;
26679         }
26680         
26681         if(!this.mapTypeId){
26682             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26683         }
26684         
26685         this.gMapContext = this.GMapContext();
26686         
26687         this.initOverlayView();
26688         
26689         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26690         
26691         var _this = this;
26692                 
26693         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26694             _this.setPosition(_this.gMapContext.marker.position);
26695         });
26696         
26697         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26698             _this.fireEvent('mapClick', this, event);
26699             
26700         });
26701
26702         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26703             _this.fireEvent('mapRightClick', this, event);
26704             
26705         });
26706         
26707         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26708             _this.fireEvent('markerClick', this, event);
26709             
26710         });
26711
26712         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26713             _this.fireEvent('markerRightClick', this, event);
26714             
26715         });
26716         
26717         this.setPosition(this.gMapContext.location);
26718         
26719         this.fireEvent('initial', this, this.gMapContext.location);
26720     },
26721     
26722     initOverlayView: function()
26723     {
26724         var _this = this;
26725         
26726         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26727             
26728             draw: function()
26729             {
26730                 _this.fireEvent('OverlayViewDraw', _this);
26731             },
26732             
26733             onAdd: function()
26734             {
26735                 _this.fireEvent('OverlayViewOnAdd', _this);
26736             },
26737             
26738             onRemove: function()
26739             {
26740                 _this.fireEvent('OverlayViewOnRemove', _this);
26741             },
26742             
26743             show: function(cpx)
26744             {
26745                 _this.fireEvent('OverlayViewShow', _this, cpx);
26746             },
26747             
26748             hide: function()
26749             {
26750                 _this.fireEvent('OverlayViewHide', _this);
26751             }
26752             
26753         });
26754     },
26755     
26756     fromLatLngToContainerPixel: function(event)
26757     {
26758         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26759     },
26760     
26761     isApplied: function() 
26762     {
26763         return this.getGmapContext() == false ? false : true;
26764     },
26765     
26766     getGmapContext: function() 
26767     {
26768         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26769     },
26770     
26771     GMapContext: function() 
26772     {
26773         var position = new google.maps.LatLng(this.latitude, this.longitude);
26774         
26775         var _map = new google.maps.Map(this.el.dom, {
26776             center: position,
26777             zoom: this.zoom,
26778             mapTypeId: this.mapTypeId,
26779             mapTypeControl: this.mapTypeControl,
26780             disableDoubleClickZoom: this.disableDoubleClickZoom,
26781             scrollwheel: this.scrollwheel,
26782             streetViewControl: this.streetViewControl,
26783             locationName: this.locationName,
26784             draggable: this.draggable,
26785             enableAutocomplete: this.enableAutocomplete,
26786             enableReverseGeocode: this.enableReverseGeocode
26787         });
26788         
26789         var _marker = new google.maps.Marker({
26790             position: position,
26791             map: _map,
26792             title: this.markerTitle,
26793             draggable: this.draggable
26794         });
26795         
26796         return {
26797             map: _map,
26798             marker: _marker,
26799             circle: null,
26800             location: position,
26801             radius: this.radius,
26802             locationName: this.locationName,
26803             addressComponents: {
26804                 formatted_address: null,
26805                 addressLine1: null,
26806                 addressLine2: null,
26807                 streetName: null,
26808                 streetNumber: null,
26809                 city: null,
26810                 district: null,
26811                 state: null,
26812                 stateOrProvince: null
26813             },
26814             settings: this,
26815             domContainer: this.el.dom,
26816             geodecoder: new google.maps.Geocoder()
26817         };
26818     },
26819     
26820     drawCircle: function(center, radius, options) 
26821     {
26822         if (this.gMapContext.circle != null) {
26823             this.gMapContext.circle.setMap(null);
26824         }
26825         if (radius > 0) {
26826             radius *= 1;
26827             options = Roo.apply({}, options, {
26828                 strokeColor: "#0000FF",
26829                 strokeOpacity: .35,
26830                 strokeWeight: 2,
26831                 fillColor: "#0000FF",
26832                 fillOpacity: .2
26833             });
26834             
26835             options.map = this.gMapContext.map;
26836             options.radius = radius;
26837             options.center = center;
26838             this.gMapContext.circle = new google.maps.Circle(options);
26839             return this.gMapContext.circle;
26840         }
26841         
26842         return null;
26843     },
26844     
26845     setPosition: function(location) 
26846     {
26847         this.gMapContext.location = location;
26848         this.gMapContext.marker.setPosition(location);
26849         this.gMapContext.map.panTo(location);
26850         this.drawCircle(location, this.gMapContext.radius, {});
26851         
26852         var _this = this;
26853         
26854         if (this.gMapContext.settings.enableReverseGeocode) {
26855             this.gMapContext.geodecoder.geocode({
26856                 latLng: this.gMapContext.location
26857             }, function(results, status) {
26858                 
26859                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26860                     _this.gMapContext.locationName = results[0].formatted_address;
26861                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26862                     
26863                     _this.fireEvent('positionchanged', this, location);
26864                 }
26865             });
26866             
26867             return;
26868         }
26869         
26870         this.fireEvent('positionchanged', this, location);
26871     },
26872     
26873     resize: function()
26874     {
26875         google.maps.event.trigger(this.gMapContext.map, "resize");
26876         
26877         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26878         
26879         this.fireEvent('resize', this);
26880     },
26881     
26882     setPositionByLatLng: function(latitude, longitude)
26883     {
26884         this.setPosition(new google.maps.LatLng(latitude, longitude));
26885     },
26886     
26887     getCurrentPosition: function() 
26888     {
26889         return {
26890             latitude: this.gMapContext.location.lat(),
26891             longitude: this.gMapContext.location.lng()
26892         };
26893     },
26894     
26895     getAddressName: function() 
26896     {
26897         return this.gMapContext.locationName;
26898     },
26899     
26900     getAddressComponents: function() 
26901     {
26902         return this.gMapContext.addressComponents;
26903     },
26904     
26905     address_component_from_google_geocode: function(address_components) 
26906     {
26907         var result = {};
26908         
26909         for (var i = 0; i < address_components.length; i++) {
26910             var component = address_components[i];
26911             if (component.types.indexOf("postal_code") >= 0) {
26912                 result.postalCode = component.short_name;
26913             } else if (component.types.indexOf("street_number") >= 0) {
26914                 result.streetNumber = component.short_name;
26915             } else if (component.types.indexOf("route") >= 0) {
26916                 result.streetName = component.short_name;
26917             } else if (component.types.indexOf("neighborhood") >= 0) {
26918                 result.city = component.short_name;
26919             } else if (component.types.indexOf("locality") >= 0) {
26920                 result.city = component.short_name;
26921             } else if (component.types.indexOf("sublocality") >= 0) {
26922                 result.district = component.short_name;
26923             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26924                 result.stateOrProvince = component.short_name;
26925             } else if (component.types.indexOf("country") >= 0) {
26926                 result.country = component.short_name;
26927             }
26928         }
26929         
26930         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26931         result.addressLine2 = "";
26932         return result;
26933     },
26934     
26935     setZoomLevel: function(zoom)
26936     {
26937         this.gMapContext.map.setZoom(zoom);
26938     },
26939     
26940     show: function()
26941     {
26942         if(!this.el){
26943             return;
26944         }
26945         
26946         this.el.show();
26947         
26948         this.resize();
26949         
26950         this.fireEvent('show', this);
26951     },
26952     
26953     hide: function()
26954     {
26955         if(!this.el){
26956             return;
26957         }
26958         
26959         this.el.hide();
26960         
26961         this.fireEvent('hide', this);
26962     }
26963     
26964 });
26965
26966 Roo.apply(Roo.bootstrap.LocationPicker, {
26967     
26968     OverlayView : function(map, options)
26969     {
26970         options = options || {};
26971         
26972         this.setMap(map);
26973     }
26974     
26975     
26976 });/*
26977  * - LGPL
26978  *
26979  * Alert
26980  * 
26981  */
26982
26983 /**
26984  * @class Roo.bootstrap.Alert
26985  * @extends Roo.bootstrap.Component
26986  * Bootstrap Alert class
26987  * @cfg {String} title The title of alert
26988  * @cfg {String} html The content of alert
26989  * @cfg {String} weight (  success | info | warning | danger )
26990  * @cfg {String} faicon font-awesomeicon
26991  * 
26992  * @constructor
26993  * Create a new alert
26994  * @param {Object} config The config object
26995  */
26996
26997
26998 Roo.bootstrap.Alert = function(config){
26999     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27000     
27001 };
27002
27003 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27004     
27005     title: '',
27006     html: '',
27007     weight: false,
27008     faicon: false,
27009     
27010     getAutoCreate : function()
27011     {
27012         
27013         var cfg = {
27014             tag : 'div',
27015             cls : 'alert',
27016             cn : [
27017                 {
27018                     tag : 'i',
27019                     cls : 'roo-alert-icon'
27020                     
27021                 },
27022                 {
27023                     tag : 'b',
27024                     cls : 'roo-alert-title',
27025                     html : this.title
27026                 },
27027                 {
27028                     tag : 'span',
27029                     cls : 'roo-alert-text',
27030                     html : this.html
27031                 }
27032             ]
27033         };
27034         
27035         if(this.faicon){
27036             cfg.cn[0].cls += ' fa ' + this.faicon;
27037         }
27038         
27039         if(this.weight){
27040             cfg.cls += ' alert-' + this.weight;
27041         }
27042         
27043         return cfg;
27044     },
27045     
27046     initEvents: function() 
27047     {
27048         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27049     },
27050     
27051     setTitle : function(str)
27052     {
27053         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27054     },
27055     
27056     setText : function(str)
27057     {
27058         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27059     },
27060     
27061     setWeight : function(weight)
27062     {
27063         if(this.weight){
27064             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27065         }
27066         
27067         this.weight = weight;
27068         
27069         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27070     },
27071     
27072     setIcon : function(icon)
27073     {
27074         if(this.faicon){
27075             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27076         }
27077         
27078         this.faicon = icon;
27079         
27080         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27081     },
27082     
27083     hide: function() 
27084     {
27085         this.el.hide();   
27086     },
27087     
27088     show: function() 
27089     {  
27090         this.el.show();   
27091     }
27092     
27093 });
27094
27095  
27096 /*
27097 * Licence: LGPL
27098 */
27099
27100 /**
27101  * @class Roo.bootstrap.UploadCropbox
27102  * @extends Roo.bootstrap.Component
27103  * Bootstrap UploadCropbox class
27104  * @cfg {String} emptyText show when image has been loaded
27105  * @cfg {String} rotateNotify show when image too small to rotate
27106  * @cfg {Number} errorTimeout default 3000
27107  * @cfg {Number} minWidth default 300
27108  * @cfg {Number} minHeight default 300
27109  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27110  * @cfg {Boolean} isDocument (true|false) default false
27111  * @cfg {String} url action url
27112  * @cfg {String} paramName default 'imageUpload'
27113  * @cfg {String} method default POST
27114  * @cfg {Boolean} loadMask (true|false) default true
27115  * @cfg {Boolean} loadingText default 'Loading...'
27116  * 
27117  * @constructor
27118  * Create a new UploadCropbox
27119  * @param {Object} config The config object
27120  */
27121
27122 Roo.bootstrap.UploadCropbox = function(config){
27123     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27124     
27125     this.addEvents({
27126         /**
27127          * @event beforeselectfile
27128          * Fire before select file
27129          * @param {Roo.bootstrap.UploadCropbox} this
27130          */
27131         "beforeselectfile" : true,
27132         /**
27133          * @event initial
27134          * Fire after initEvent
27135          * @param {Roo.bootstrap.UploadCropbox} this
27136          */
27137         "initial" : true,
27138         /**
27139          * @event crop
27140          * Fire after initEvent
27141          * @param {Roo.bootstrap.UploadCropbox} this
27142          * @param {String} data
27143          */
27144         "crop" : true,
27145         /**
27146          * @event prepare
27147          * Fire when preparing the file data
27148          * @param {Roo.bootstrap.UploadCropbox} this
27149          * @param {Object} file
27150          */
27151         "prepare" : true,
27152         /**
27153          * @event exception
27154          * Fire when get exception
27155          * @param {Roo.bootstrap.UploadCropbox} this
27156          * @param {XMLHttpRequest} xhr
27157          */
27158         "exception" : true,
27159         /**
27160          * @event beforeloadcanvas
27161          * Fire before load the canvas
27162          * @param {Roo.bootstrap.UploadCropbox} this
27163          * @param {String} src
27164          */
27165         "beforeloadcanvas" : true,
27166         /**
27167          * @event trash
27168          * Fire when trash image
27169          * @param {Roo.bootstrap.UploadCropbox} this
27170          */
27171         "trash" : true,
27172         /**
27173          * @event download
27174          * Fire when download the image
27175          * @param {Roo.bootstrap.UploadCropbox} this
27176          */
27177         "download" : true,
27178         /**
27179          * @event footerbuttonclick
27180          * Fire when footerbuttonclick
27181          * @param {Roo.bootstrap.UploadCropbox} this
27182          * @param {String} type
27183          */
27184         "footerbuttonclick" : true,
27185         /**
27186          * @event resize
27187          * Fire when resize
27188          * @param {Roo.bootstrap.UploadCropbox} this
27189          */
27190         "resize" : true,
27191         /**
27192          * @event rotate
27193          * Fire when rotate the image
27194          * @param {Roo.bootstrap.UploadCropbox} this
27195          * @param {String} pos
27196          */
27197         "rotate" : true,
27198         /**
27199          * @event inspect
27200          * Fire when inspect the file
27201          * @param {Roo.bootstrap.UploadCropbox} this
27202          * @param {Object} file
27203          */
27204         "inspect" : true,
27205         /**
27206          * @event upload
27207          * Fire when xhr upload the file
27208          * @param {Roo.bootstrap.UploadCropbox} this
27209          * @param {Object} data
27210          */
27211         "upload" : true,
27212         /**
27213          * @event arrange
27214          * Fire when arrange the file data
27215          * @param {Roo.bootstrap.UploadCropbox} this
27216          * @param {Object} formData
27217          */
27218         "arrange" : true
27219     });
27220     
27221     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27222 };
27223
27224 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27225     
27226     emptyText : 'Click to upload image',
27227     rotateNotify : 'Image is too small to rotate',
27228     errorTimeout : 3000,
27229     scale : 0,
27230     baseScale : 1,
27231     rotate : 0,
27232     dragable : false,
27233     pinching : false,
27234     mouseX : 0,
27235     mouseY : 0,
27236     cropData : false,
27237     minWidth : 300,
27238     minHeight : 300,
27239     file : false,
27240     exif : {},
27241     baseRotate : 1,
27242     cropType : 'image/jpeg',
27243     buttons : false,
27244     canvasLoaded : false,
27245     isDocument : false,
27246     method : 'POST',
27247     paramName : 'imageUpload',
27248     loadMask : true,
27249     loadingText : 'Loading...',
27250     maskEl : false,
27251     
27252     getAutoCreate : function()
27253     {
27254         var cfg = {
27255             tag : 'div',
27256             cls : 'roo-upload-cropbox',
27257             cn : [
27258                 {
27259                     tag : 'input',
27260                     cls : 'roo-upload-cropbox-selector',
27261                     type : 'file'
27262                 },
27263                 {
27264                     tag : 'div',
27265                     cls : 'roo-upload-cropbox-body',
27266                     style : 'cursor:pointer',
27267                     cn : [
27268                         {
27269                             tag : 'div',
27270                             cls : 'roo-upload-cropbox-preview'
27271                         },
27272                         {
27273                             tag : 'div',
27274                             cls : 'roo-upload-cropbox-thumb'
27275                         },
27276                         {
27277                             tag : 'div',
27278                             cls : 'roo-upload-cropbox-empty-notify',
27279                             html : this.emptyText
27280                         },
27281                         {
27282                             tag : 'div',
27283                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27284                             html : this.rotateNotify
27285                         }
27286                     ]
27287                 },
27288                 {
27289                     tag : 'div',
27290                     cls : 'roo-upload-cropbox-footer',
27291                     cn : {
27292                         tag : 'div',
27293                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27294                         cn : []
27295                     }
27296                 }
27297             ]
27298         };
27299         
27300         return cfg;
27301     },
27302     
27303     onRender : function(ct, position)
27304     {
27305         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27306         
27307         if (this.buttons.length) {
27308             
27309             Roo.each(this.buttons, function(bb) {
27310                 
27311                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27312                 
27313                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27314                 
27315             }, this);
27316         }
27317         
27318         if(this.loadMask){
27319             this.maskEl = this.el;
27320         }
27321     },
27322     
27323     initEvents : function()
27324     {
27325         this.urlAPI = (window.createObjectURL && window) || 
27326                                 (window.URL && URL.revokeObjectURL && URL) || 
27327                                 (window.webkitURL && webkitURL);
27328                         
27329         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27330         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27331         
27332         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27333         this.selectorEl.hide();
27334         
27335         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27336         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27337         
27338         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27339         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27340         this.thumbEl.hide();
27341         
27342         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27343         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27344         
27345         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27346         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27347         this.errorEl.hide();
27348         
27349         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27350         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27351         this.footerEl.hide();
27352         
27353         this.setThumbBoxSize();
27354         
27355         this.bind();
27356         
27357         this.resize();
27358         
27359         this.fireEvent('initial', this);
27360     },
27361
27362     bind : function()
27363     {
27364         var _this = this;
27365         
27366         window.addEventListener("resize", function() { _this.resize(); } );
27367         
27368         this.bodyEl.on('click', this.beforeSelectFile, this);
27369         
27370         if(Roo.isTouch){
27371             this.bodyEl.on('touchstart', this.onTouchStart, this);
27372             this.bodyEl.on('touchmove', this.onTouchMove, this);
27373             this.bodyEl.on('touchend', this.onTouchEnd, this);
27374         }
27375         
27376         if(!Roo.isTouch){
27377             this.bodyEl.on('mousedown', this.onMouseDown, this);
27378             this.bodyEl.on('mousemove', this.onMouseMove, this);
27379             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27380             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27381             Roo.get(document).on('mouseup', this.onMouseUp, this);
27382         }
27383         
27384         this.selectorEl.on('change', this.onFileSelected, this);
27385     },
27386     
27387     reset : function()
27388     {    
27389         this.scale = 0;
27390         this.baseScale = 1;
27391         this.rotate = 0;
27392         this.baseRotate = 1;
27393         this.dragable = false;
27394         this.pinching = false;
27395         this.mouseX = 0;
27396         this.mouseY = 0;
27397         this.cropData = false;
27398         this.notifyEl.dom.innerHTML = this.emptyText;
27399         
27400         this.selectorEl.dom.value = '';
27401         
27402     },
27403     
27404     resize : function()
27405     {
27406         if(this.fireEvent('resize', this) != false){
27407             this.setThumbBoxPosition();
27408             this.setCanvasPosition();
27409         }
27410     },
27411     
27412     onFooterButtonClick : function(e, el, o, type)
27413     {
27414         switch (type) {
27415             case 'rotate-left' :
27416                 this.onRotateLeft(e);
27417                 break;
27418             case 'rotate-right' :
27419                 this.onRotateRight(e);
27420                 break;
27421             case 'picture' :
27422                 this.beforeSelectFile(e);
27423                 break;
27424             case 'trash' :
27425                 this.trash(e);
27426                 break;
27427             case 'crop' :
27428                 this.crop(e);
27429                 break;
27430             case 'download' :
27431                 this.download(e);
27432                 break;
27433             default :
27434                 break;
27435         }
27436         
27437         this.fireEvent('footerbuttonclick', this, type);
27438     },
27439     
27440     beforeSelectFile : function(e)
27441     {
27442         e.preventDefault();
27443         
27444         if(this.fireEvent('beforeselectfile', this) != false){
27445             this.selectorEl.dom.click();
27446         }
27447     },
27448     
27449     onFileSelected : function(e)
27450     {
27451         e.preventDefault();
27452         
27453         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27454             return;
27455         }
27456         
27457         var file = this.selectorEl.dom.files[0];
27458         
27459         if(this.fireEvent('inspect', this, file) != false){
27460             this.prepare(file);
27461         }
27462         
27463     },
27464     
27465     trash : function(e)
27466     {
27467         this.fireEvent('trash', this);
27468     },
27469     
27470     download : function(e)
27471     {
27472         this.fireEvent('download', this);
27473     },
27474     
27475     loadCanvas : function(src)
27476     {   
27477         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27478             
27479             this.reset();
27480             
27481             this.imageEl = document.createElement('img');
27482             
27483             var _this = this;
27484             
27485             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27486             
27487             this.imageEl.src = src;
27488         }
27489     },
27490     
27491     onLoadCanvas : function()
27492     {   
27493         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27494         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27495         
27496         this.bodyEl.un('click', this.beforeSelectFile, this);
27497         
27498         this.notifyEl.hide();
27499         this.thumbEl.show();
27500         this.footerEl.show();
27501         
27502         this.baseRotateLevel();
27503         
27504         if(this.isDocument){
27505             this.setThumbBoxSize();
27506         }
27507         
27508         this.setThumbBoxPosition();
27509         
27510         this.baseScaleLevel();
27511         
27512         this.draw();
27513         
27514         this.resize();
27515         
27516         this.canvasLoaded = true;
27517         
27518         if(this.loadMask){
27519             this.maskEl.unmask();
27520         }
27521         
27522     },
27523     
27524     setCanvasPosition : function()
27525     {   
27526         if(!this.canvasEl){
27527             return;
27528         }
27529         
27530         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27531         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27532         
27533         this.previewEl.setLeft(pw);
27534         this.previewEl.setTop(ph);
27535         
27536     },
27537     
27538     onMouseDown : function(e)
27539     {   
27540         e.stopEvent();
27541         
27542         this.dragable = true;
27543         this.pinching = false;
27544         
27545         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27546             this.dragable = false;
27547             return;
27548         }
27549         
27550         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27551         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27552         
27553     },
27554     
27555     onMouseMove : function(e)
27556     {   
27557         e.stopEvent();
27558         
27559         if(!this.canvasLoaded){
27560             return;
27561         }
27562         
27563         if (!this.dragable){
27564             return;
27565         }
27566         
27567         var minX = Math.ceil(this.thumbEl.getLeft(true));
27568         var minY = Math.ceil(this.thumbEl.getTop(true));
27569         
27570         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27571         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27572         
27573         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27574         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27575         
27576         x = x - this.mouseX;
27577         y = y - this.mouseY;
27578         
27579         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27580         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27581         
27582         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27583         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27584         
27585         this.previewEl.setLeft(bgX);
27586         this.previewEl.setTop(bgY);
27587         
27588         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27589         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27590     },
27591     
27592     onMouseUp : function(e)
27593     {   
27594         e.stopEvent();
27595         
27596         this.dragable = false;
27597     },
27598     
27599     onMouseWheel : function(e)
27600     {   
27601         e.stopEvent();
27602         
27603         this.startScale = this.scale;
27604         
27605         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27606         
27607         if(!this.zoomable()){
27608             this.scale = this.startScale;
27609             return;
27610         }
27611         
27612         this.draw();
27613         
27614         return;
27615     },
27616     
27617     zoomable : function()
27618     {
27619         var minScale = this.thumbEl.getWidth() / this.minWidth;
27620         
27621         if(this.minWidth < this.minHeight){
27622             minScale = this.thumbEl.getHeight() / this.minHeight;
27623         }
27624         
27625         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27626         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27627         
27628         if(
27629                 this.isDocument &&
27630                 (this.rotate == 0 || this.rotate == 180) && 
27631                 (
27632                     width > this.imageEl.OriginWidth || 
27633                     height > this.imageEl.OriginHeight ||
27634                     (width < this.minWidth && height < this.minHeight)
27635                 )
27636         ){
27637             return false;
27638         }
27639         
27640         if(
27641                 this.isDocument &&
27642                 (this.rotate == 90 || this.rotate == 270) && 
27643                 (
27644                     width > this.imageEl.OriginWidth || 
27645                     height > this.imageEl.OriginHeight ||
27646                     (width < this.minHeight && height < this.minWidth)
27647                 )
27648         ){
27649             return false;
27650         }
27651         
27652         if(
27653                 !this.isDocument &&
27654                 (this.rotate == 0 || this.rotate == 180) && 
27655                 (
27656                     width < this.minWidth || 
27657                     width > this.imageEl.OriginWidth || 
27658                     height < this.minHeight || 
27659                     height > this.imageEl.OriginHeight
27660                 )
27661         ){
27662             return false;
27663         }
27664         
27665         if(
27666                 !this.isDocument &&
27667                 (this.rotate == 90 || this.rotate == 270) && 
27668                 (
27669                     width < this.minHeight || 
27670                     width > this.imageEl.OriginWidth || 
27671                     height < this.minWidth || 
27672                     height > this.imageEl.OriginHeight
27673                 )
27674         ){
27675             return false;
27676         }
27677         
27678         return true;
27679         
27680     },
27681     
27682     onRotateLeft : function(e)
27683     {   
27684         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27685             
27686             var minScale = this.thumbEl.getWidth() / this.minWidth;
27687             
27688             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27689             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27690             
27691             this.startScale = this.scale;
27692             
27693             while (this.getScaleLevel() < minScale){
27694             
27695                 this.scale = this.scale + 1;
27696                 
27697                 if(!this.zoomable()){
27698                     break;
27699                 }
27700                 
27701                 if(
27702                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27703                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27704                 ){
27705                     continue;
27706                 }
27707                 
27708                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27709
27710                 this.draw();
27711                 
27712                 return;
27713             }
27714             
27715             this.scale = this.startScale;
27716             
27717             this.onRotateFail();
27718             
27719             return false;
27720         }
27721         
27722         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27723
27724         if(this.isDocument){
27725             this.setThumbBoxSize();
27726             this.setThumbBoxPosition();
27727             this.setCanvasPosition();
27728         }
27729         
27730         this.draw();
27731         
27732         this.fireEvent('rotate', this, 'left');
27733         
27734     },
27735     
27736     onRotateRight : function(e)
27737     {
27738         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27739             
27740             var minScale = this.thumbEl.getWidth() / this.minWidth;
27741         
27742             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27743             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27744             
27745             this.startScale = this.scale;
27746             
27747             while (this.getScaleLevel() < minScale){
27748             
27749                 this.scale = this.scale + 1;
27750                 
27751                 if(!this.zoomable()){
27752                     break;
27753                 }
27754                 
27755                 if(
27756                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27757                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27758                 ){
27759                     continue;
27760                 }
27761                 
27762                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27763
27764                 this.draw();
27765                 
27766                 return;
27767             }
27768             
27769             this.scale = this.startScale;
27770             
27771             this.onRotateFail();
27772             
27773             return false;
27774         }
27775         
27776         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27777
27778         if(this.isDocument){
27779             this.setThumbBoxSize();
27780             this.setThumbBoxPosition();
27781             this.setCanvasPosition();
27782         }
27783         
27784         this.draw();
27785         
27786         this.fireEvent('rotate', this, 'right');
27787     },
27788     
27789     onRotateFail : function()
27790     {
27791         this.errorEl.show(true);
27792         
27793         var _this = this;
27794         
27795         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27796     },
27797     
27798     draw : function()
27799     {
27800         this.previewEl.dom.innerHTML = '';
27801         
27802         var canvasEl = document.createElement("canvas");
27803         
27804         var contextEl = canvasEl.getContext("2d");
27805         
27806         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27807         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27808         var center = this.imageEl.OriginWidth / 2;
27809         
27810         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27811             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27812             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27813             center = this.imageEl.OriginHeight / 2;
27814         }
27815         
27816         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27817         
27818         contextEl.translate(center, center);
27819         contextEl.rotate(this.rotate * Math.PI / 180);
27820
27821         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27822         
27823         this.canvasEl = document.createElement("canvas");
27824         
27825         this.contextEl = this.canvasEl.getContext("2d");
27826         
27827         switch (this.rotate) {
27828             case 0 :
27829                 
27830                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27831                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27832                 
27833                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27834                 
27835                 break;
27836             case 90 : 
27837                 
27838                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27839                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27840                 
27841                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27842                     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);
27843                     break;
27844                 }
27845                 
27846                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27847                 
27848                 break;
27849             case 180 :
27850                 
27851                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27852                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27853                 
27854                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27855                     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);
27856                     break;
27857                 }
27858                 
27859                 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);
27860                 
27861                 break;
27862             case 270 :
27863                 
27864                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27865                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27866         
27867                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27868                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27869                     break;
27870                 }
27871                 
27872                 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);
27873                 
27874                 break;
27875             default : 
27876                 break;
27877         }
27878         
27879         this.previewEl.appendChild(this.canvasEl);
27880         
27881         this.setCanvasPosition();
27882     },
27883     
27884     crop : function()
27885     {
27886         if(!this.canvasLoaded){
27887             return;
27888         }
27889         
27890         var imageCanvas = document.createElement("canvas");
27891         
27892         var imageContext = imageCanvas.getContext("2d");
27893         
27894         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27895         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27896         
27897         var center = imageCanvas.width / 2;
27898         
27899         imageContext.translate(center, center);
27900         
27901         imageContext.rotate(this.rotate * Math.PI / 180);
27902         
27903         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27904         
27905         var canvas = document.createElement("canvas");
27906         
27907         var context = canvas.getContext("2d");
27908                 
27909         canvas.width = this.minWidth;
27910         canvas.height = this.minHeight;
27911
27912         switch (this.rotate) {
27913             case 0 :
27914                 
27915                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27916                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27917                 
27918                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27919                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27920                 
27921                 var targetWidth = this.minWidth - 2 * x;
27922                 var targetHeight = this.minHeight - 2 * y;
27923                 
27924                 var scale = 1;
27925                 
27926                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27927                     scale = targetWidth / width;
27928                 }
27929                 
27930                 if(x > 0 && y == 0){
27931                     scale = targetHeight / height;
27932                 }
27933                 
27934                 if(x > 0 && y > 0){
27935                     scale = targetWidth / width;
27936                     
27937                     if(width < height){
27938                         scale = targetHeight / height;
27939                     }
27940                 }
27941                 
27942                 context.scale(scale, scale);
27943                 
27944                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27945                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27946
27947                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27948                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27949
27950                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27951                 
27952                 break;
27953             case 90 : 
27954                 
27955                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27956                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27957                 
27958                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27959                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27960                 
27961                 var targetWidth = this.minWidth - 2 * x;
27962                 var targetHeight = this.minHeight - 2 * y;
27963                 
27964                 var scale = 1;
27965                 
27966                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27967                     scale = targetWidth / width;
27968                 }
27969                 
27970                 if(x > 0 && y == 0){
27971                     scale = targetHeight / height;
27972                 }
27973                 
27974                 if(x > 0 && y > 0){
27975                     scale = targetWidth / width;
27976                     
27977                     if(width < height){
27978                         scale = targetHeight / height;
27979                     }
27980                 }
27981                 
27982                 context.scale(scale, scale);
27983                 
27984                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27985                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27986
27987                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27988                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27989                 
27990                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27991                 
27992                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27993                 
27994                 break;
27995             case 180 :
27996                 
27997                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27998                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27999                 
28000                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28001                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28002                 
28003                 var targetWidth = this.minWidth - 2 * x;
28004                 var targetHeight = this.minHeight - 2 * y;
28005                 
28006                 var scale = 1;
28007                 
28008                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28009                     scale = targetWidth / width;
28010                 }
28011                 
28012                 if(x > 0 && y == 0){
28013                     scale = targetHeight / height;
28014                 }
28015                 
28016                 if(x > 0 && y > 0){
28017                     scale = targetWidth / width;
28018                     
28019                     if(width < height){
28020                         scale = targetHeight / height;
28021                     }
28022                 }
28023                 
28024                 context.scale(scale, scale);
28025                 
28026                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28027                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28028
28029                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28030                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28031
28032                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28033                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28034                 
28035                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28036                 
28037                 break;
28038             case 270 :
28039                 
28040                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28041                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28042                 
28043                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28044                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28045                 
28046                 var targetWidth = this.minWidth - 2 * x;
28047                 var targetHeight = this.minHeight - 2 * y;
28048                 
28049                 var scale = 1;
28050                 
28051                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28052                     scale = targetWidth / width;
28053                 }
28054                 
28055                 if(x > 0 && y == 0){
28056                     scale = targetHeight / height;
28057                 }
28058                 
28059                 if(x > 0 && y > 0){
28060                     scale = targetWidth / width;
28061                     
28062                     if(width < height){
28063                         scale = targetHeight / height;
28064                     }
28065                 }
28066                 
28067                 context.scale(scale, scale);
28068                 
28069                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28070                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28071
28072                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28073                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28074                 
28075                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28076                 
28077                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28078                 
28079                 break;
28080             default : 
28081                 break;
28082         }
28083         
28084         this.cropData = canvas.toDataURL(this.cropType);
28085         
28086         if(this.fireEvent('crop', this, this.cropData) !== false){
28087             this.process(this.file, this.cropData);
28088         }
28089         
28090         return;
28091         
28092     },
28093     
28094     setThumbBoxSize : function()
28095     {
28096         var width, height;
28097         
28098         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28099             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28100             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28101             
28102             this.minWidth = width;
28103             this.minHeight = height;
28104             
28105             if(this.rotate == 90 || this.rotate == 270){
28106                 this.minWidth = height;
28107                 this.minHeight = width;
28108             }
28109         }
28110         
28111         height = 300;
28112         width = Math.ceil(this.minWidth * height / this.minHeight);
28113         
28114         if(this.minWidth > this.minHeight){
28115             width = 300;
28116             height = Math.ceil(this.minHeight * width / this.minWidth);
28117         }
28118         
28119         this.thumbEl.setStyle({
28120             width : width + 'px',
28121             height : height + 'px'
28122         });
28123
28124         return;
28125             
28126     },
28127     
28128     setThumbBoxPosition : function()
28129     {
28130         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28131         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28132         
28133         this.thumbEl.setLeft(x);
28134         this.thumbEl.setTop(y);
28135         
28136     },
28137     
28138     baseRotateLevel : function()
28139     {
28140         this.baseRotate = 1;
28141         
28142         if(
28143                 typeof(this.exif) != 'undefined' &&
28144                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28145                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28146         ){
28147             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28148         }
28149         
28150         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28151         
28152     },
28153     
28154     baseScaleLevel : function()
28155     {
28156         var width, height;
28157         
28158         if(this.isDocument){
28159             
28160             if(this.baseRotate == 6 || this.baseRotate == 8){
28161             
28162                 height = this.thumbEl.getHeight();
28163                 this.baseScale = height / this.imageEl.OriginWidth;
28164
28165                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28166                     width = this.thumbEl.getWidth();
28167                     this.baseScale = width / this.imageEl.OriginHeight;
28168                 }
28169
28170                 return;
28171             }
28172
28173             height = this.thumbEl.getHeight();
28174             this.baseScale = height / this.imageEl.OriginHeight;
28175
28176             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28177                 width = this.thumbEl.getWidth();
28178                 this.baseScale = width / this.imageEl.OriginWidth;
28179             }
28180
28181             return;
28182         }
28183         
28184         if(this.baseRotate == 6 || this.baseRotate == 8){
28185             
28186             width = this.thumbEl.getHeight();
28187             this.baseScale = width / this.imageEl.OriginHeight;
28188             
28189             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28190                 height = this.thumbEl.getWidth();
28191                 this.baseScale = height / this.imageEl.OriginHeight;
28192             }
28193             
28194             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28195                 height = this.thumbEl.getWidth();
28196                 this.baseScale = height / this.imageEl.OriginHeight;
28197                 
28198                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28199                     width = this.thumbEl.getHeight();
28200                     this.baseScale = width / this.imageEl.OriginWidth;
28201                 }
28202             }
28203             
28204             return;
28205         }
28206         
28207         width = this.thumbEl.getWidth();
28208         this.baseScale = width / this.imageEl.OriginWidth;
28209         
28210         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28211             height = this.thumbEl.getHeight();
28212             this.baseScale = height / this.imageEl.OriginHeight;
28213         }
28214         
28215         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28216             
28217             height = this.thumbEl.getHeight();
28218             this.baseScale = height / this.imageEl.OriginHeight;
28219             
28220             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28221                 width = this.thumbEl.getWidth();
28222                 this.baseScale = width / this.imageEl.OriginWidth;
28223             }
28224             
28225         }
28226         
28227         return;
28228     },
28229     
28230     getScaleLevel : function()
28231     {
28232         return this.baseScale * Math.pow(1.1, this.scale);
28233     },
28234     
28235     onTouchStart : function(e)
28236     {
28237         if(!this.canvasLoaded){
28238             this.beforeSelectFile(e);
28239             return;
28240         }
28241         
28242         var touches = e.browserEvent.touches;
28243         
28244         if(!touches){
28245             return;
28246         }
28247         
28248         if(touches.length == 1){
28249             this.onMouseDown(e);
28250             return;
28251         }
28252         
28253         if(touches.length != 2){
28254             return;
28255         }
28256         
28257         var coords = [];
28258         
28259         for(var i = 0, finger; finger = touches[i]; i++){
28260             coords.push(finger.pageX, finger.pageY);
28261         }
28262         
28263         var x = Math.pow(coords[0] - coords[2], 2);
28264         var y = Math.pow(coords[1] - coords[3], 2);
28265         
28266         this.startDistance = Math.sqrt(x + y);
28267         
28268         this.startScale = this.scale;
28269         
28270         this.pinching = true;
28271         this.dragable = false;
28272         
28273     },
28274     
28275     onTouchMove : function(e)
28276     {
28277         if(!this.pinching && !this.dragable){
28278             return;
28279         }
28280         
28281         var touches = e.browserEvent.touches;
28282         
28283         if(!touches){
28284             return;
28285         }
28286         
28287         if(this.dragable){
28288             this.onMouseMove(e);
28289             return;
28290         }
28291         
28292         var coords = [];
28293         
28294         for(var i = 0, finger; finger = touches[i]; i++){
28295             coords.push(finger.pageX, finger.pageY);
28296         }
28297         
28298         var x = Math.pow(coords[0] - coords[2], 2);
28299         var y = Math.pow(coords[1] - coords[3], 2);
28300         
28301         this.endDistance = Math.sqrt(x + y);
28302         
28303         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28304         
28305         if(!this.zoomable()){
28306             this.scale = this.startScale;
28307             return;
28308         }
28309         
28310         this.draw();
28311         
28312     },
28313     
28314     onTouchEnd : function(e)
28315     {
28316         this.pinching = false;
28317         this.dragable = false;
28318         
28319     },
28320     
28321     process : function(file, crop)
28322     {
28323         if(this.loadMask){
28324             this.maskEl.mask(this.loadingText);
28325         }
28326         
28327         this.xhr = new XMLHttpRequest();
28328         
28329         file.xhr = this.xhr;
28330
28331         this.xhr.open(this.method, this.url, true);
28332         
28333         var headers = {
28334             "Accept": "application/json",
28335             "Cache-Control": "no-cache",
28336             "X-Requested-With": "XMLHttpRequest"
28337         };
28338         
28339         for (var headerName in headers) {
28340             var headerValue = headers[headerName];
28341             if (headerValue) {
28342                 this.xhr.setRequestHeader(headerName, headerValue);
28343             }
28344         }
28345         
28346         var _this = this;
28347         
28348         this.xhr.onload = function()
28349         {
28350             _this.xhrOnLoad(_this.xhr);
28351         }
28352         
28353         this.xhr.onerror = function()
28354         {
28355             _this.xhrOnError(_this.xhr);
28356         }
28357         
28358         var formData = new FormData();
28359
28360         formData.append('returnHTML', 'NO');
28361         
28362         if(crop){
28363             formData.append('crop', crop);
28364         }
28365         
28366         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28367             formData.append(this.paramName, file, file.name);
28368         }
28369         
28370         if(typeof(file.filename) != 'undefined'){
28371             formData.append('filename', file.filename);
28372         }
28373         
28374         if(typeof(file.mimetype) != 'undefined'){
28375             formData.append('mimetype', file.mimetype);
28376         }
28377         
28378         if(this.fireEvent('arrange', this, formData) != false){
28379             this.xhr.send(formData);
28380         };
28381     },
28382     
28383     xhrOnLoad : function(xhr)
28384     {
28385         if(this.loadMask){
28386             this.maskEl.unmask();
28387         }
28388         
28389         if (xhr.readyState !== 4) {
28390             this.fireEvent('exception', this, xhr);
28391             return;
28392         }
28393
28394         var response = Roo.decode(xhr.responseText);
28395         
28396         if(!response.success){
28397             this.fireEvent('exception', this, xhr);
28398             return;
28399         }
28400         
28401         var response = Roo.decode(xhr.responseText);
28402         
28403         this.fireEvent('upload', this, response);
28404         
28405     },
28406     
28407     xhrOnError : function()
28408     {
28409         if(this.loadMask){
28410             this.maskEl.unmask();
28411         }
28412         
28413         Roo.log('xhr on error');
28414         
28415         var response = Roo.decode(xhr.responseText);
28416           
28417         Roo.log(response);
28418         
28419     },
28420     
28421     prepare : function(file)
28422     {   
28423         if(this.loadMask){
28424             this.maskEl.mask(this.loadingText);
28425         }
28426         
28427         this.file = false;
28428         this.exif = {};
28429         
28430         if(typeof(file) === 'string'){
28431             this.loadCanvas(file);
28432             return;
28433         }
28434         
28435         if(!file || !this.urlAPI){
28436             return;
28437         }
28438         
28439         this.file = file;
28440         this.cropType = file.type;
28441         
28442         var _this = this;
28443         
28444         if(this.fireEvent('prepare', this, this.file) != false){
28445             
28446             var reader = new FileReader();
28447             
28448             reader.onload = function (e) {
28449                 if (e.target.error) {
28450                     Roo.log(e.target.error);
28451                     return;
28452                 }
28453                 
28454                 var buffer = e.target.result,
28455                     dataView = new DataView(buffer),
28456                     offset = 2,
28457                     maxOffset = dataView.byteLength - 4,
28458                     markerBytes,
28459                     markerLength;
28460                 
28461                 if (dataView.getUint16(0) === 0xffd8) {
28462                     while (offset < maxOffset) {
28463                         markerBytes = dataView.getUint16(offset);
28464                         
28465                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28466                             markerLength = dataView.getUint16(offset + 2) + 2;
28467                             if (offset + markerLength > dataView.byteLength) {
28468                                 Roo.log('Invalid meta data: Invalid segment size.');
28469                                 break;
28470                             }
28471                             
28472                             if(markerBytes == 0xffe1){
28473                                 _this.parseExifData(
28474                                     dataView,
28475                                     offset,
28476                                     markerLength
28477                                 );
28478                             }
28479                             
28480                             offset += markerLength;
28481                             
28482                             continue;
28483                         }
28484                         
28485                         break;
28486                     }
28487                     
28488                 }
28489                 
28490                 var url = _this.urlAPI.createObjectURL(_this.file);
28491                 
28492                 _this.loadCanvas(url);
28493                 
28494                 return;
28495             }
28496             
28497             reader.readAsArrayBuffer(this.file);
28498             
28499         }
28500         
28501     },
28502     
28503     parseExifData : function(dataView, offset, length)
28504     {
28505         var tiffOffset = offset + 10,
28506             littleEndian,
28507             dirOffset;
28508     
28509         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28510             // No Exif data, might be XMP data instead
28511             return;
28512         }
28513         
28514         // Check for the ASCII code for "Exif" (0x45786966):
28515         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28516             // No Exif data, might be XMP data instead
28517             return;
28518         }
28519         if (tiffOffset + 8 > dataView.byteLength) {
28520             Roo.log('Invalid Exif data: Invalid segment size.');
28521             return;
28522         }
28523         // Check for the two null bytes:
28524         if (dataView.getUint16(offset + 8) !== 0x0000) {
28525             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28526             return;
28527         }
28528         // Check the byte alignment:
28529         switch (dataView.getUint16(tiffOffset)) {
28530         case 0x4949:
28531             littleEndian = true;
28532             break;
28533         case 0x4D4D:
28534             littleEndian = false;
28535             break;
28536         default:
28537             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28538             return;
28539         }
28540         // Check for the TIFF tag marker (0x002A):
28541         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28542             Roo.log('Invalid Exif data: Missing TIFF marker.');
28543             return;
28544         }
28545         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28546         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28547         
28548         this.parseExifTags(
28549             dataView,
28550             tiffOffset,
28551             tiffOffset + dirOffset,
28552             littleEndian
28553         );
28554     },
28555     
28556     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28557     {
28558         var tagsNumber,
28559             dirEndOffset,
28560             i;
28561         if (dirOffset + 6 > dataView.byteLength) {
28562             Roo.log('Invalid Exif data: Invalid directory offset.');
28563             return;
28564         }
28565         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28566         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28567         if (dirEndOffset + 4 > dataView.byteLength) {
28568             Roo.log('Invalid Exif data: Invalid directory size.');
28569             return;
28570         }
28571         for (i = 0; i < tagsNumber; i += 1) {
28572             this.parseExifTag(
28573                 dataView,
28574                 tiffOffset,
28575                 dirOffset + 2 + 12 * i, // tag offset
28576                 littleEndian
28577             );
28578         }
28579         // Return the offset to the next directory:
28580         return dataView.getUint32(dirEndOffset, littleEndian);
28581     },
28582     
28583     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28584     {
28585         var tag = dataView.getUint16(offset, littleEndian);
28586         
28587         this.exif[tag] = this.getExifValue(
28588             dataView,
28589             tiffOffset,
28590             offset,
28591             dataView.getUint16(offset + 2, littleEndian), // tag type
28592             dataView.getUint32(offset + 4, littleEndian), // tag length
28593             littleEndian
28594         );
28595     },
28596     
28597     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28598     {
28599         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28600             tagSize,
28601             dataOffset,
28602             values,
28603             i,
28604             str,
28605             c;
28606     
28607         if (!tagType) {
28608             Roo.log('Invalid Exif data: Invalid tag type.');
28609             return;
28610         }
28611         
28612         tagSize = tagType.size * length;
28613         // Determine if the value is contained in the dataOffset bytes,
28614         // or if the value at the dataOffset is a pointer to the actual data:
28615         dataOffset = tagSize > 4 ?
28616                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28617         if (dataOffset + tagSize > dataView.byteLength) {
28618             Roo.log('Invalid Exif data: Invalid data offset.');
28619             return;
28620         }
28621         if (length === 1) {
28622             return tagType.getValue(dataView, dataOffset, littleEndian);
28623         }
28624         values = [];
28625         for (i = 0; i < length; i += 1) {
28626             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28627         }
28628         
28629         if (tagType.ascii) {
28630             str = '';
28631             // Concatenate the chars:
28632             for (i = 0; i < values.length; i += 1) {
28633                 c = values[i];
28634                 // Ignore the terminating NULL byte(s):
28635                 if (c === '\u0000') {
28636                     break;
28637                 }
28638                 str += c;
28639             }
28640             return str;
28641         }
28642         return values;
28643     }
28644     
28645 });
28646
28647 Roo.apply(Roo.bootstrap.UploadCropbox, {
28648     tags : {
28649         'Orientation': 0x0112
28650     },
28651     
28652     Orientation: {
28653             1: 0, //'top-left',
28654 //            2: 'top-right',
28655             3: 180, //'bottom-right',
28656 //            4: 'bottom-left',
28657 //            5: 'left-top',
28658             6: 90, //'right-top',
28659 //            7: 'right-bottom',
28660             8: 270 //'left-bottom'
28661     },
28662     
28663     exifTagTypes : {
28664         // byte, 8-bit unsigned int:
28665         1: {
28666             getValue: function (dataView, dataOffset) {
28667                 return dataView.getUint8(dataOffset);
28668             },
28669             size: 1
28670         },
28671         // ascii, 8-bit byte:
28672         2: {
28673             getValue: function (dataView, dataOffset) {
28674                 return String.fromCharCode(dataView.getUint8(dataOffset));
28675             },
28676             size: 1,
28677             ascii: true
28678         },
28679         // short, 16 bit int:
28680         3: {
28681             getValue: function (dataView, dataOffset, littleEndian) {
28682                 return dataView.getUint16(dataOffset, littleEndian);
28683             },
28684             size: 2
28685         },
28686         // long, 32 bit int:
28687         4: {
28688             getValue: function (dataView, dataOffset, littleEndian) {
28689                 return dataView.getUint32(dataOffset, littleEndian);
28690             },
28691             size: 4
28692         },
28693         // rational = two long values, first is numerator, second is denominator:
28694         5: {
28695             getValue: function (dataView, dataOffset, littleEndian) {
28696                 return dataView.getUint32(dataOffset, littleEndian) /
28697                     dataView.getUint32(dataOffset + 4, littleEndian);
28698             },
28699             size: 8
28700         },
28701         // slong, 32 bit signed int:
28702         9: {
28703             getValue: function (dataView, dataOffset, littleEndian) {
28704                 return dataView.getInt32(dataOffset, littleEndian);
28705             },
28706             size: 4
28707         },
28708         // srational, two slongs, first is numerator, second is denominator:
28709         10: {
28710             getValue: function (dataView, dataOffset, littleEndian) {
28711                 return dataView.getInt32(dataOffset, littleEndian) /
28712                     dataView.getInt32(dataOffset + 4, littleEndian);
28713             },
28714             size: 8
28715         }
28716     },
28717     
28718     footer : {
28719         STANDARD : [
28720             {
28721                 tag : 'div',
28722                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28723                 action : 'rotate-left',
28724                 cn : [
28725                     {
28726                         tag : 'button',
28727                         cls : 'btn btn-default',
28728                         html : '<i class="fa fa-undo"></i>'
28729                     }
28730                 ]
28731             },
28732             {
28733                 tag : 'div',
28734                 cls : 'btn-group roo-upload-cropbox-picture',
28735                 action : 'picture',
28736                 cn : [
28737                     {
28738                         tag : 'button',
28739                         cls : 'btn btn-default',
28740                         html : '<i class="fa fa-picture-o"></i>'
28741                     }
28742                 ]
28743             },
28744             {
28745                 tag : 'div',
28746                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28747                 action : 'rotate-right',
28748                 cn : [
28749                     {
28750                         tag : 'button',
28751                         cls : 'btn btn-default',
28752                         html : '<i class="fa fa-repeat"></i>'
28753                     }
28754                 ]
28755             }
28756         ],
28757         DOCUMENT : [
28758             {
28759                 tag : 'div',
28760                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28761                 action : 'rotate-left',
28762                 cn : [
28763                     {
28764                         tag : 'button',
28765                         cls : 'btn btn-default',
28766                         html : '<i class="fa fa-undo"></i>'
28767                     }
28768                 ]
28769             },
28770             {
28771                 tag : 'div',
28772                 cls : 'btn-group roo-upload-cropbox-download',
28773                 action : 'download',
28774                 cn : [
28775                     {
28776                         tag : 'button',
28777                         cls : 'btn btn-default',
28778                         html : '<i class="fa fa-download"></i>'
28779                     }
28780                 ]
28781             },
28782             {
28783                 tag : 'div',
28784                 cls : 'btn-group roo-upload-cropbox-crop',
28785                 action : 'crop',
28786                 cn : [
28787                     {
28788                         tag : 'button',
28789                         cls : 'btn btn-default',
28790                         html : '<i class="fa fa-crop"></i>'
28791                     }
28792                 ]
28793             },
28794             {
28795                 tag : 'div',
28796                 cls : 'btn-group roo-upload-cropbox-trash',
28797                 action : 'trash',
28798                 cn : [
28799                     {
28800                         tag : 'button',
28801                         cls : 'btn btn-default',
28802                         html : '<i class="fa fa-trash"></i>'
28803                     }
28804                 ]
28805             },
28806             {
28807                 tag : 'div',
28808                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28809                 action : 'rotate-right',
28810                 cn : [
28811                     {
28812                         tag : 'button',
28813                         cls : 'btn btn-default',
28814                         html : '<i class="fa fa-repeat"></i>'
28815                     }
28816                 ]
28817             }
28818         ],
28819         ROTATOR : [
28820             {
28821                 tag : 'div',
28822                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28823                 action : 'rotate-left',
28824                 cn : [
28825                     {
28826                         tag : 'button',
28827                         cls : 'btn btn-default',
28828                         html : '<i class="fa fa-undo"></i>'
28829                     }
28830                 ]
28831             },
28832             {
28833                 tag : 'div',
28834                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28835                 action : 'rotate-right',
28836                 cn : [
28837                     {
28838                         tag : 'button',
28839                         cls : 'btn btn-default',
28840                         html : '<i class="fa fa-repeat"></i>'
28841                     }
28842                 ]
28843             }
28844         ]
28845     }
28846 });
28847
28848 /*
28849 * Licence: LGPL
28850 */
28851
28852 /**
28853  * @class Roo.bootstrap.DocumentManager
28854  * @extends Roo.bootstrap.Component
28855  * Bootstrap DocumentManager class
28856  * @cfg {String} paramName default 'imageUpload'
28857  * @cfg {String} toolTipName default 'filename'
28858  * @cfg {String} method default POST
28859  * @cfg {String} url action url
28860  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28861  * @cfg {Boolean} multiple multiple upload default true
28862  * @cfg {Number} thumbSize default 300
28863  * @cfg {String} fieldLabel
28864  * @cfg {Number} labelWidth default 4
28865  * @cfg {String} labelAlign (left|top) default left
28866  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28867 * @cfg {Number} labellg set the width of label (1-12)
28868  * @cfg {Number} labelmd set the width of label (1-12)
28869  * @cfg {Number} labelsm set the width of label (1-12)
28870  * @cfg {Number} labelxs set the width of label (1-12)
28871  * 
28872  * @constructor
28873  * Create a new DocumentManager
28874  * @param {Object} config The config object
28875  */
28876
28877 Roo.bootstrap.DocumentManager = function(config){
28878     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28879     
28880     this.files = [];
28881     this.delegates = [];
28882     
28883     this.addEvents({
28884         /**
28885          * @event initial
28886          * Fire when initial the DocumentManager
28887          * @param {Roo.bootstrap.DocumentManager} this
28888          */
28889         "initial" : true,
28890         /**
28891          * @event inspect
28892          * inspect selected file
28893          * @param {Roo.bootstrap.DocumentManager} this
28894          * @param {File} file
28895          */
28896         "inspect" : true,
28897         /**
28898          * @event exception
28899          * Fire when xhr load exception
28900          * @param {Roo.bootstrap.DocumentManager} this
28901          * @param {XMLHttpRequest} xhr
28902          */
28903         "exception" : true,
28904         /**
28905          * @event afterupload
28906          * Fire when xhr load exception
28907          * @param {Roo.bootstrap.DocumentManager} this
28908          * @param {XMLHttpRequest} xhr
28909          */
28910         "afterupload" : true,
28911         /**
28912          * @event prepare
28913          * prepare the form data
28914          * @param {Roo.bootstrap.DocumentManager} this
28915          * @param {Object} formData
28916          */
28917         "prepare" : true,
28918         /**
28919          * @event remove
28920          * Fire when remove the file
28921          * @param {Roo.bootstrap.DocumentManager} this
28922          * @param {Object} file
28923          */
28924         "remove" : true,
28925         /**
28926          * @event refresh
28927          * Fire after refresh the file
28928          * @param {Roo.bootstrap.DocumentManager} this
28929          */
28930         "refresh" : true,
28931         /**
28932          * @event click
28933          * Fire after click the image
28934          * @param {Roo.bootstrap.DocumentManager} this
28935          * @param {Object} file
28936          */
28937         "click" : true,
28938         /**
28939          * @event edit
28940          * Fire when upload a image and editable set to true
28941          * @param {Roo.bootstrap.DocumentManager} this
28942          * @param {Object} file
28943          */
28944         "edit" : true,
28945         /**
28946          * @event beforeselectfile
28947          * Fire before select file
28948          * @param {Roo.bootstrap.DocumentManager} this
28949          */
28950         "beforeselectfile" : true,
28951         /**
28952          * @event process
28953          * Fire before process file
28954          * @param {Roo.bootstrap.DocumentManager} this
28955          * @param {Object} file
28956          */
28957         "process" : true,
28958         /**
28959          * @event previewrendered
28960          * Fire when preview rendered
28961          * @param {Roo.bootstrap.DocumentManager} this
28962          * @param {Object} file
28963          */
28964         "previewrendered" : true,
28965         /**
28966          */
28967         "previewResize" : true
28968         
28969     });
28970 };
28971
28972 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28973     
28974     boxes : 0,
28975     inputName : '',
28976     thumbSize : 300,
28977     multiple : true,
28978     files : false,
28979     method : 'POST',
28980     url : '',
28981     paramName : 'imageUpload',
28982     toolTipName : 'filename',
28983     fieldLabel : '',
28984     labelWidth : 4,
28985     labelAlign : 'left',
28986     editable : true,
28987     delegates : false,
28988     xhr : false, 
28989     
28990     labellg : 0,
28991     labelmd : 0,
28992     labelsm : 0,
28993     labelxs : 0,
28994     
28995     getAutoCreate : function()
28996     {   
28997         var managerWidget = {
28998             tag : 'div',
28999             cls : 'roo-document-manager',
29000             cn : [
29001                 {
29002                     tag : 'input',
29003                     cls : 'roo-document-manager-selector',
29004                     type : 'file'
29005                 },
29006                 {
29007                     tag : 'div',
29008                     cls : 'roo-document-manager-uploader',
29009                     cn : [
29010                         {
29011                             tag : 'div',
29012                             cls : 'roo-document-manager-upload-btn',
29013                             html : '<i class="fa fa-plus"></i>'
29014                         }
29015                     ]
29016                     
29017                 }
29018             ]
29019         };
29020         
29021         var content = [
29022             {
29023                 tag : 'div',
29024                 cls : 'column col-md-12',
29025                 cn : managerWidget
29026             }
29027         ];
29028         
29029         if(this.fieldLabel.length){
29030             
29031             content = [
29032                 {
29033                     tag : 'div',
29034                     cls : 'column col-md-12',
29035                     html : this.fieldLabel
29036                 },
29037                 {
29038                     tag : 'div',
29039                     cls : 'column col-md-12',
29040                     cn : managerWidget
29041                 }
29042             ];
29043
29044             if(this.labelAlign == 'left'){
29045                 content = [
29046                     {
29047                         tag : 'div',
29048                         cls : 'column',
29049                         html : this.fieldLabel
29050                     },
29051                     {
29052                         tag : 'div',
29053                         cls : 'column',
29054                         cn : managerWidget
29055                     }
29056                 ];
29057                 
29058                 if(this.labelWidth > 12){
29059                     content[0].style = "width: " + this.labelWidth + 'px';
29060                 }
29061
29062                 if(this.labelWidth < 13 && this.labelmd == 0){
29063                     this.labelmd = this.labelWidth;
29064                 }
29065
29066                 if(this.labellg > 0){
29067                     content[0].cls += ' col-lg-' + this.labellg;
29068                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29069                 }
29070
29071                 if(this.labelmd > 0){
29072                     content[0].cls += ' col-md-' + this.labelmd;
29073                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29074                 }
29075
29076                 if(this.labelsm > 0){
29077                     content[0].cls += ' col-sm-' + this.labelsm;
29078                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29079                 }
29080
29081                 if(this.labelxs > 0){
29082                     content[0].cls += ' col-xs-' + this.labelxs;
29083                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29084                 }
29085                 
29086             }
29087         }
29088         
29089         var cfg = {
29090             tag : 'div',
29091             cls : 'row clearfix',
29092             cn : content
29093         };
29094         
29095         return cfg;
29096         
29097     },
29098     
29099     initEvents : function()
29100     {
29101         this.managerEl = this.el.select('.roo-document-manager', true).first();
29102         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29103         
29104         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29105         this.selectorEl.hide();
29106         
29107         if(this.multiple){
29108             this.selectorEl.attr('multiple', 'multiple');
29109         }
29110         
29111         this.selectorEl.on('change', this.onFileSelected, this);
29112         
29113         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29114         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29115         
29116         this.uploader.on('click', this.onUploaderClick, this);
29117         
29118         this.renderProgressDialog();
29119         
29120         var _this = this;
29121         
29122         window.addEventListener("resize", function() { _this.refresh(); } );
29123         
29124         this.fireEvent('initial', this);
29125     },
29126     
29127     renderProgressDialog : function()
29128     {
29129         var _this = this;
29130         
29131         this.progressDialog = new Roo.bootstrap.Modal({
29132             cls : 'roo-document-manager-progress-dialog',
29133             allow_close : false,
29134             title : '',
29135             buttons : [
29136                 {
29137                     name  :'cancel',
29138                     weight : 'danger',
29139                     html : 'Cancel'
29140                 }
29141             ], 
29142             listeners : { 
29143                 btnclick : function() {
29144                     _this.uploadCancel();
29145                     this.hide();
29146                 }
29147             }
29148         });
29149          
29150         this.progressDialog.render(Roo.get(document.body));
29151          
29152         this.progress = new Roo.bootstrap.Progress({
29153             cls : 'roo-document-manager-progress',
29154             active : true,
29155             striped : true
29156         });
29157         
29158         this.progress.render(this.progressDialog.getChildContainer());
29159         
29160         this.progressBar = new Roo.bootstrap.ProgressBar({
29161             cls : 'roo-document-manager-progress-bar',
29162             aria_valuenow : 0,
29163             aria_valuemin : 0,
29164             aria_valuemax : 12,
29165             panel : 'success'
29166         });
29167         
29168         this.progressBar.render(this.progress.getChildContainer());
29169     },
29170     
29171     onUploaderClick : function(e)
29172     {
29173         e.preventDefault();
29174      
29175         if(this.fireEvent('beforeselectfile', this) != false){
29176             this.selectorEl.dom.click();
29177         }
29178         
29179     },
29180     
29181     onFileSelected : function(e)
29182     {
29183         e.preventDefault();
29184         
29185         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29186             return;
29187         }
29188         
29189         Roo.each(this.selectorEl.dom.files, function(file){
29190             if(this.fireEvent('inspect', this, file) != false){
29191                 this.files.push(file);
29192             }
29193         }, this);
29194         
29195         this.queue();
29196         
29197     },
29198     
29199     queue : function()
29200     {
29201         this.selectorEl.dom.value = '';
29202         
29203         if(!this.files || !this.files.length){
29204             return;
29205         }
29206         
29207         if(this.boxes > 0 && this.files.length > this.boxes){
29208             this.files = this.files.slice(0, this.boxes);
29209         }
29210         
29211         this.uploader.show();
29212         
29213         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29214             this.uploader.hide();
29215         }
29216         
29217         var _this = this;
29218         
29219         var files = [];
29220         
29221         var docs = [];
29222         
29223         Roo.each(this.files, function(file){
29224             
29225             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29226                 var f = this.renderPreview(file);
29227                 files.push(f);
29228                 return;
29229             }
29230             
29231             if(file.type.indexOf('image') != -1){
29232                 this.delegates.push(
29233                     (function(){
29234                         _this.process(file);
29235                     }).createDelegate(this)
29236                 );
29237         
29238                 return;
29239             }
29240             
29241             docs.push(
29242                 (function(){
29243                     _this.process(file);
29244                 }).createDelegate(this)
29245             );
29246             
29247         }, this);
29248         
29249         this.files = files;
29250         
29251         this.delegates = this.delegates.concat(docs);
29252         
29253         if(!this.delegates.length){
29254             this.refresh();
29255             return;
29256         }
29257         
29258         this.progressBar.aria_valuemax = this.delegates.length;
29259         
29260         this.arrange();
29261         
29262         return;
29263     },
29264     
29265     arrange : function()
29266     {
29267         if(!this.delegates.length){
29268             this.progressDialog.hide();
29269             this.refresh();
29270             return;
29271         }
29272         
29273         var delegate = this.delegates.shift();
29274         
29275         this.progressDialog.show();
29276         
29277         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29278         
29279         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29280         
29281         delegate();
29282     },
29283     
29284     refresh : function()
29285     {
29286         this.uploader.show();
29287         
29288         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29289             this.uploader.hide();
29290         }
29291         
29292         Roo.isTouch ? this.closable(false) : this.closable(true);
29293         
29294         this.fireEvent('refresh', this);
29295     },
29296     
29297     onRemove : function(e, el, o)
29298     {
29299         e.preventDefault();
29300         
29301         this.fireEvent('remove', this, o);
29302         
29303     },
29304     
29305     remove : function(o)
29306     {
29307         var files = [];
29308         
29309         Roo.each(this.files, function(file){
29310             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29311                 files.push(file);
29312                 return;
29313             }
29314
29315             o.target.remove();
29316
29317         }, this);
29318         
29319         this.files = files;
29320         
29321         this.refresh();
29322     },
29323     
29324     clear : function()
29325     {
29326         Roo.each(this.files, function(file){
29327             if(!file.target){
29328                 return;
29329             }
29330             
29331             file.target.remove();
29332
29333         }, this);
29334         
29335         this.files = [];
29336         
29337         this.refresh();
29338     },
29339     
29340     onClick : function(e, el, o)
29341     {
29342         e.preventDefault();
29343         
29344         this.fireEvent('click', this, o);
29345         
29346     },
29347     
29348     closable : function(closable)
29349     {
29350         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29351             
29352             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29353             
29354             if(closable){
29355                 el.show();
29356                 return;
29357             }
29358             
29359             el.hide();
29360             
29361         }, this);
29362     },
29363     
29364     xhrOnLoad : function(xhr)
29365     {
29366         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29367             el.remove();
29368         }, this);
29369         
29370         if (xhr.readyState !== 4) {
29371             this.arrange();
29372             this.fireEvent('exception', this, xhr);
29373             return;
29374         }
29375
29376         var response = Roo.decode(xhr.responseText);
29377         
29378         if(!response.success){
29379             this.arrange();
29380             this.fireEvent('exception', this, xhr);
29381             return;
29382         }
29383         
29384         var file = this.renderPreview(response.data);
29385         
29386         this.files.push(file);
29387         
29388         this.arrange();
29389         
29390         this.fireEvent('afterupload', this, xhr);
29391         
29392     },
29393     
29394     xhrOnError : function(xhr)
29395     {
29396         Roo.log('xhr on error');
29397         
29398         var response = Roo.decode(xhr.responseText);
29399           
29400         Roo.log(response);
29401         
29402         this.arrange();
29403     },
29404     
29405     process : function(file)
29406     {
29407         if(this.fireEvent('process', this, file) !== false){
29408             if(this.editable && file.type.indexOf('image') != -1){
29409                 this.fireEvent('edit', this, file);
29410                 return;
29411             }
29412
29413             this.uploadStart(file, false);
29414
29415             return;
29416         }
29417         
29418     },
29419     
29420     uploadStart : function(file, crop)
29421     {
29422         this.xhr = new XMLHttpRequest();
29423         
29424         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29425             this.arrange();
29426             return;
29427         }
29428         
29429         file.xhr = this.xhr;
29430             
29431         this.managerEl.createChild({
29432             tag : 'div',
29433             cls : 'roo-document-manager-loading',
29434             cn : [
29435                 {
29436                     tag : 'div',
29437                     tooltip : file.name,
29438                     cls : 'roo-document-manager-thumb',
29439                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29440                 }
29441             ]
29442
29443         });
29444
29445         this.xhr.open(this.method, this.url, true);
29446         
29447         var headers = {
29448             "Accept": "application/json",
29449             "Cache-Control": "no-cache",
29450             "X-Requested-With": "XMLHttpRequest"
29451         };
29452         
29453         for (var headerName in headers) {
29454             var headerValue = headers[headerName];
29455             if (headerValue) {
29456                 this.xhr.setRequestHeader(headerName, headerValue);
29457             }
29458         }
29459         
29460         var _this = this;
29461         
29462         this.xhr.onload = function()
29463         {
29464             _this.xhrOnLoad(_this.xhr);
29465         }
29466         
29467         this.xhr.onerror = function()
29468         {
29469             _this.xhrOnError(_this.xhr);
29470         }
29471         
29472         var formData = new FormData();
29473
29474         formData.append('returnHTML', 'NO');
29475         
29476         if(crop){
29477             formData.append('crop', crop);
29478         }
29479         
29480         formData.append(this.paramName, file, file.name);
29481         
29482         var options = {
29483             file : file, 
29484             manually : false
29485         };
29486         
29487         if(this.fireEvent('prepare', this, formData, options) != false){
29488             
29489             if(options.manually){
29490                 return;
29491             }
29492             
29493             this.xhr.send(formData);
29494             return;
29495         };
29496         
29497         this.uploadCancel();
29498     },
29499     
29500     uploadCancel : function()
29501     {
29502         if (this.xhr) {
29503             this.xhr.abort();
29504         }
29505         
29506         this.delegates = [];
29507         
29508         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29509             el.remove();
29510         }, this);
29511         
29512         this.arrange();
29513     },
29514     
29515     renderPreview : function(file)
29516     {
29517         if(typeof(file.target) != 'undefined' && file.target){
29518             return file;
29519         }
29520         
29521         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29522         
29523         var previewEl = this.managerEl.createChild({
29524             tag : 'div',
29525             cls : 'roo-document-manager-preview',
29526             cn : [
29527                 {
29528                     tag : 'div',
29529                     tooltip : file[this.toolTipName],
29530                     cls : 'roo-document-manager-thumb',
29531                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29532                 },
29533                 {
29534                     tag : 'button',
29535                     cls : 'close',
29536                     html : '<i class="fa fa-times-circle"></i>'
29537                 }
29538             ]
29539         });
29540
29541         var close = previewEl.select('button.close', true).first();
29542
29543         close.on('click', this.onRemove, this, file);
29544
29545         file.target = previewEl;
29546
29547         var image = previewEl.select('img', true).first();
29548         
29549         var _this = this;
29550         
29551         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29552         
29553         image.on('click', this.onClick, this, file);
29554         
29555         this.fireEvent('previewrendered', this, file);
29556         
29557         return file;
29558         
29559     },
29560     
29561     onPreviewLoad : function(file, image)
29562     {
29563         if(typeof(file.target) == 'undefined' || !file.target){
29564             return;
29565         }
29566         
29567         var width = image.dom.naturalWidth || image.dom.width;
29568         var height = image.dom.naturalHeight || image.dom.height;
29569         
29570         if(!this.previewResize) {
29571             return;
29572         }
29573         
29574         if(width > height){
29575             file.target.addClass('wide');
29576             return;
29577         }
29578         
29579         file.target.addClass('tall');
29580         return;
29581         
29582     },
29583     
29584     uploadFromSource : function(file, crop)
29585     {
29586         this.xhr = new XMLHttpRequest();
29587         
29588         this.managerEl.createChild({
29589             tag : 'div',
29590             cls : 'roo-document-manager-loading',
29591             cn : [
29592                 {
29593                     tag : 'div',
29594                     tooltip : file.name,
29595                     cls : 'roo-document-manager-thumb',
29596                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29597                 }
29598             ]
29599
29600         });
29601
29602         this.xhr.open(this.method, this.url, true);
29603         
29604         var headers = {
29605             "Accept": "application/json",
29606             "Cache-Control": "no-cache",
29607             "X-Requested-With": "XMLHttpRequest"
29608         };
29609         
29610         for (var headerName in headers) {
29611             var headerValue = headers[headerName];
29612             if (headerValue) {
29613                 this.xhr.setRequestHeader(headerName, headerValue);
29614             }
29615         }
29616         
29617         var _this = this;
29618         
29619         this.xhr.onload = function()
29620         {
29621             _this.xhrOnLoad(_this.xhr);
29622         }
29623         
29624         this.xhr.onerror = function()
29625         {
29626             _this.xhrOnError(_this.xhr);
29627         }
29628         
29629         var formData = new FormData();
29630
29631         formData.append('returnHTML', 'NO');
29632         
29633         formData.append('crop', crop);
29634         
29635         if(typeof(file.filename) != 'undefined'){
29636             formData.append('filename', file.filename);
29637         }
29638         
29639         if(typeof(file.mimetype) != 'undefined'){
29640             formData.append('mimetype', file.mimetype);
29641         }
29642         
29643         Roo.log(formData);
29644         
29645         if(this.fireEvent('prepare', this, formData) != false){
29646             this.xhr.send(formData);
29647         };
29648     }
29649 });
29650
29651 /*
29652 * Licence: LGPL
29653 */
29654
29655 /**
29656  * @class Roo.bootstrap.DocumentViewer
29657  * @extends Roo.bootstrap.Component
29658  * Bootstrap DocumentViewer class
29659  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29660  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29661  * 
29662  * @constructor
29663  * Create a new DocumentViewer
29664  * @param {Object} config The config object
29665  */
29666
29667 Roo.bootstrap.DocumentViewer = function(config){
29668     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29669     
29670     this.addEvents({
29671         /**
29672          * @event initial
29673          * Fire after initEvent
29674          * @param {Roo.bootstrap.DocumentViewer} this
29675          */
29676         "initial" : true,
29677         /**
29678          * @event click
29679          * Fire after click
29680          * @param {Roo.bootstrap.DocumentViewer} this
29681          */
29682         "click" : true,
29683         /**
29684          * @event download
29685          * Fire after download button
29686          * @param {Roo.bootstrap.DocumentViewer} this
29687          */
29688         "download" : true,
29689         /**
29690          * @event trash
29691          * Fire after trash button
29692          * @param {Roo.bootstrap.DocumentViewer} this
29693          */
29694         "trash" : true
29695         
29696     });
29697 };
29698
29699 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29700     
29701     showDownload : true,
29702     
29703     showTrash : true,
29704     
29705     getAutoCreate : function()
29706     {
29707         var cfg = {
29708             tag : 'div',
29709             cls : 'roo-document-viewer',
29710             cn : [
29711                 {
29712                     tag : 'div',
29713                     cls : 'roo-document-viewer-body',
29714                     cn : [
29715                         {
29716                             tag : 'div',
29717                             cls : 'roo-document-viewer-thumb',
29718                             cn : [
29719                                 {
29720                                     tag : 'img',
29721                                     cls : 'roo-document-viewer-image'
29722                                 }
29723                             ]
29724                         }
29725                     ]
29726                 },
29727                 {
29728                     tag : 'div',
29729                     cls : 'roo-document-viewer-footer',
29730                     cn : {
29731                         tag : 'div',
29732                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29733                         cn : [
29734                             {
29735                                 tag : 'div',
29736                                 cls : 'btn-group roo-document-viewer-download',
29737                                 cn : [
29738                                     {
29739                                         tag : 'button',
29740                                         cls : 'btn btn-default',
29741                                         html : '<i class="fa fa-download"></i>'
29742                                     }
29743                                 ]
29744                             },
29745                             {
29746                                 tag : 'div',
29747                                 cls : 'btn-group roo-document-viewer-trash',
29748                                 cn : [
29749                                     {
29750                                         tag : 'button',
29751                                         cls : 'btn btn-default',
29752                                         html : '<i class="fa fa-trash"></i>'
29753                                     }
29754                                 ]
29755                             }
29756                         ]
29757                     }
29758                 }
29759             ]
29760         };
29761         
29762         return cfg;
29763     },
29764     
29765     initEvents : function()
29766     {
29767         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29768         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29769         
29770         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29771         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29772         
29773         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29774         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29775         
29776         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29777         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29778         
29779         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29780         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29781         
29782         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29783         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29784         
29785         this.bodyEl.on('click', this.onClick, this);
29786         this.downloadBtn.on('click', this.onDownload, this);
29787         this.trashBtn.on('click', this.onTrash, this);
29788         
29789         this.downloadBtn.hide();
29790         this.trashBtn.hide();
29791         
29792         if(this.showDownload){
29793             this.downloadBtn.show();
29794         }
29795         
29796         if(this.showTrash){
29797             this.trashBtn.show();
29798         }
29799         
29800         if(!this.showDownload && !this.showTrash) {
29801             this.footerEl.hide();
29802         }
29803         
29804     },
29805     
29806     initial : function()
29807     {
29808         this.fireEvent('initial', this);
29809         
29810     },
29811     
29812     onClick : function(e)
29813     {
29814         e.preventDefault();
29815         
29816         this.fireEvent('click', this);
29817     },
29818     
29819     onDownload : function(e)
29820     {
29821         e.preventDefault();
29822         
29823         this.fireEvent('download', this);
29824     },
29825     
29826     onTrash : function(e)
29827     {
29828         e.preventDefault();
29829         
29830         this.fireEvent('trash', this);
29831     }
29832     
29833 });
29834 /*
29835  * - LGPL
29836  *
29837  * nav progress bar
29838  * 
29839  */
29840
29841 /**
29842  * @class Roo.bootstrap.NavProgressBar
29843  * @extends Roo.bootstrap.Component
29844  * Bootstrap NavProgressBar class
29845  * 
29846  * @constructor
29847  * Create a new nav progress bar
29848  * @param {Object} config The config object
29849  */
29850
29851 Roo.bootstrap.NavProgressBar = function(config){
29852     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29853
29854     this.bullets = this.bullets || [];
29855    
29856 //    Roo.bootstrap.NavProgressBar.register(this);
29857      this.addEvents({
29858         /**
29859              * @event changed
29860              * Fires when the active item changes
29861              * @param {Roo.bootstrap.NavProgressBar} this
29862              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29863              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29864          */
29865         'changed': true
29866      });
29867     
29868 };
29869
29870 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29871     
29872     bullets : [],
29873     barItems : [],
29874     
29875     getAutoCreate : function()
29876     {
29877         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29878         
29879         cfg = {
29880             tag : 'div',
29881             cls : 'roo-navigation-bar-group',
29882             cn : [
29883                 {
29884                     tag : 'div',
29885                     cls : 'roo-navigation-top-bar'
29886                 },
29887                 {
29888                     tag : 'div',
29889                     cls : 'roo-navigation-bullets-bar',
29890                     cn : [
29891                         {
29892                             tag : 'ul',
29893                             cls : 'roo-navigation-bar'
29894                         }
29895                     ]
29896                 },
29897                 
29898                 {
29899                     tag : 'div',
29900                     cls : 'roo-navigation-bottom-bar'
29901                 }
29902             ]
29903             
29904         };
29905         
29906         return cfg;
29907         
29908     },
29909     
29910     initEvents: function() 
29911     {
29912         
29913     },
29914     
29915     onRender : function(ct, position) 
29916     {
29917         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29918         
29919         if(this.bullets.length){
29920             Roo.each(this.bullets, function(b){
29921                this.addItem(b);
29922             }, this);
29923         }
29924         
29925         this.format();
29926         
29927     },
29928     
29929     addItem : function(cfg)
29930     {
29931         var item = new Roo.bootstrap.NavProgressItem(cfg);
29932         
29933         item.parentId = this.id;
29934         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29935         
29936         if(cfg.html){
29937             var top = new Roo.bootstrap.Element({
29938                 tag : 'div',
29939                 cls : 'roo-navigation-bar-text'
29940             });
29941             
29942             var bottom = new Roo.bootstrap.Element({
29943                 tag : 'div',
29944                 cls : 'roo-navigation-bar-text'
29945             });
29946             
29947             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29948             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29949             
29950             var topText = new Roo.bootstrap.Element({
29951                 tag : 'span',
29952                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29953             });
29954             
29955             var bottomText = new Roo.bootstrap.Element({
29956                 tag : 'span',
29957                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29958             });
29959             
29960             topText.onRender(top.el, null);
29961             bottomText.onRender(bottom.el, null);
29962             
29963             item.topEl = top;
29964             item.bottomEl = bottom;
29965         }
29966         
29967         this.barItems.push(item);
29968         
29969         return item;
29970     },
29971     
29972     getActive : function()
29973     {
29974         var active = false;
29975         
29976         Roo.each(this.barItems, function(v){
29977             
29978             if (!v.isActive()) {
29979                 return;
29980             }
29981             
29982             active = v;
29983             return false;
29984             
29985         });
29986         
29987         return active;
29988     },
29989     
29990     setActiveItem : function(item)
29991     {
29992         var prev = false;
29993         
29994         Roo.each(this.barItems, function(v){
29995             if (v.rid == item.rid) {
29996                 return ;
29997             }
29998             
29999             if (v.isActive()) {
30000                 v.setActive(false);
30001                 prev = v;
30002             }
30003         });
30004
30005         item.setActive(true);
30006         
30007         this.fireEvent('changed', this, item, prev);
30008     },
30009     
30010     getBarItem: function(rid)
30011     {
30012         var ret = false;
30013         
30014         Roo.each(this.barItems, function(e) {
30015             if (e.rid != rid) {
30016                 return;
30017             }
30018             
30019             ret =  e;
30020             return false;
30021         });
30022         
30023         return ret;
30024     },
30025     
30026     indexOfItem : function(item)
30027     {
30028         var index = false;
30029         
30030         Roo.each(this.barItems, function(v, i){
30031             
30032             if (v.rid != item.rid) {
30033                 return;
30034             }
30035             
30036             index = i;
30037             return false
30038         });
30039         
30040         return index;
30041     },
30042     
30043     setActiveNext : function()
30044     {
30045         var i = this.indexOfItem(this.getActive());
30046         
30047         if (i > this.barItems.length) {
30048             return;
30049         }
30050         
30051         this.setActiveItem(this.barItems[i+1]);
30052     },
30053     
30054     setActivePrev : function()
30055     {
30056         var i = this.indexOfItem(this.getActive());
30057         
30058         if (i  < 1) {
30059             return;
30060         }
30061         
30062         this.setActiveItem(this.barItems[i-1]);
30063     },
30064     
30065     format : function()
30066     {
30067         if(!this.barItems.length){
30068             return;
30069         }
30070      
30071         var width = 100 / this.barItems.length;
30072         
30073         Roo.each(this.barItems, function(i){
30074             i.el.setStyle('width', width + '%');
30075             i.topEl.el.setStyle('width', width + '%');
30076             i.bottomEl.el.setStyle('width', width + '%');
30077         }, this);
30078         
30079     }
30080     
30081 });
30082 /*
30083  * - LGPL
30084  *
30085  * Nav Progress Item
30086  * 
30087  */
30088
30089 /**
30090  * @class Roo.bootstrap.NavProgressItem
30091  * @extends Roo.bootstrap.Component
30092  * Bootstrap NavProgressItem class
30093  * @cfg {String} rid the reference id
30094  * @cfg {Boolean} active (true|false) Is item active default false
30095  * @cfg {Boolean} disabled (true|false) Is item active default false
30096  * @cfg {String} html
30097  * @cfg {String} position (top|bottom) text position default bottom
30098  * @cfg {String} icon show icon instead of number
30099  * 
30100  * @constructor
30101  * Create a new NavProgressItem
30102  * @param {Object} config The config object
30103  */
30104 Roo.bootstrap.NavProgressItem = function(config){
30105     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30106     this.addEvents({
30107         // raw events
30108         /**
30109          * @event click
30110          * The raw click event for the entire grid.
30111          * @param {Roo.bootstrap.NavProgressItem} this
30112          * @param {Roo.EventObject} e
30113          */
30114         "click" : true
30115     });
30116    
30117 };
30118
30119 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30120     
30121     rid : '',
30122     active : false,
30123     disabled : false,
30124     html : '',
30125     position : 'bottom',
30126     icon : false,
30127     
30128     getAutoCreate : function()
30129     {
30130         var iconCls = 'roo-navigation-bar-item-icon';
30131         
30132         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30133         
30134         var cfg = {
30135             tag: 'li',
30136             cls: 'roo-navigation-bar-item',
30137             cn : [
30138                 {
30139                     tag : 'i',
30140                     cls : iconCls
30141                 }
30142             ]
30143         };
30144         
30145         if(this.active){
30146             cfg.cls += ' active';
30147         }
30148         if(this.disabled){
30149             cfg.cls += ' disabled';
30150         }
30151         
30152         return cfg;
30153     },
30154     
30155     disable : function()
30156     {
30157         this.setDisabled(true);
30158     },
30159     
30160     enable : function()
30161     {
30162         this.setDisabled(false);
30163     },
30164     
30165     initEvents: function() 
30166     {
30167         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30168         
30169         this.iconEl.on('click', this.onClick, this);
30170     },
30171     
30172     onClick : function(e)
30173     {
30174         e.preventDefault();
30175         
30176         if(this.disabled){
30177             return;
30178         }
30179         
30180         if(this.fireEvent('click', this, e) === false){
30181             return;
30182         };
30183         
30184         this.parent().setActiveItem(this);
30185     },
30186     
30187     isActive: function () 
30188     {
30189         return this.active;
30190     },
30191     
30192     setActive : function(state)
30193     {
30194         if(this.active == state){
30195             return;
30196         }
30197         
30198         this.active = state;
30199         
30200         if (state) {
30201             this.el.addClass('active');
30202             return;
30203         }
30204         
30205         this.el.removeClass('active');
30206         
30207         return;
30208     },
30209     
30210     setDisabled : function(state)
30211     {
30212         if(this.disabled == state){
30213             return;
30214         }
30215         
30216         this.disabled = state;
30217         
30218         if (state) {
30219             this.el.addClass('disabled');
30220             return;
30221         }
30222         
30223         this.el.removeClass('disabled');
30224     },
30225     
30226     tooltipEl : function()
30227     {
30228         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30229     }
30230 });
30231  
30232
30233  /*
30234  * - LGPL
30235  *
30236  * FieldLabel
30237  * 
30238  */
30239
30240 /**
30241  * @class Roo.bootstrap.FieldLabel
30242  * @extends Roo.bootstrap.Component
30243  * Bootstrap FieldLabel class
30244  * @cfg {String} html contents of the element
30245  * @cfg {String} tag tag of the element default label
30246  * @cfg {String} cls class of the element
30247  * @cfg {String} target label target 
30248  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30249  * @cfg {String} invalidClass default "text-warning"
30250  * @cfg {String} validClass default "text-success"
30251  * @cfg {String} iconTooltip default "This field is required"
30252  * @cfg {String} indicatorpos (left|right) default left
30253  * 
30254  * @constructor
30255  * Create a new FieldLabel
30256  * @param {Object} config The config object
30257  */
30258
30259 Roo.bootstrap.FieldLabel = function(config){
30260     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30261     
30262     this.addEvents({
30263             /**
30264              * @event invalid
30265              * Fires after the field has been marked as invalid.
30266              * @param {Roo.form.FieldLabel} this
30267              * @param {String} msg The validation message
30268              */
30269             invalid : true,
30270             /**
30271              * @event valid
30272              * Fires after the field has been validated with no errors.
30273              * @param {Roo.form.FieldLabel} this
30274              */
30275             valid : true
30276         });
30277 };
30278
30279 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30280     
30281     tag: 'label',
30282     cls: '',
30283     html: '',
30284     target: '',
30285     allowBlank : true,
30286     invalidClass : 'has-warning',
30287     validClass : 'has-success',
30288     iconTooltip : 'This field is required',
30289     indicatorpos : 'left',
30290     
30291     getAutoCreate : function(){
30292         
30293         var cls = "";
30294         if (!this.allowBlank) {
30295             cls  = "visible";
30296         }
30297         
30298         var cfg = {
30299             tag : this.tag,
30300             cls : 'roo-bootstrap-field-label ' + this.cls,
30301             for : this.target,
30302             cn : [
30303                 {
30304                     tag : 'i',
30305                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30306                     tooltip : this.iconTooltip
30307                 },
30308                 {
30309                     tag : 'span',
30310                     html : this.html
30311                 }
30312             ] 
30313         };
30314         
30315         if(this.indicatorpos == 'right'){
30316             var cfg = {
30317                 tag : this.tag,
30318                 cls : 'roo-bootstrap-field-label ' + this.cls,
30319                 for : this.target,
30320                 cn : [
30321                     {
30322                         tag : 'span',
30323                         html : this.html
30324                     },
30325                     {
30326                         tag : 'i',
30327                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30328                         tooltip : this.iconTooltip
30329                     }
30330                 ] 
30331             };
30332         }
30333         
30334         return cfg;
30335     },
30336     
30337     initEvents: function() 
30338     {
30339         Roo.bootstrap.Element.superclass.initEvents.call(this);
30340         
30341         this.indicator = this.indicatorEl();
30342         
30343         if(this.indicator){
30344             this.indicator.removeClass('visible');
30345             this.indicator.addClass('invisible');
30346         }
30347         
30348         Roo.bootstrap.FieldLabel.register(this);
30349     },
30350     
30351     indicatorEl : function()
30352     {
30353         var indicator = this.el.select('i.roo-required-indicator',true).first();
30354         
30355         if(!indicator){
30356             return false;
30357         }
30358         
30359         return indicator;
30360         
30361     },
30362     
30363     /**
30364      * Mark this field as valid
30365      */
30366     markValid : function()
30367     {
30368         if(this.indicator){
30369             this.indicator.removeClass('visible');
30370             this.indicator.addClass('invisible');
30371         }
30372         
30373         this.el.removeClass(this.invalidClass);
30374         
30375         this.el.addClass(this.validClass);
30376         
30377         this.fireEvent('valid', this);
30378     },
30379     
30380     /**
30381      * Mark this field as invalid
30382      * @param {String} msg The validation message
30383      */
30384     markInvalid : function(msg)
30385     {
30386         if(this.indicator){
30387             this.indicator.removeClass('invisible');
30388             this.indicator.addClass('visible');
30389         }
30390         
30391         this.el.removeClass(this.validClass);
30392         
30393         this.el.addClass(this.invalidClass);
30394         
30395         this.fireEvent('invalid', this, msg);
30396     }
30397     
30398    
30399 });
30400
30401 Roo.apply(Roo.bootstrap.FieldLabel, {
30402     
30403     groups: {},
30404     
30405      /**
30406     * register a FieldLabel Group
30407     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30408     */
30409     register : function(label)
30410     {
30411         if(this.groups.hasOwnProperty(label.target)){
30412             return;
30413         }
30414      
30415         this.groups[label.target] = label;
30416         
30417     },
30418     /**
30419     * fetch a FieldLabel Group based on the target
30420     * @param {string} target
30421     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30422     */
30423     get: function(target) {
30424         if (typeof(this.groups[target]) == 'undefined') {
30425             return false;
30426         }
30427         
30428         return this.groups[target] ;
30429     }
30430 });
30431
30432  
30433
30434  /*
30435  * - LGPL
30436  *
30437  * page DateSplitField.
30438  * 
30439  */
30440
30441
30442 /**
30443  * @class Roo.bootstrap.DateSplitField
30444  * @extends Roo.bootstrap.Component
30445  * Bootstrap DateSplitField class
30446  * @cfg {string} fieldLabel - the label associated
30447  * @cfg {Number} labelWidth set the width of label (0-12)
30448  * @cfg {String} labelAlign (top|left)
30449  * @cfg {Boolean} dayAllowBlank (true|false) default false
30450  * @cfg {Boolean} monthAllowBlank (true|false) default false
30451  * @cfg {Boolean} yearAllowBlank (true|false) default false
30452  * @cfg {string} dayPlaceholder 
30453  * @cfg {string} monthPlaceholder
30454  * @cfg {string} yearPlaceholder
30455  * @cfg {string} dayFormat default 'd'
30456  * @cfg {string} monthFormat default 'm'
30457  * @cfg {string} yearFormat default 'Y'
30458  * @cfg {Number} labellg set the width of label (1-12)
30459  * @cfg {Number} labelmd set the width of label (1-12)
30460  * @cfg {Number} labelsm set the width of label (1-12)
30461  * @cfg {Number} labelxs set the width of label (1-12)
30462
30463  *     
30464  * @constructor
30465  * Create a new DateSplitField
30466  * @param {Object} config The config object
30467  */
30468
30469 Roo.bootstrap.DateSplitField = function(config){
30470     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30471     
30472     this.addEvents({
30473         // raw events
30474          /**
30475          * @event years
30476          * getting the data of years
30477          * @param {Roo.bootstrap.DateSplitField} this
30478          * @param {Object} years
30479          */
30480         "years" : true,
30481         /**
30482          * @event days
30483          * getting the data of days
30484          * @param {Roo.bootstrap.DateSplitField} this
30485          * @param {Object} days
30486          */
30487         "days" : true,
30488         /**
30489          * @event invalid
30490          * Fires after the field has been marked as invalid.
30491          * @param {Roo.form.Field} this
30492          * @param {String} msg The validation message
30493          */
30494         invalid : true,
30495        /**
30496          * @event valid
30497          * Fires after the field has been validated with no errors.
30498          * @param {Roo.form.Field} this
30499          */
30500         valid : true
30501     });
30502 };
30503
30504 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30505     
30506     fieldLabel : '',
30507     labelAlign : 'top',
30508     labelWidth : 3,
30509     dayAllowBlank : false,
30510     monthAllowBlank : false,
30511     yearAllowBlank : false,
30512     dayPlaceholder : '',
30513     monthPlaceholder : '',
30514     yearPlaceholder : '',
30515     dayFormat : 'd',
30516     monthFormat : 'm',
30517     yearFormat : 'Y',
30518     isFormField : true,
30519     labellg : 0,
30520     labelmd : 0,
30521     labelsm : 0,
30522     labelxs : 0,
30523     
30524     getAutoCreate : function()
30525     {
30526         var cfg = {
30527             tag : 'div',
30528             cls : 'row roo-date-split-field-group',
30529             cn : [
30530                 {
30531                     tag : 'input',
30532                     type : 'hidden',
30533                     cls : 'form-hidden-field roo-date-split-field-group-value',
30534                     name : this.name
30535                 }
30536             ]
30537         };
30538         
30539         var labelCls = 'col-md-12';
30540         var contentCls = 'col-md-4';
30541         
30542         if(this.fieldLabel){
30543             
30544             var label = {
30545                 tag : 'div',
30546                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30547                 cn : [
30548                     {
30549                         tag : 'label',
30550                         html : this.fieldLabel
30551                     }
30552                 ]
30553             };
30554             
30555             if(this.labelAlign == 'left'){
30556             
30557                 if(this.labelWidth > 12){
30558                     label.style = "width: " + this.labelWidth + 'px';
30559                 }
30560
30561                 if(this.labelWidth < 13 && this.labelmd == 0){
30562                     this.labelmd = this.labelWidth;
30563                 }
30564
30565                 if(this.labellg > 0){
30566                     labelCls = ' col-lg-' + this.labellg;
30567                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30568                 }
30569
30570                 if(this.labelmd > 0){
30571                     labelCls = ' col-md-' + this.labelmd;
30572                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30573                 }
30574
30575                 if(this.labelsm > 0){
30576                     labelCls = ' col-sm-' + this.labelsm;
30577                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30578                 }
30579
30580                 if(this.labelxs > 0){
30581                     labelCls = ' col-xs-' + this.labelxs;
30582                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30583                 }
30584             }
30585             
30586             label.cls += ' ' + labelCls;
30587             
30588             cfg.cn.push(label);
30589         }
30590         
30591         Roo.each(['day', 'month', 'year'], function(t){
30592             cfg.cn.push({
30593                 tag : 'div',
30594                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30595             });
30596         }, this);
30597         
30598         return cfg;
30599     },
30600     
30601     inputEl: function ()
30602     {
30603         return this.el.select('.roo-date-split-field-group-value', true).first();
30604     },
30605     
30606     onRender : function(ct, position) 
30607     {
30608         var _this = this;
30609         
30610         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30611         
30612         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30613         
30614         this.dayField = new Roo.bootstrap.ComboBox({
30615             allowBlank : this.dayAllowBlank,
30616             alwaysQuery : true,
30617             displayField : 'value',
30618             editable : false,
30619             fieldLabel : '',
30620             forceSelection : true,
30621             mode : 'local',
30622             placeholder : this.dayPlaceholder,
30623             selectOnFocus : true,
30624             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30625             triggerAction : 'all',
30626             typeAhead : true,
30627             valueField : 'value',
30628             store : new Roo.data.SimpleStore({
30629                 data : (function() {    
30630                     var days = [];
30631                     _this.fireEvent('days', _this, days);
30632                     return days;
30633                 })(),
30634                 fields : [ 'value' ]
30635             }),
30636             listeners : {
30637                 select : function (_self, record, index)
30638                 {
30639                     _this.setValue(_this.getValue());
30640                 }
30641             }
30642         });
30643
30644         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30645         
30646         this.monthField = new Roo.bootstrap.MonthField({
30647             after : '<i class=\"fa fa-calendar\"></i>',
30648             allowBlank : this.monthAllowBlank,
30649             placeholder : this.monthPlaceholder,
30650             readOnly : true,
30651             listeners : {
30652                 render : function (_self)
30653                 {
30654                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30655                         e.preventDefault();
30656                         _self.focus();
30657                     });
30658                 },
30659                 select : function (_self, oldvalue, newvalue)
30660                 {
30661                     _this.setValue(_this.getValue());
30662                 }
30663             }
30664         });
30665         
30666         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30667         
30668         this.yearField = new Roo.bootstrap.ComboBox({
30669             allowBlank : this.yearAllowBlank,
30670             alwaysQuery : true,
30671             displayField : 'value',
30672             editable : false,
30673             fieldLabel : '',
30674             forceSelection : true,
30675             mode : 'local',
30676             placeholder : this.yearPlaceholder,
30677             selectOnFocus : true,
30678             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30679             triggerAction : 'all',
30680             typeAhead : true,
30681             valueField : 'value',
30682             store : new Roo.data.SimpleStore({
30683                 data : (function() {
30684                     var years = [];
30685                     _this.fireEvent('years', _this, years);
30686                     return years;
30687                 })(),
30688                 fields : [ 'value' ]
30689             }),
30690             listeners : {
30691                 select : function (_self, record, index)
30692                 {
30693                     _this.setValue(_this.getValue());
30694                 }
30695             }
30696         });
30697
30698         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30699     },
30700     
30701     setValue : function(v, format)
30702     {
30703         this.inputEl.dom.value = v;
30704         
30705         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30706         
30707         var d = Date.parseDate(v, f);
30708         
30709         if(!d){
30710             this.validate();
30711             return;
30712         }
30713         
30714         this.setDay(d.format(this.dayFormat));
30715         this.setMonth(d.format(this.monthFormat));
30716         this.setYear(d.format(this.yearFormat));
30717         
30718         this.validate();
30719         
30720         return;
30721     },
30722     
30723     setDay : function(v)
30724     {
30725         this.dayField.setValue(v);
30726         this.inputEl.dom.value = this.getValue();
30727         this.validate();
30728         return;
30729     },
30730     
30731     setMonth : function(v)
30732     {
30733         this.monthField.setValue(v, true);
30734         this.inputEl.dom.value = this.getValue();
30735         this.validate();
30736         return;
30737     },
30738     
30739     setYear : function(v)
30740     {
30741         this.yearField.setValue(v);
30742         this.inputEl.dom.value = this.getValue();
30743         this.validate();
30744         return;
30745     },
30746     
30747     getDay : function()
30748     {
30749         return this.dayField.getValue();
30750     },
30751     
30752     getMonth : function()
30753     {
30754         return this.monthField.getValue();
30755     },
30756     
30757     getYear : function()
30758     {
30759         return this.yearField.getValue();
30760     },
30761     
30762     getValue : function()
30763     {
30764         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30765         
30766         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30767         
30768         return date;
30769     },
30770     
30771     reset : function()
30772     {
30773         this.setDay('');
30774         this.setMonth('');
30775         this.setYear('');
30776         this.inputEl.dom.value = '';
30777         this.validate();
30778         return;
30779     },
30780     
30781     validate : function()
30782     {
30783         var d = this.dayField.validate();
30784         var m = this.monthField.validate();
30785         var y = this.yearField.validate();
30786         
30787         var valid = true;
30788         
30789         if(
30790                 (!this.dayAllowBlank && !d) ||
30791                 (!this.monthAllowBlank && !m) ||
30792                 (!this.yearAllowBlank && !y)
30793         ){
30794             valid = false;
30795         }
30796         
30797         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30798             return valid;
30799         }
30800         
30801         if(valid){
30802             this.markValid();
30803             return valid;
30804         }
30805         
30806         this.markInvalid();
30807         
30808         return valid;
30809     },
30810     
30811     markValid : function()
30812     {
30813         
30814         var label = this.el.select('label', true).first();
30815         var icon = this.el.select('i.fa-star', true).first();
30816
30817         if(label && icon){
30818             icon.remove();
30819         }
30820         
30821         this.fireEvent('valid', this);
30822     },
30823     
30824      /**
30825      * Mark this field as invalid
30826      * @param {String} msg The validation message
30827      */
30828     markInvalid : function(msg)
30829     {
30830         
30831         var label = this.el.select('label', true).first();
30832         var icon = this.el.select('i.fa-star', true).first();
30833
30834         if(label && !icon){
30835             this.el.select('.roo-date-split-field-label', true).createChild({
30836                 tag : 'i',
30837                 cls : 'text-danger fa fa-lg fa-star',
30838                 tooltip : 'This field is required',
30839                 style : 'margin-right:5px;'
30840             }, label, true);
30841         }
30842         
30843         this.fireEvent('invalid', this, msg);
30844     },
30845     
30846     clearInvalid : function()
30847     {
30848         var label = this.el.select('label', true).first();
30849         var icon = this.el.select('i.fa-star', true).first();
30850
30851         if(label && icon){
30852             icon.remove();
30853         }
30854         
30855         this.fireEvent('valid', this);
30856     },
30857     
30858     getName: function()
30859     {
30860         return this.name;
30861     }
30862     
30863 });
30864
30865  /**
30866  *
30867  * This is based on 
30868  * http://masonry.desandro.com
30869  *
30870  * The idea is to render all the bricks based on vertical width...
30871  *
30872  * The original code extends 'outlayer' - we might need to use that....
30873  * 
30874  */
30875
30876
30877 /**
30878  * @class Roo.bootstrap.LayoutMasonry
30879  * @extends Roo.bootstrap.Component
30880  * Bootstrap Layout Masonry class
30881  * 
30882  * @constructor
30883  * Create a new Element
30884  * @param {Object} config The config object
30885  */
30886
30887 Roo.bootstrap.LayoutMasonry = function(config){
30888     
30889     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30890     
30891     this.bricks = [];
30892     
30893     Roo.bootstrap.LayoutMasonry.register(this);
30894     
30895     this.addEvents({
30896         // raw events
30897         /**
30898          * @event layout
30899          * Fire after layout the items
30900          * @param {Roo.bootstrap.LayoutMasonry} this
30901          * @param {Roo.EventObject} e
30902          */
30903         "layout" : true
30904     });
30905     
30906 };
30907
30908 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30909     
30910     /**
30911      * @cfg {Boolean} isLayoutInstant = no animation?
30912      */   
30913     isLayoutInstant : false, // needed?
30914    
30915     /**
30916      * @cfg {Number} boxWidth  width of the columns
30917      */   
30918     boxWidth : 450,
30919     
30920       /**
30921      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30922      */   
30923     boxHeight : 0,
30924     
30925     /**
30926      * @cfg {Number} padWidth padding below box..
30927      */   
30928     padWidth : 10, 
30929     
30930     /**
30931      * @cfg {Number} gutter gutter width..
30932      */   
30933     gutter : 10,
30934     
30935      /**
30936      * @cfg {Number} maxCols maximum number of columns
30937      */   
30938     
30939     maxCols: 0,
30940     
30941     /**
30942      * @cfg {Boolean} isAutoInitial defalut true
30943      */   
30944     isAutoInitial : true, 
30945     
30946     containerWidth: 0,
30947     
30948     /**
30949      * @cfg {Boolean} isHorizontal defalut false
30950      */   
30951     isHorizontal : false, 
30952
30953     currentSize : null,
30954     
30955     tag: 'div',
30956     
30957     cls: '',
30958     
30959     bricks: null, //CompositeElement
30960     
30961     cols : 1,
30962     
30963     _isLayoutInited : false,
30964     
30965 //    isAlternative : false, // only use for vertical layout...
30966     
30967     /**
30968      * @cfg {Number} alternativePadWidth padding below box..
30969      */   
30970     alternativePadWidth : 50,
30971     
30972     selectedBrick : [],
30973     
30974     getAutoCreate : function(){
30975         
30976         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30977         
30978         var cfg = {
30979             tag: this.tag,
30980             cls: 'blog-masonary-wrapper ' + this.cls,
30981             cn : {
30982                 cls : 'mas-boxes masonary'
30983             }
30984         };
30985         
30986         return cfg;
30987     },
30988     
30989     getChildContainer: function( )
30990     {
30991         if (this.boxesEl) {
30992             return this.boxesEl;
30993         }
30994         
30995         this.boxesEl = this.el.select('.mas-boxes').first();
30996         
30997         return this.boxesEl;
30998     },
30999     
31000     
31001     initEvents : function()
31002     {
31003         var _this = this;
31004         
31005         if(this.isAutoInitial){
31006             Roo.log('hook children rendered');
31007             this.on('childrenrendered', function() {
31008                 Roo.log('children rendered');
31009                 _this.initial();
31010             } ,this);
31011         }
31012     },
31013     
31014     initial : function()
31015     {
31016         this.selectedBrick = [];
31017         
31018         this.currentSize = this.el.getBox(true);
31019         
31020         Roo.EventManager.onWindowResize(this.resize, this); 
31021
31022         if(!this.isAutoInitial){
31023             this.layout();
31024             return;
31025         }
31026         
31027         this.layout();
31028         
31029         return;
31030         //this.layout.defer(500,this);
31031         
31032     },
31033     
31034     resize : function()
31035     {
31036         var cs = this.el.getBox(true);
31037         
31038         if (
31039                 this.currentSize.width == cs.width && 
31040                 this.currentSize.x == cs.x && 
31041                 this.currentSize.height == cs.height && 
31042                 this.currentSize.y == cs.y 
31043         ) {
31044             Roo.log("no change in with or X or Y");
31045             return;
31046         }
31047         
31048         this.currentSize = cs;
31049         
31050         this.layout();
31051         
31052     },
31053     
31054     layout : function()
31055     {   
31056         this._resetLayout();
31057         
31058         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31059         
31060         this.layoutItems( isInstant );
31061       
31062         this._isLayoutInited = true;
31063         
31064         this.fireEvent('layout', this);
31065         
31066     },
31067     
31068     _resetLayout : function()
31069     {
31070         if(this.isHorizontal){
31071             this.horizontalMeasureColumns();
31072             return;
31073         }
31074         
31075         this.verticalMeasureColumns();
31076         
31077     },
31078     
31079     verticalMeasureColumns : function()
31080     {
31081         this.getContainerWidth();
31082         
31083 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31084 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31085 //            return;
31086 //        }
31087         
31088         var boxWidth = this.boxWidth + this.padWidth;
31089         
31090         if(this.containerWidth < this.boxWidth){
31091             boxWidth = this.containerWidth
31092         }
31093         
31094         var containerWidth = this.containerWidth;
31095         
31096         var cols = Math.floor(containerWidth / boxWidth);
31097         
31098         this.cols = Math.max( cols, 1 );
31099         
31100         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31101         
31102         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31103         
31104         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31105         
31106         this.colWidth = boxWidth + avail - this.padWidth;
31107         
31108         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31109         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31110     },
31111     
31112     horizontalMeasureColumns : function()
31113     {
31114         this.getContainerWidth();
31115         
31116         var boxWidth = this.boxWidth;
31117         
31118         if(this.containerWidth < boxWidth){
31119             boxWidth = this.containerWidth;
31120         }
31121         
31122         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31123         
31124         this.el.setHeight(boxWidth);
31125         
31126     },
31127     
31128     getContainerWidth : function()
31129     {
31130         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31131     },
31132     
31133     layoutItems : function( isInstant )
31134     {
31135         Roo.log(this.bricks);
31136         
31137         var items = Roo.apply([], this.bricks);
31138         
31139         if(this.isHorizontal){
31140             this._horizontalLayoutItems( items , isInstant );
31141             return;
31142         }
31143         
31144 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31145 //            this._verticalAlternativeLayoutItems( items , isInstant );
31146 //            return;
31147 //        }
31148         
31149         this._verticalLayoutItems( items , isInstant );
31150         
31151     },
31152     
31153     _verticalLayoutItems : function ( items , isInstant)
31154     {
31155         if ( !items || !items.length ) {
31156             return;
31157         }
31158         
31159         var standard = [
31160             ['xs', 'xs', 'xs', 'tall'],
31161             ['xs', 'xs', 'tall'],
31162             ['xs', 'xs', 'sm'],
31163             ['xs', 'xs', 'xs'],
31164             ['xs', 'tall'],
31165             ['xs', 'sm'],
31166             ['xs', 'xs'],
31167             ['xs'],
31168             
31169             ['sm', 'xs', 'xs'],
31170             ['sm', 'xs'],
31171             ['sm'],
31172             
31173             ['tall', 'xs', 'xs', 'xs'],
31174             ['tall', 'xs', 'xs'],
31175             ['tall', 'xs'],
31176             ['tall']
31177             
31178         ];
31179         
31180         var queue = [];
31181         
31182         var boxes = [];
31183         
31184         var box = [];
31185         
31186         Roo.each(items, function(item, k){
31187             
31188             switch (item.size) {
31189                 // these layouts take up a full box,
31190                 case 'md' :
31191                 case 'md-left' :
31192                 case 'md-right' :
31193                 case 'wide' :
31194                     
31195                     if(box.length){
31196                         boxes.push(box);
31197                         box = [];
31198                     }
31199                     
31200                     boxes.push([item]);
31201                     
31202                     break;
31203                     
31204                 case 'xs' :
31205                 case 'sm' :
31206                 case 'tall' :
31207                     
31208                     box.push(item);
31209                     
31210                     break;
31211                 default :
31212                     break;
31213                     
31214             }
31215             
31216         }, this);
31217         
31218         if(box.length){
31219             boxes.push(box);
31220             box = [];
31221         }
31222         
31223         var filterPattern = function(box, length)
31224         {
31225             if(!box.length){
31226                 return;
31227             }
31228             
31229             var match = false;
31230             
31231             var pattern = box.slice(0, length);
31232             
31233             var format = [];
31234             
31235             Roo.each(pattern, function(i){
31236                 format.push(i.size);
31237             }, this);
31238             
31239             Roo.each(standard, function(s){
31240                 
31241                 if(String(s) != String(format)){
31242                     return;
31243                 }
31244                 
31245                 match = true;
31246                 return false;
31247                 
31248             }, this);
31249             
31250             if(!match && length == 1){
31251                 return;
31252             }
31253             
31254             if(!match){
31255                 filterPattern(box, length - 1);
31256                 return;
31257             }
31258                 
31259             queue.push(pattern);
31260
31261             box = box.slice(length, box.length);
31262
31263             filterPattern(box, 4);
31264
31265             return;
31266             
31267         }
31268         
31269         Roo.each(boxes, function(box, k){
31270             
31271             if(!box.length){
31272                 return;
31273             }
31274             
31275             if(box.length == 1){
31276                 queue.push(box);
31277                 return;
31278             }
31279             
31280             filterPattern(box, 4);
31281             
31282         }, this);
31283         
31284         this._processVerticalLayoutQueue( queue, isInstant );
31285         
31286     },
31287     
31288 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31289 //    {
31290 //        if ( !items || !items.length ) {
31291 //            return;
31292 //        }
31293 //
31294 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31295 //        
31296 //    },
31297     
31298     _horizontalLayoutItems : function ( items , isInstant)
31299     {
31300         if ( !items || !items.length || items.length < 3) {
31301             return;
31302         }
31303         
31304         items.reverse();
31305         
31306         var eItems = items.slice(0, 3);
31307         
31308         items = items.slice(3, items.length);
31309         
31310         var standard = [
31311             ['xs', 'xs', 'xs', 'wide'],
31312             ['xs', 'xs', 'wide'],
31313             ['xs', 'xs', 'sm'],
31314             ['xs', 'xs', 'xs'],
31315             ['xs', 'wide'],
31316             ['xs', 'sm'],
31317             ['xs', 'xs'],
31318             ['xs'],
31319             
31320             ['sm', 'xs', 'xs'],
31321             ['sm', 'xs'],
31322             ['sm'],
31323             
31324             ['wide', 'xs', 'xs', 'xs'],
31325             ['wide', 'xs', 'xs'],
31326             ['wide', 'xs'],
31327             ['wide'],
31328             
31329             ['wide-thin']
31330         ];
31331         
31332         var queue = [];
31333         
31334         var boxes = [];
31335         
31336         var box = [];
31337         
31338         Roo.each(items, function(item, k){
31339             
31340             switch (item.size) {
31341                 case 'md' :
31342                 case 'md-left' :
31343                 case 'md-right' :
31344                 case 'tall' :
31345                     
31346                     if(box.length){
31347                         boxes.push(box);
31348                         box = [];
31349                     }
31350                     
31351                     boxes.push([item]);
31352                     
31353                     break;
31354                     
31355                 case 'xs' :
31356                 case 'sm' :
31357                 case 'wide' :
31358                 case 'wide-thin' :
31359                     
31360                     box.push(item);
31361                     
31362                     break;
31363                 default :
31364                     break;
31365                     
31366             }
31367             
31368         }, this);
31369         
31370         if(box.length){
31371             boxes.push(box);
31372             box = [];
31373         }
31374         
31375         var filterPattern = function(box, length)
31376         {
31377             if(!box.length){
31378                 return;
31379             }
31380             
31381             var match = false;
31382             
31383             var pattern = box.slice(0, length);
31384             
31385             var format = [];
31386             
31387             Roo.each(pattern, function(i){
31388                 format.push(i.size);
31389             }, this);
31390             
31391             Roo.each(standard, function(s){
31392                 
31393                 if(String(s) != String(format)){
31394                     return;
31395                 }
31396                 
31397                 match = true;
31398                 return false;
31399                 
31400             }, this);
31401             
31402             if(!match && length == 1){
31403                 return;
31404             }
31405             
31406             if(!match){
31407                 filterPattern(box, length - 1);
31408                 return;
31409             }
31410                 
31411             queue.push(pattern);
31412
31413             box = box.slice(length, box.length);
31414
31415             filterPattern(box, 4);
31416
31417             return;
31418             
31419         }
31420         
31421         Roo.each(boxes, function(box, k){
31422             
31423             if(!box.length){
31424                 return;
31425             }
31426             
31427             if(box.length == 1){
31428                 queue.push(box);
31429                 return;
31430             }
31431             
31432             filterPattern(box, 4);
31433             
31434         }, this);
31435         
31436         
31437         var prune = [];
31438         
31439         var pos = this.el.getBox(true);
31440         
31441         var minX = pos.x;
31442         
31443         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31444         
31445         var hit_end = false;
31446         
31447         Roo.each(queue, function(box){
31448             
31449             if(hit_end){
31450                 
31451                 Roo.each(box, function(b){
31452                 
31453                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31454                     b.el.hide();
31455
31456                 }, this);
31457
31458                 return;
31459             }
31460             
31461             var mx = 0;
31462             
31463             Roo.each(box, function(b){
31464                 
31465                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31466                 b.el.show();
31467
31468                 mx = Math.max(mx, b.x);
31469                 
31470             }, this);
31471             
31472             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31473             
31474             if(maxX < minX){
31475                 
31476                 Roo.each(box, function(b){
31477                 
31478                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31479                     b.el.hide();
31480                     
31481                 }, this);
31482                 
31483                 hit_end = true;
31484                 
31485                 return;
31486             }
31487             
31488             prune.push(box);
31489             
31490         }, this);
31491         
31492         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31493     },
31494     
31495     /** Sets position of item in DOM
31496     * @param {Element} item
31497     * @param {Number} x - horizontal position
31498     * @param {Number} y - vertical position
31499     * @param {Boolean} isInstant - disables transitions
31500     */
31501     _processVerticalLayoutQueue : function( queue, isInstant )
31502     {
31503         var pos = this.el.getBox(true);
31504         var x = pos.x;
31505         var y = pos.y;
31506         var maxY = [];
31507         
31508         for (var i = 0; i < this.cols; i++){
31509             maxY[i] = pos.y;
31510         }
31511         
31512         Roo.each(queue, function(box, k){
31513             
31514             var col = k % this.cols;
31515             
31516             Roo.each(box, function(b,kk){
31517                 
31518                 b.el.position('absolute');
31519                 
31520                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31521                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31522                 
31523                 if(b.size == 'md-left' || b.size == 'md-right'){
31524                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31525                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31526                 }
31527                 
31528                 b.el.setWidth(width);
31529                 b.el.setHeight(height);
31530                 // iframe?
31531                 b.el.select('iframe',true).setSize(width,height);
31532                 
31533             }, this);
31534             
31535             for (var i = 0; i < this.cols; i++){
31536                 
31537                 if(maxY[i] < maxY[col]){
31538                     col = i;
31539                     continue;
31540                 }
31541                 
31542                 col = Math.min(col, i);
31543                 
31544             }
31545             
31546             x = pos.x + col * (this.colWidth + this.padWidth);
31547             
31548             y = maxY[col];
31549             
31550             var positions = [];
31551             
31552             switch (box.length){
31553                 case 1 :
31554                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31555                     break;
31556                 case 2 :
31557                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31558                     break;
31559                 case 3 :
31560                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31561                     break;
31562                 case 4 :
31563                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31564                     break;
31565                 default :
31566                     break;
31567             }
31568             
31569             Roo.each(box, function(b,kk){
31570                 
31571                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31572                 
31573                 var sz = b.el.getSize();
31574                 
31575                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31576                 
31577             }, this);
31578             
31579         }, this);
31580         
31581         var mY = 0;
31582         
31583         for (var i = 0; i < this.cols; i++){
31584             mY = Math.max(mY, maxY[i]);
31585         }
31586         
31587         this.el.setHeight(mY - pos.y);
31588         
31589     },
31590     
31591 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31592 //    {
31593 //        var pos = this.el.getBox(true);
31594 //        var x = pos.x;
31595 //        var y = pos.y;
31596 //        var maxX = pos.right;
31597 //        
31598 //        var maxHeight = 0;
31599 //        
31600 //        Roo.each(items, function(item, k){
31601 //            
31602 //            var c = k % 2;
31603 //            
31604 //            item.el.position('absolute');
31605 //                
31606 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31607 //
31608 //            item.el.setWidth(width);
31609 //
31610 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31611 //
31612 //            item.el.setHeight(height);
31613 //            
31614 //            if(c == 0){
31615 //                item.el.setXY([x, y], isInstant ? false : true);
31616 //            } else {
31617 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31618 //            }
31619 //            
31620 //            y = y + height + this.alternativePadWidth;
31621 //            
31622 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31623 //            
31624 //        }, this);
31625 //        
31626 //        this.el.setHeight(maxHeight);
31627 //        
31628 //    },
31629     
31630     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31631     {
31632         var pos = this.el.getBox(true);
31633         
31634         var minX = pos.x;
31635         var minY = pos.y;
31636         
31637         var maxX = pos.right;
31638         
31639         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31640         
31641         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31642         
31643         Roo.each(queue, function(box, k){
31644             
31645             Roo.each(box, function(b, kk){
31646                 
31647                 b.el.position('absolute');
31648                 
31649                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31650                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31651                 
31652                 if(b.size == 'md-left' || b.size == 'md-right'){
31653                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31654                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31655                 }
31656                 
31657                 b.el.setWidth(width);
31658                 b.el.setHeight(height);
31659                 
31660             }, this);
31661             
31662             if(!box.length){
31663                 return;
31664             }
31665             
31666             var positions = [];
31667             
31668             switch (box.length){
31669                 case 1 :
31670                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31671                     break;
31672                 case 2 :
31673                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31674                     break;
31675                 case 3 :
31676                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31677                     break;
31678                 case 4 :
31679                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31680                     break;
31681                 default :
31682                     break;
31683             }
31684             
31685             Roo.each(box, function(b,kk){
31686                 
31687                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31688                 
31689                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31690                 
31691             }, this);
31692             
31693         }, this);
31694         
31695     },
31696     
31697     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31698     {
31699         Roo.each(eItems, function(b,k){
31700             
31701             b.size = (k == 0) ? 'sm' : 'xs';
31702             b.x = (k == 0) ? 2 : 1;
31703             b.y = (k == 0) ? 2 : 1;
31704             
31705             b.el.position('absolute');
31706             
31707             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31708                 
31709             b.el.setWidth(width);
31710             
31711             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31712             
31713             b.el.setHeight(height);
31714             
31715         }, this);
31716
31717         var positions = [];
31718         
31719         positions.push({
31720             x : maxX - this.unitWidth * 2 - this.gutter,
31721             y : minY
31722         });
31723         
31724         positions.push({
31725             x : maxX - this.unitWidth,
31726             y : minY + (this.unitWidth + this.gutter) * 2
31727         });
31728         
31729         positions.push({
31730             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31731             y : minY
31732         });
31733         
31734         Roo.each(eItems, function(b,k){
31735             
31736             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31737
31738         }, this);
31739         
31740     },
31741     
31742     getVerticalOneBoxColPositions : function(x, y, box)
31743     {
31744         var pos = [];
31745         
31746         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31747         
31748         if(box[0].size == 'md-left'){
31749             rand = 0;
31750         }
31751         
31752         if(box[0].size == 'md-right'){
31753             rand = 1;
31754         }
31755         
31756         pos.push({
31757             x : x + (this.unitWidth + this.gutter) * rand,
31758             y : y
31759         });
31760         
31761         return pos;
31762     },
31763     
31764     getVerticalTwoBoxColPositions : function(x, y, box)
31765     {
31766         var pos = [];
31767         
31768         if(box[0].size == 'xs'){
31769             
31770             pos.push({
31771                 x : x,
31772                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31773             });
31774
31775             pos.push({
31776                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31777                 y : y
31778             });
31779             
31780             return pos;
31781             
31782         }
31783         
31784         pos.push({
31785             x : x,
31786             y : y
31787         });
31788
31789         pos.push({
31790             x : x + (this.unitWidth + this.gutter) * 2,
31791             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31792         });
31793         
31794         return pos;
31795         
31796     },
31797     
31798     getVerticalThreeBoxColPositions : function(x, y, box)
31799     {
31800         var pos = [];
31801         
31802         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31803             
31804             pos.push({
31805                 x : x,
31806                 y : y
31807             });
31808
31809             pos.push({
31810                 x : x + (this.unitWidth + this.gutter) * 1,
31811                 y : y
31812             });
31813             
31814             pos.push({
31815                 x : x + (this.unitWidth + this.gutter) * 2,
31816                 y : y
31817             });
31818             
31819             return pos;
31820             
31821         }
31822         
31823         if(box[0].size == 'xs' && box[1].size == 'xs'){
31824             
31825             pos.push({
31826                 x : x,
31827                 y : y
31828             });
31829
31830             pos.push({
31831                 x : x,
31832                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31833             });
31834             
31835             pos.push({
31836                 x : x + (this.unitWidth + this.gutter) * 1,
31837                 y : y
31838             });
31839             
31840             return pos;
31841             
31842         }
31843         
31844         pos.push({
31845             x : x,
31846             y : y
31847         });
31848
31849         pos.push({
31850             x : x + (this.unitWidth + this.gutter) * 2,
31851             y : y
31852         });
31853
31854         pos.push({
31855             x : x + (this.unitWidth + this.gutter) * 2,
31856             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31857         });
31858             
31859         return pos;
31860         
31861     },
31862     
31863     getVerticalFourBoxColPositions : function(x, y, box)
31864     {
31865         var pos = [];
31866         
31867         if(box[0].size == 'xs'){
31868             
31869             pos.push({
31870                 x : x,
31871                 y : y
31872             });
31873
31874             pos.push({
31875                 x : x,
31876                 y : y + (this.unitHeight + this.gutter) * 1
31877             });
31878             
31879             pos.push({
31880                 x : x,
31881                 y : y + (this.unitHeight + this.gutter) * 2
31882             });
31883             
31884             pos.push({
31885                 x : x + (this.unitWidth + this.gutter) * 1,
31886                 y : y
31887             });
31888             
31889             return pos;
31890             
31891         }
31892         
31893         pos.push({
31894             x : x,
31895             y : y
31896         });
31897
31898         pos.push({
31899             x : x + (this.unitWidth + this.gutter) * 2,
31900             y : y
31901         });
31902
31903         pos.push({
31904             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31905             y : y + (this.unitHeight + this.gutter) * 1
31906         });
31907
31908         pos.push({
31909             x : x + (this.unitWidth + this.gutter) * 2,
31910             y : y + (this.unitWidth + this.gutter) * 2
31911         });
31912
31913         return pos;
31914         
31915     },
31916     
31917     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31918     {
31919         var pos = [];
31920         
31921         if(box[0].size == 'md-left'){
31922             pos.push({
31923                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31924                 y : minY
31925             });
31926             
31927             return pos;
31928         }
31929         
31930         if(box[0].size == 'md-right'){
31931             pos.push({
31932                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31933                 y : minY + (this.unitWidth + this.gutter) * 1
31934             });
31935             
31936             return pos;
31937         }
31938         
31939         var rand = Math.floor(Math.random() * (4 - box[0].y));
31940         
31941         pos.push({
31942             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31943             y : minY + (this.unitWidth + this.gutter) * rand
31944         });
31945         
31946         return pos;
31947         
31948     },
31949     
31950     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31951     {
31952         var pos = [];
31953         
31954         if(box[0].size == 'xs'){
31955             
31956             pos.push({
31957                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31958                 y : minY
31959             });
31960
31961             pos.push({
31962                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31963                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31964             });
31965             
31966             return pos;
31967             
31968         }
31969         
31970         pos.push({
31971             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31972             y : minY
31973         });
31974
31975         pos.push({
31976             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31977             y : minY + (this.unitWidth + this.gutter) * 2
31978         });
31979         
31980         return pos;
31981         
31982     },
31983     
31984     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31985     {
31986         var pos = [];
31987         
31988         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31989             
31990             pos.push({
31991                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31992                 y : minY
31993             });
31994
31995             pos.push({
31996                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31997                 y : minY + (this.unitWidth + this.gutter) * 1
31998             });
31999             
32000             pos.push({
32001                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32002                 y : minY + (this.unitWidth + this.gutter) * 2
32003             });
32004             
32005             return pos;
32006             
32007         }
32008         
32009         if(box[0].size == 'xs' && box[1].size == 'xs'){
32010             
32011             pos.push({
32012                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32013                 y : minY
32014             });
32015
32016             pos.push({
32017                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32018                 y : minY
32019             });
32020             
32021             pos.push({
32022                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32023                 y : minY + (this.unitWidth + this.gutter) * 1
32024             });
32025             
32026             return pos;
32027             
32028         }
32029         
32030         pos.push({
32031             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32032             y : minY
32033         });
32034
32035         pos.push({
32036             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32037             y : minY + (this.unitWidth + this.gutter) * 2
32038         });
32039
32040         pos.push({
32041             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32042             y : minY + (this.unitWidth + this.gutter) * 2
32043         });
32044             
32045         return pos;
32046         
32047     },
32048     
32049     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32050     {
32051         var pos = [];
32052         
32053         if(box[0].size == 'xs'){
32054             
32055             pos.push({
32056                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32057                 y : minY
32058             });
32059
32060             pos.push({
32061                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32062                 y : minY
32063             });
32064             
32065             pos.push({
32066                 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),
32067                 y : minY
32068             });
32069             
32070             pos.push({
32071                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32072                 y : minY + (this.unitWidth + this.gutter) * 1
32073             });
32074             
32075             return pos;
32076             
32077         }
32078         
32079         pos.push({
32080             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32081             y : minY
32082         });
32083         
32084         pos.push({
32085             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32086             y : minY + (this.unitWidth + this.gutter) * 2
32087         });
32088         
32089         pos.push({
32090             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32091             y : minY + (this.unitWidth + this.gutter) * 2
32092         });
32093         
32094         pos.push({
32095             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),
32096             y : minY + (this.unitWidth + this.gutter) * 2
32097         });
32098
32099         return pos;
32100         
32101     },
32102     
32103     /**
32104     * remove a Masonry Brick
32105     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32106     */
32107     removeBrick : function(brick_id)
32108     {
32109         if (!brick_id) {
32110             return;
32111         }
32112         
32113         for (var i = 0; i<this.bricks.length; i++) {
32114             if (this.bricks[i].id == brick_id) {
32115                 this.bricks.splice(i,1);
32116                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32117                 this.initial();
32118             }
32119         }
32120     },
32121     
32122     /**
32123     * adds a Masonry Brick
32124     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32125     */
32126     addBrick : function(cfg)
32127     {
32128         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32129         //this.register(cn);
32130         cn.parentId = this.id;
32131         cn.render(this.el);
32132         return cn;
32133     },
32134     
32135     /**
32136     * register a Masonry Brick
32137     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32138     */
32139     
32140     register : function(brick)
32141     {
32142         this.bricks.push(brick);
32143         brick.masonryId = this.id;
32144     },
32145     
32146     /**
32147     * clear all the Masonry Brick
32148     */
32149     clearAll : function()
32150     {
32151         this.bricks = [];
32152         //this.getChildContainer().dom.innerHTML = "";
32153         this.el.dom.innerHTML = '';
32154     },
32155     
32156     getSelected : function()
32157     {
32158         if (!this.selectedBrick) {
32159             return false;
32160         }
32161         
32162         return this.selectedBrick;
32163     }
32164 });
32165
32166 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32167     
32168     groups: {},
32169      /**
32170     * register a Masonry Layout
32171     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32172     */
32173     
32174     register : function(layout)
32175     {
32176         this.groups[layout.id] = layout;
32177     },
32178     /**
32179     * fetch a  Masonry Layout based on the masonry layout ID
32180     * @param {string} the masonry layout to add
32181     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32182     */
32183     
32184     get: function(layout_id) {
32185         if (typeof(this.groups[layout_id]) == 'undefined') {
32186             return false;
32187         }
32188         return this.groups[layout_id] ;
32189     }
32190     
32191     
32192     
32193 });
32194
32195  
32196
32197  /**
32198  *
32199  * This is based on 
32200  * http://masonry.desandro.com
32201  *
32202  * The idea is to render all the bricks based on vertical width...
32203  *
32204  * The original code extends 'outlayer' - we might need to use that....
32205  * 
32206  */
32207
32208
32209 /**
32210  * @class Roo.bootstrap.LayoutMasonryAuto
32211  * @extends Roo.bootstrap.Component
32212  * Bootstrap Layout Masonry class
32213  * 
32214  * @constructor
32215  * Create a new Element
32216  * @param {Object} config The config object
32217  */
32218
32219 Roo.bootstrap.LayoutMasonryAuto = function(config){
32220     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32221 };
32222
32223 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32224     
32225       /**
32226      * @cfg {Boolean} isFitWidth  - resize the width..
32227      */   
32228     isFitWidth : false,  // options..
32229     /**
32230      * @cfg {Boolean} isOriginLeft = left align?
32231      */   
32232     isOriginLeft : true,
32233     /**
32234      * @cfg {Boolean} isOriginTop = top align?
32235      */   
32236     isOriginTop : false,
32237     /**
32238      * @cfg {Boolean} isLayoutInstant = no animation?
32239      */   
32240     isLayoutInstant : false, // needed?
32241     /**
32242      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32243      */   
32244     isResizingContainer : true,
32245     /**
32246      * @cfg {Number} columnWidth  width of the columns 
32247      */   
32248     
32249     columnWidth : 0,
32250     
32251     /**
32252      * @cfg {Number} maxCols maximum number of columns
32253      */   
32254     
32255     maxCols: 0,
32256     /**
32257      * @cfg {Number} padHeight padding below box..
32258      */   
32259     
32260     padHeight : 10, 
32261     
32262     /**
32263      * @cfg {Boolean} isAutoInitial defalut true
32264      */   
32265     
32266     isAutoInitial : true, 
32267     
32268     // private?
32269     gutter : 0,
32270     
32271     containerWidth: 0,
32272     initialColumnWidth : 0,
32273     currentSize : null,
32274     
32275     colYs : null, // array.
32276     maxY : 0,
32277     padWidth: 10,
32278     
32279     
32280     tag: 'div',
32281     cls: '',
32282     bricks: null, //CompositeElement
32283     cols : 0, // array?
32284     // element : null, // wrapped now this.el
32285     _isLayoutInited : null, 
32286     
32287     
32288     getAutoCreate : function(){
32289         
32290         var cfg = {
32291             tag: this.tag,
32292             cls: 'blog-masonary-wrapper ' + this.cls,
32293             cn : {
32294                 cls : 'mas-boxes masonary'
32295             }
32296         };
32297         
32298         return cfg;
32299     },
32300     
32301     getChildContainer: function( )
32302     {
32303         if (this.boxesEl) {
32304             return this.boxesEl;
32305         }
32306         
32307         this.boxesEl = this.el.select('.mas-boxes').first();
32308         
32309         return this.boxesEl;
32310     },
32311     
32312     
32313     initEvents : function()
32314     {
32315         var _this = this;
32316         
32317         if(this.isAutoInitial){
32318             Roo.log('hook children rendered');
32319             this.on('childrenrendered', function() {
32320                 Roo.log('children rendered');
32321                 _this.initial();
32322             } ,this);
32323         }
32324         
32325     },
32326     
32327     initial : function()
32328     {
32329         this.reloadItems();
32330
32331         this.currentSize = this.el.getBox(true);
32332
32333         /// was window resize... - let's see if this works..
32334         Roo.EventManager.onWindowResize(this.resize, this); 
32335
32336         if(!this.isAutoInitial){
32337             this.layout();
32338             return;
32339         }
32340         
32341         this.layout.defer(500,this);
32342     },
32343     
32344     reloadItems: function()
32345     {
32346         this.bricks = this.el.select('.masonry-brick', true);
32347         
32348         this.bricks.each(function(b) {
32349             //Roo.log(b.getSize());
32350             if (!b.attr('originalwidth')) {
32351                 b.attr('originalwidth',  b.getSize().width);
32352             }
32353             
32354         });
32355         
32356         Roo.log(this.bricks.elements.length);
32357     },
32358     
32359     resize : function()
32360     {
32361         Roo.log('resize');
32362         var cs = this.el.getBox(true);
32363         
32364         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32365             Roo.log("no change in with or X");
32366             return;
32367         }
32368         this.currentSize = cs;
32369         this.layout();
32370     },
32371     
32372     layout : function()
32373     {
32374          Roo.log('layout');
32375         this._resetLayout();
32376         //this._manageStamps();
32377       
32378         // don't animate first layout
32379         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32380         this.layoutItems( isInstant );
32381       
32382         // flag for initalized
32383         this._isLayoutInited = true;
32384     },
32385     
32386     layoutItems : function( isInstant )
32387     {
32388         //var items = this._getItemsForLayout( this.items );
32389         // original code supports filtering layout items.. we just ignore it..
32390         
32391         this._layoutItems( this.bricks , isInstant );
32392       
32393         this._postLayout();
32394     },
32395     _layoutItems : function ( items , isInstant)
32396     {
32397        //this.fireEvent( 'layout', this, items );
32398     
32399
32400         if ( !items || !items.elements.length ) {
32401           // no items, emit event with empty array
32402             return;
32403         }
32404
32405         var queue = [];
32406         items.each(function(item) {
32407             Roo.log("layout item");
32408             Roo.log(item);
32409             // get x/y object from method
32410             var position = this._getItemLayoutPosition( item );
32411             // enqueue
32412             position.item = item;
32413             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32414             queue.push( position );
32415         }, this);
32416       
32417         this._processLayoutQueue( queue );
32418     },
32419     /** Sets position of item in DOM
32420     * @param {Element} item
32421     * @param {Number} x - horizontal position
32422     * @param {Number} y - vertical position
32423     * @param {Boolean} isInstant - disables transitions
32424     */
32425     _processLayoutQueue : function( queue )
32426     {
32427         for ( var i=0, len = queue.length; i < len; i++ ) {
32428             var obj = queue[i];
32429             obj.item.position('absolute');
32430             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32431         }
32432     },
32433       
32434     
32435     /**
32436     * Any logic you want to do after each layout,
32437     * i.e. size the container
32438     */
32439     _postLayout : function()
32440     {
32441         this.resizeContainer();
32442     },
32443     
32444     resizeContainer : function()
32445     {
32446         if ( !this.isResizingContainer ) {
32447             return;
32448         }
32449         var size = this._getContainerSize();
32450         if ( size ) {
32451             this.el.setSize(size.width,size.height);
32452             this.boxesEl.setSize(size.width,size.height);
32453         }
32454     },
32455     
32456     
32457     
32458     _resetLayout : function()
32459     {
32460         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32461         this.colWidth = this.el.getWidth();
32462         //this.gutter = this.el.getWidth(); 
32463         
32464         this.measureColumns();
32465
32466         // reset column Y
32467         var i = this.cols;
32468         this.colYs = [];
32469         while (i--) {
32470             this.colYs.push( 0 );
32471         }
32472     
32473         this.maxY = 0;
32474     },
32475
32476     measureColumns : function()
32477     {
32478         this.getContainerWidth();
32479       // if columnWidth is 0, default to outerWidth of first item
32480         if ( !this.columnWidth ) {
32481             var firstItem = this.bricks.first();
32482             Roo.log(firstItem);
32483             this.columnWidth  = this.containerWidth;
32484             if (firstItem && firstItem.attr('originalwidth') ) {
32485                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32486             }
32487             // columnWidth fall back to item of first element
32488             Roo.log("set column width?");
32489                         this.initialColumnWidth = this.columnWidth  ;
32490
32491             // if first elem has no width, default to size of container
32492             
32493         }
32494         
32495         
32496         if (this.initialColumnWidth) {
32497             this.columnWidth = this.initialColumnWidth;
32498         }
32499         
32500         
32501             
32502         // column width is fixed at the top - however if container width get's smaller we should
32503         // reduce it...
32504         
32505         // this bit calcs how man columns..
32506             
32507         var columnWidth = this.columnWidth += this.gutter;
32508       
32509         // calculate columns
32510         var containerWidth = this.containerWidth + this.gutter;
32511         
32512         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32513         // fix rounding errors, typically with gutters
32514         var excess = columnWidth - containerWidth % columnWidth;
32515         
32516         
32517         // if overshoot is less than a pixel, round up, otherwise floor it
32518         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32519         cols = Math[ mathMethod ]( cols );
32520         this.cols = Math.max( cols, 1 );
32521         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32522         
32523          // padding positioning..
32524         var totalColWidth = this.cols * this.columnWidth;
32525         var padavail = this.containerWidth - totalColWidth;
32526         // so for 2 columns - we need 3 'pads'
32527         
32528         var padNeeded = (1+this.cols) * this.padWidth;
32529         
32530         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32531         
32532         this.columnWidth += padExtra
32533         //this.padWidth = Math.floor(padavail /  ( this.cols));
32534         
32535         // adjust colum width so that padding is fixed??
32536         
32537         // we have 3 columns ... total = width * 3
32538         // we have X left over... that should be used by 
32539         
32540         //if (this.expandC) {
32541             
32542         //}
32543         
32544         
32545         
32546     },
32547     
32548     getContainerWidth : function()
32549     {
32550        /* // container is parent if fit width
32551         var container = this.isFitWidth ? this.element.parentNode : this.element;
32552         // check that this.size and size are there
32553         // IE8 triggers resize on body size change, so they might not be
32554         
32555         var size = getSize( container );  //FIXME
32556         this.containerWidth = size && size.innerWidth; //FIXME
32557         */
32558          
32559         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32560         
32561     },
32562     
32563     _getItemLayoutPosition : function( item )  // what is item?
32564     {
32565         // we resize the item to our columnWidth..
32566       
32567         item.setWidth(this.columnWidth);
32568         item.autoBoxAdjust  = false;
32569         
32570         var sz = item.getSize();
32571  
32572         // how many columns does this brick span
32573         var remainder = this.containerWidth % this.columnWidth;
32574         
32575         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32576         // round if off by 1 pixel, otherwise use ceil
32577         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32578         colSpan = Math.min( colSpan, this.cols );
32579         
32580         // normally this should be '1' as we dont' currently allow multi width columns..
32581         
32582         var colGroup = this._getColGroup( colSpan );
32583         // get the minimum Y value from the columns
32584         var minimumY = Math.min.apply( Math, colGroup );
32585         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32586         
32587         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32588          
32589         // position the brick
32590         var position = {
32591             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32592             y: this.currentSize.y + minimumY + this.padHeight
32593         };
32594         
32595         Roo.log(position);
32596         // apply setHeight to necessary columns
32597         var setHeight = minimumY + sz.height + this.padHeight;
32598         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32599         
32600         var setSpan = this.cols + 1 - colGroup.length;
32601         for ( var i = 0; i < setSpan; i++ ) {
32602           this.colYs[ shortColIndex + i ] = setHeight ;
32603         }
32604       
32605         return position;
32606     },
32607     
32608     /**
32609      * @param {Number} colSpan - number of columns the element spans
32610      * @returns {Array} colGroup
32611      */
32612     _getColGroup : function( colSpan )
32613     {
32614         if ( colSpan < 2 ) {
32615           // if brick spans only one column, use all the column Ys
32616           return this.colYs;
32617         }
32618       
32619         var colGroup = [];
32620         // how many different places could this brick fit horizontally
32621         var groupCount = this.cols + 1 - colSpan;
32622         // for each group potential horizontal position
32623         for ( var i = 0; i < groupCount; i++ ) {
32624           // make an array of colY values for that one group
32625           var groupColYs = this.colYs.slice( i, i + colSpan );
32626           // and get the max value of the array
32627           colGroup[i] = Math.max.apply( Math, groupColYs );
32628         }
32629         return colGroup;
32630     },
32631     /*
32632     _manageStamp : function( stamp )
32633     {
32634         var stampSize =  stamp.getSize();
32635         var offset = stamp.getBox();
32636         // get the columns that this stamp affects
32637         var firstX = this.isOriginLeft ? offset.x : offset.right;
32638         var lastX = firstX + stampSize.width;
32639         var firstCol = Math.floor( firstX / this.columnWidth );
32640         firstCol = Math.max( 0, firstCol );
32641         
32642         var lastCol = Math.floor( lastX / this.columnWidth );
32643         // lastCol should not go over if multiple of columnWidth #425
32644         lastCol -= lastX % this.columnWidth ? 0 : 1;
32645         lastCol = Math.min( this.cols - 1, lastCol );
32646         
32647         // set colYs to bottom of the stamp
32648         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32649             stampSize.height;
32650             
32651         for ( var i = firstCol; i <= lastCol; i++ ) {
32652           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32653         }
32654     },
32655     */
32656     
32657     _getContainerSize : function()
32658     {
32659         this.maxY = Math.max.apply( Math, this.colYs );
32660         var size = {
32661             height: this.maxY
32662         };
32663       
32664         if ( this.isFitWidth ) {
32665             size.width = this._getContainerFitWidth();
32666         }
32667       
32668         return size;
32669     },
32670     
32671     _getContainerFitWidth : function()
32672     {
32673         var unusedCols = 0;
32674         // count unused columns
32675         var i = this.cols;
32676         while ( --i ) {
32677           if ( this.colYs[i] !== 0 ) {
32678             break;
32679           }
32680           unusedCols++;
32681         }
32682         // fit container to columns that have been used
32683         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32684     },
32685     
32686     needsResizeLayout : function()
32687     {
32688         var previousWidth = this.containerWidth;
32689         this.getContainerWidth();
32690         return previousWidth !== this.containerWidth;
32691     }
32692  
32693 });
32694
32695  
32696
32697  /*
32698  * - LGPL
32699  *
32700  * element
32701  * 
32702  */
32703
32704 /**
32705  * @class Roo.bootstrap.MasonryBrick
32706  * @extends Roo.bootstrap.Component
32707  * Bootstrap MasonryBrick class
32708  * 
32709  * @constructor
32710  * Create a new MasonryBrick
32711  * @param {Object} config The config object
32712  */
32713
32714 Roo.bootstrap.MasonryBrick = function(config){
32715     
32716     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32717     
32718     Roo.bootstrap.MasonryBrick.register(this);
32719     
32720     this.addEvents({
32721         // raw events
32722         /**
32723          * @event click
32724          * When a MasonryBrick is clcik
32725          * @param {Roo.bootstrap.MasonryBrick} this
32726          * @param {Roo.EventObject} e
32727          */
32728         "click" : true
32729     });
32730 };
32731
32732 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32733     
32734     /**
32735      * @cfg {String} title
32736      */   
32737     title : '',
32738     /**
32739      * @cfg {String} html
32740      */   
32741     html : '',
32742     /**
32743      * @cfg {String} bgimage
32744      */   
32745     bgimage : '',
32746     /**
32747      * @cfg {String} videourl
32748      */   
32749     videourl : '',
32750     /**
32751      * @cfg {String} cls
32752      */   
32753     cls : '',
32754     /**
32755      * @cfg {String} href
32756      */   
32757     href : '',
32758     /**
32759      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32760      */   
32761     size : 'xs',
32762     
32763     /**
32764      * @cfg {String} placetitle (center|bottom)
32765      */   
32766     placetitle : '',
32767     
32768     /**
32769      * @cfg {Boolean} isFitContainer defalut true
32770      */   
32771     isFitContainer : true, 
32772     
32773     /**
32774      * @cfg {Boolean} preventDefault defalut false
32775      */   
32776     preventDefault : false, 
32777     
32778     /**
32779      * @cfg {Boolean} inverse defalut false
32780      */   
32781     maskInverse : false, 
32782     
32783     getAutoCreate : function()
32784     {
32785         if(!this.isFitContainer){
32786             return this.getSplitAutoCreate();
32787         }
32788         
32789         var cls = 'masonry-brick masonry-brick-full';
32790         
32791         if(this.href.length){
32792             cls += ' masonry-brick-link';
32793         }
32794         
32795         if(this.bgimage.length){
32796             cls += ' masonry-brick-image';
32797         }
32798         
32799         if(this.maskInverse){
32800             cls += ' mask-inverse';
32801         }
32802         
32803         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32804             cls += ' enable-mask';
32805         }
32806         
32807         if(this.size){
32808             cls += ' masonry-' + this.size + '-brick';
32809         }
32810         
32811         if(this.placetitle.length){
32812             
32813             switch (this.placetitle) {
32814                 case 'center' :
32815                     cls += ' masonry-center-title';
32816                     break;
32817                 case 'bottom' :
32818                     cls += ' masonry-bottom-title';
32819                     break;
32820                 default:
32821                     break;
32822             }
32823             
32824         } else {
32825             if(!this.html.length && !this.bgimage.length){
32826                 cls += ' masonry-center-title';
32827             }
32828
32829             if(!this.html.length && this.bgimage.length){
32830                 cls += ' masonry-bottom-title';
32831             }
32832         }
32833         
32834         if(this.cls){
32835             cls += ' ' + this.cls;
32836         }
32837         
32838         var cfg = {
32839             tag: (this.href.length) ? 'a' : 'div',
32840             cls: cls,
32841             cn: [
32842                 {
32843                     tag: 'div',
32844                     cls: 'masonry-brick-mask'
32845                 },
32846                 {
32847                     tag: 'div',
32848                     cls: 'masonry-brick-paragraph',
32849                     cn: []
32850                 }
32851             ]
32852         };
32853         
32854         if(this.href.length){
32855             cfg.href = this.href;
32856         }
32857         
32858         var cn = cfg.cn[1].cn;
32859         
32860         if(this.title.length){
32861             cn.push({
32862                 tag: 'h4',
32863                 cls: 'masonry-brick-title',
32864                 html: this.title
32865             });
32866         }
32867         
32868         if(this.html.length){
32869             cn.push({
32870                 tag: 'p',
32871                 cls: 'masonry-brick-text',
32872                 html: this.html
32873             });
32874         }
32875         
32876         if (!this.title.length && !this.html.length) {
32877             cfg.cn[1].cls += ' hide';
32878         }
32879         
32880         if(this.bgimage.length){
32881             cfg.cn.push({
32882                 tag: 'img',
32883                 cls: 'masonry-brick-image-view',
32884                 src: this.bgimage
32885             });
32886         }
32887         
32888         if(this.videourl.length){
32889             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32890             // youtube support only?
32891             cfg.cn.push({
32892                 tag: 'iframe',
32893                 cls: 'masonry-brick-image-view',
32894                 src: vurl,
32895                 frameborder : 0,
32896                 allowfullscreen : true
32897             });
32898         }
32899         
32900         return cfg;
32901         
32902     },
32903     
32904     getSplitAutoCreate : function()
32905     {
32906         var cls = 'masonry-brick masonry-brick-split';
32907         
32908         if(this.href.length){
32909             cls += ' masonry-brick-link';
32910         }
32911         
32912         if(this.bgimage.length){
32913             cls += ' masonry-brick-image';
32914         }
32915         
32916         if(this.size){
32917             cls += ' masonry-' + this.size + '-brick';
32918         }
32919         
32920         switch (this.placetitle) {
32921             case 'center' :
32922                 cls += ' masonry-center-title';
32923                 break;
32924             case 'bottom' :
32925                 cls += ' masonry-bottom-title';
32926                 break;
32927             default:
32928                 if(!this.bgimage.length){
32929                     cls += ' masonry-center-title';
32930                 }
32931
32932                 if(this.bgimage.length){
32933                     cls += ' masonry-bottom-title';
32934                 }
32935                 break;
32936         }
32937         
32938         if(this.cls){
32939             cls += ' ' + this.cls;
32940         }
32941         
32942         var cfg = {
32943             tag: (this.href.length) ? 'a' : 'div',
32944             cls: cls,
32945             cn: [
32946                 {
32947                     tag: 'div',
32948                     cls: 'masonry-brick-split-head',
32949                     cn: [
32950                         {
32951                             tag: 'div',
32952                             cls: 'masonry-brick-paragraph',
32953                             cn: []
32954                         }
32955                     ]
32956                 },
32957                 {
32958                     tag: 'div',
32959                     cls: 'masonry-brick-split-body',
32960                     cn: []
32961                 }
32962             ]
32963         };
32964         
32965         if(this.href.length){
32966             cfg.href = this.href;
32967         }
32968         
32969         if(this.title.length){
32970             cfg.cn[0].cn[0].cn.push({
32971                 tag: 'h4',
32972                 cls: 'masonry-brick-title',
32973                 html: this.title
32974             });
32975         }
32976         
32977         if(this.html.length){
32978             cfg.cn[1].cn.push({
32979                 tag: 'p',
32980                 cls: 'masonry-brick-text',
32981                 html: this.html
32982             });
32983         }
32984
32985         if(this.bgimage.length){
32986             cfg.cn[0].cn.push({
32987                 tag: 'img',
32988                 cls: 'masonry-brick-image-view',
32989                 src: this.bgimage
32990             });
32991         }
32992         
32993         if(this.videourl.length){
32994             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32995             // youtube support only?
32996             cfg.cn[0].cn.cn.push({
32997                 tag: 'iframe',
32998                 cls: 'masonry-brick-image-view',
32999                 src: vurl,
33000                 frameborder : 0,
33001                 allowfullscreen : true
33002             });
33003         }
33004         
33005         return cfg;
33006     },
33007     
33008     initEvents: function() 
33009     {
33010         switch (this.size) {
33011             case 'xs' :
33012                 this.x = 1;
33013                 this.y = 1;
33014                 break;
33015             case 'sm' :
33016                 this.x = 2;
33017                 this.y = 2;
33018                 break;
33019             case 'md' :
33020             case 'md-left' :
33021             case 'md-right' :
33022                 this.x = 3;
33023                 this.y = 3;
33024                 break;
33025             case 'tall' :
33026                 this.x = 2;
33027                 this.y = 3;
33028                 break;
33029             case 'wide' :
33030                 this.x = 3;
33031                 this.y = 2;
33032                 break;
33033             case 'wide-thin' :
33034                 this.x = 3;
33035                 this.y = 1;
33036                 break;
33037                         
33038             default :
33039                 break;
33040         }
33041         
33042         if(Roo.isTouch){
33043             this.el.on('touchstart', this.onTouchStart, this);
33044             this.el.on('touchmove', this.onTouchMove, this);
33045             this.el.on('touchend', this.onTouchEnd, this);
33046             this.el.on('contextmenu', this.onContextMenu, this);
33047         } else {
33048             this.el.on('mouseenter'  ,this.enter, this);
33049             this.el.on('mouseleave', this.leave, this);
33050             this.el.on('click', this.onClick, this);
33051         }
33052         
33053         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33054             this.parent().bricks.push(this);   
33055         }
33056         
33057     },
33058     
33059     onClick: function(e, el)
33060     {
33061         var time = this.endTimer - this.startTimer;
33062         // Roo.log(e.preventDefault());
33063         if(Roo.isTouch){
33064             if(time > 1000){
33065                 e.preventDefault();
33066                 return;
33067             }
33068         }
33069         
33070         if(!this.preventDefault){
33071             return;
33072         }
33073         
33074         e.preventDefault();
33075         
33076         if (this.activeClass != '') {
33077             this.selectBrick();
33078         }
33079         
33080         this.fireEvent('click', this, e);
33081     },
33082     
33083     enter: function(e, el)
33084     {
33085         e.preventDefault();
33086         
33087         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33088             return;
33089         }
33090         
33091         if(this.bgimage.length && this.html.length){
33092             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33093         }
33094     },
33095     
33096     leave: function(e, el)
33097     {
33098         e.preventDefault();
33099         
33100         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33101             return;
33102         }
33103         
33104         if(this.bgimage.length && this.html.length){
33105             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33106         }
33107     },
33108     
33109     onTouchStart: function(e, el)
33110     {
33111 //        e.preventDefault();
33112         
33113         this.touchmoved = false;
33114         
33115         if(!this.isFitContainer){
33116             return;
33117         }
33118         
33119         if(!this.bgimage.length || !this.html.length){
33120             return;
33121         }
33122         
33123         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33124         
33125         this.timer = new Date().getTime();
33126         
33127     },
33128     
33129     onTouchMove: function(e, el)
33130     {
33131         this.touchmoved = true;
33132     },
33133     
33134     onContextMenu : function(e,el)
33135     {
33136         e.preventDefault();
33137         e.stopPropagation();
33138         return false;
33139     },
33140     
33141     onTouchEnd: function(e, el)
33142     {
33143 //        e.preventDefault();
33144         
33145         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33146         
33147             this.leave(e,el);
33148             
33149             return;
33150         }
33151         
33152         if(!this.bgimage.length || !this.html.length){
33153             
33154             if(this.href.length){
33155                 window.location.href = this.href;
33156             }
33157             
33158             return;
33159         }
33160         
33161         if(!this.isFitContainer){
33162             return;
33163         }
33164         
33165         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33166         
33167         window.location.href = this.href;
33168     },
33169     
33170     //selection on single brick only
33171     selectBrick : function() {
33172         
33173         if (!this.parentId) {
33174             return;
33175         }
33176         
33177         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33178         var index = m.selectedBrick.indexOf(this.id);
33179         
33180         if ( index > -1) {
33181             m.selectedBrick.splice(index,1);
33182             this.el.removeClass(this.activeClass);
33183             return;
33184         }
33185         
33186         for(var i = 0; i < m.selectedBrick.length; i++) {
33187             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33188             b.el.removeClass(b.activeClass);
33189         }
33190         
33191         m.selectedBrick = [];
33192         
33193         m.selectedBrick.push(this.id);
33194         this.el.addClass(this.activeClass);
33195         return;
33196     },
33197     
33198     isSelected : function(){
33199         return this.el.hasClass(this.activeClass);
33200         
33201     }
33202 });
33203
33204 Roo.apply(Roo.bootstrap.MasonryBrick, {
33205     
33206     //groups: {},
33207     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33208      /**
33209     * register a Masonry Brick
33210     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33211     */
33212     
33213     register : function(brick)
33214     {
33215         //this.groups[brick.id] = brick;
33216         this.groups.add(brick.id, brick);
33217     },
33218     /**
33219     * fetch a  masonry brick based on the masonry brick ID
33220     * @param {string} the masonry brick to add
33221     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33222     */
33223     
33224     get: function(brick_id) 
33225     {
33226         // if (typeof(this.groups[brick_id]) == 'undefined') {
33227         //     return false;
33228         // }
33229         // return this.groups[brick_id] ;
33230         
33231         if(this.groups.key(brick_id)) {
33232             return this.groups.key(brick_id);
33233         }
33234         
33235         return false;
33236     }
33237     
33238     
33239     
33240 });
33241
33242  /*
33243  * - LGPL
33244  *
33245  * element
33246  * 
33247  */
33248
33249 /**
33250  * @class Roo.bootstrap.Brick
33251  * @extends Roo.bootstrap.Component
33252  * Bootstrap Brick class
33253  * 
33254  * @constructor
33255  * Create a new Brick
33256  * @param {Object} config The config object
33257  */
33258
33259 Roo.bootstrap.Brick = function(config){
33260     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33261     
33262     this.addEvents({
33263         // raw events
33264         /**
33265          * @event click
33266          * When a Brick is click
33267          * @param {Roo.bootstrap.Brick} this
33268          * @param {Roo.EventObject} e
33269          */
33270         "click" : true
33271     });
33272 };
33273
33274 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33275     
33276     /**
33277      * @cfg {String} title
33278      */   
33279     title : '',
33280     /**
33281      * @cfg {String} html
33282      */   
33283     html : '',
33284     /**
33285      * @cfg {String} bgimage
33286      */   
33287     bgimage : '',
33288     /**
33289      * @cfg {String} cls
33290      */   
33291     cls : '',
33292     /**
33293      * @cfg {String} href
33294      */   
33295     href : '',
33296     /**
33297      * @cfg {String} video
33298      */   
33299     video : '',
33300     /**
33301      * @cfg {Boolean} square
33302      */   
33303     square : true,
33304     
33305     getAutoCreate : function()
33306     {
33307         var cls = 'roo-brick';
33308         
33309         if(this.href.length){
33310             cls += ' roo-brick-link';
33311         }
33312         
33313         if(this.bgimage.length){
33314             cls += ' roo-brick-image';
33315         }
33316         
33317         if(!this.html.length && !this.bgimage.length){
33318             cls += ' roo-brick-center-title';
33319         }
33320         
33321         if(!this.html.length && this.bgimage.length){
33322             cls += ' roo-brick-bottom-title';
33323         }
33324         
33325         if(this.cls){
33326             cls += ' ' + this.cls;
33327         }
33328         
33329         var cfg = {
33330             tag: (this.href.length) ? 'a' : 'div',
33331             cls: cls,
33332             cn: [
33333                 {
33334                     tag: 'div',
33335                     cls: 'roo-brick-paragraph',
33336                     cn: []
33337                 }
33338             ]
33339         };
33340         
33341         if(this.href.length){
33342             cfg.href = this.href;
33343         }
33344         
33345         var cn = cfg.cn[0].cn;
33346         
33347         if(this.title.length){
33348             cn.push({
33349                 tag: 'h4',
33350                 cls: 'roo-brick-title',
33351                 html: this.title
33352             });
33353         }
33354         
33355         if(this.html.length){
33356             cn.push({
33357                 tag: 'p',
33358                 cls: 'roo-brick-text',
33359                 html: this.html
33360             });
33361         } else {
33362             cn.cls += ' hide';
33363         }
33364         
33365         if(this.bgimage.length){
33366             cfg.cn.push({
33367                 tag: 'img',
33368                 cls: 'roo-brick-image-view',
33369                 src: this.bgimage
33370             });
33371         }
33372         
33373         return cfg;
33374     },
33375     
33376     initEvents: function() 
33377     {
33378         if(this.title.length || this.html.length){
33379             this.el.on('mouseenter'  ,this.enter, this);
33380             this.el.on('mouseleave', this.leave, this);
33381         }
33382         
33383         Roo.EventManager.onWindowResize(this.resize, this); 
33384         
33385         if(this.bgimage.length){
33386             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33387             this.imageEl.on('load', this.onImageLoad, this);
33388             return;
33389         }
33390         
33391         this.resize();
33392     },
33393     
33394     onImageLoad : function()
33395     {
33396         this.resize();
33397     },
33398     
33399     resize : function()
33400     {
33401         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33402         
33403         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33404         
33405         if(this.bgimage.length){
33406             var image = this.el.select('.roo-brick-image-view', true).first();
33407             
33408             image.setWidth(paragraph.getWidth());
33409             
33410             if(this.square){
33411                 image.setHeight(paragraph.getWidth());
33412             }
33413             
33414             this.el.setHeight(image.getHeight());
33415             paragraph.setHeight(image.getHeight());
33416             
33417         }
33418         
33419     },
33420     
33421     enter: function(e, el)
33422     {
33423         e.preventDefault();
33424         
33425         if(this.bgimage.length){
33426             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33427             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33428         }
33429     },
33430     
33431     leave: function(e, el)
33432     {
33433         e.preventDefault();
33434         
33435         if(this.bgimage.length){
33436             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33437             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33438         }
33439     }
33440     
33441 });
33442
33443  
33444
33445  /*
33446  * - LGPL
33447  *
33448  * Number field 
33449  */
33450
33451 /**
33452  * @class Roo.bootstrap.NumberField
33453  * @extends Roo.bootstrap.Input
33454  * Bootstrap NumberField class
33455  * 
33456  * 
33457  * 
33458  * 
33459  * @constructor
33460  * Create a new NumberField
33461  * @param {Object} config The config object
33462  */
33463
33464 Roo.bootstrap.NumberField = function(config){
33465     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33466 };
33467
33468 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33469     
33470     /**
33471      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33472      */
33473     allowDecimals : true,
33474     /**
33475      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33476      */
33477     decimalSeparator : ".",
33478     /**
33479      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33480      */
33481     decimalPrecision : 2,
33482     /**
33483      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33484      */
33485     allowNegative : true,
33486     
33487     /**
33488      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33489      */
33490     allowZero: true,
33491     /**
33492      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33493      */
33494     minValue : Number.NEGATIVE_INFINITY,
33495     /**
33496      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33497      */
33498     maxValue : Number.MAX_VALUE,
33499     /**
33500      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33501      */
33502     minText : "The minimum value for this field is {0}",
33503     /**
33504      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33505      */
33506     maxText : "The maximum value for this field is {0}",
33507     /**
33508      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33509      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33510      */
33511     nanText : "{0} is not a valid number",
33512     /**
33513      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33514      */
33515     thousandsDelimiter : false,
33516     /**
33517      * @cfg {String} valueAlign alignment of value
33518      */
33519     valueAlign : "left",
33520
33521     getAutoCreate : function()
33522     {
33523         var hiddenInput = {
33524             tag: 'input',
33525             type: 'hidden',
33526             id: Roo.id(),
33527             cls: 'hidden-number-input'
33528         };
33529         
33530         if (this.name) {
33531             hiddenInput.name = this.name;
33532         }
33533         
33534         this.name = '';
33535         
33536         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33537         
33538         this.name = hiddenInput.name;
33539         
33540         if(cfg.cn.length > 0) {
33541             cfg.cn.push(hiddenInput);
33542         }
33543         
33544         return cfg;
33545     },
33546
33547     // private
33548     initEvents : function()
33549     {   
33550         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33551         
33552         var allowed = "0123456789";
33553         
33554         if(this.allowDecimals){
33555             allowed += this.decimalSeparator;
33556         }
33557         
33558         if(this.allowNegative){
33559             allowed += "-";
33560         }
33561         
33562         if(this.thousandsDelimiter) {
33563             allowed += ",";
33564         }
33565         
33566         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33567         
33568         var keyPress = function(e){
33569             
33570             var k = e.getKey();
33571             
33572             var c = e.getCharCode();
33573             
33574             if(
33575                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33576                     allowed.indexOf(String.fromCharCode(c)) === -1
33577             ){
33578                 e.stopEvent();
33579                 return;
33580             }
33581             
33582             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33583                 return;
33584             }
33585             
33586             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33587                 e.stopEvent();
33588             }
33589         };
33590         
33591         this.el.on("keypress", keyPress, this);
33592     },
33593     
33594     validateValue : function(value)
33595     {
33596         
33597         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33598             return false;
33599         }
33600         
33601         var num = this.parseValue(value);
33602         
33603         if(isNaN(num)){
33604             this.markInvalid(String.format(this.nanText, value));
33605             return false;
33606         }
33607         
33608         if(num < this.minValue){
33609             this.markInvalid(String.format(this.minText, this.minValue));
33610             return false;
33611         }
33612         
33613         if(num > this.maxValue){
33614             this.markInvalid(String.format(this.maxText, this.maxValue));
33615             return false;
33616         }
33617         
33618         return true;
33619     },
33620
33621     getValue : function()
33622     {
33623         var v = this.hiddenEl().getValue();
33624         
33625         return this.fixPrecision(this.parseValue(v));
33626     },
33627
33628     parseValue : function(value)
33629     {
33630         if(this.thousandsDelimiter) {
33631             value += "";
33632             r = new RegExp(",", "g");
33633             value = value.replace(r, "");
33634         }
33635         
33636         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33637         return isNaN(value) ? '' : value;
33638     },
33639
33640     fixPrecision : function(value)
33641     {
33642         if(this.thousandsDelimiter) {
33643             value += "";
33644             r = new RegExp(",", "g");
33645             value = value.replace(r, "");
33646         }
33647         
33648         var nan = isNaN(value);
33649         
33650         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33651             return nan ? '' : value;
33652         }
33653         return parseFloat(value).toFixed(this.decimalPrecision);
33654     },
33655
33656     setValue : function(v)
33657     {
33658         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33659         
33660         this.value = v;
33661         
33662         if(this.rendered){
33663             
33664             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33665             
33666             this.inputEl().dom.value = (v == '') ? '' :
33667                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33668             
33669             if(!this.allowZero && v === '0') {
33670                 this.hiddenEl().dom.value = '';
33671                 this.inputEl().dom.value = '';
33672             }
33673             
33674             this.validate();
33675         }
33676     },
33677
33678     decimalPrecisionFcn : function(v)
33679     {
33680         return Math.floor(v);
33681     },
33682
33683     beforeBlur : function()
33684     {
33685         var v = this.parseValue(this.getRawValue());
33686         
33687         if(v || v === 0 || v === ''){
33688             this.setValue(v);
33689         }
33690     },
33691     
33692     hiddenEl : function()
33693     {
33694         return this.el.select('input.hidden-number-input',true).first();
33695     }
33696     
33697 });
33698
33699  
33700
33701 /*
33702 * Licence: LGPL
33703 */
33704
33705 /**
33706  * @class Roo.bootstrap.DocumentSlider
33707  * @extends Roo.bootstrap.Component
33708  * Bootstrap DocumentSlider class
33709  * 
33710  * @constructor
33711  * Create a new DocumentViewer
33712  * @param {Object} config The config object
33713  */
33714
33715 Roo.bootstrap.DocumentSlider = function(config){
33716     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33717     
33718     this.files = [];
33719     
33720     this.addEvents({
33721         /**
33722          * @event initial
33723          * Fire after initEvent
33724          * @param {Roo.bootstrap.DocumentSlider} this
33725          */
33726         "initial" : true,
33727         /**
33728          * @event update
33729          * Fire after update
33730          * @param {Roo.bootstrap.DocumentSlider} this
33731          */
33732         "update" : true,
33733         /**
33734          * @event click
33735          * Fire after click
33736          * @param {Roo.bootstrap.DocumentSlider} this
33737          */
33738         "click" : true
33739     });
33740 };
33741
33742 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33743     
33744     files : false,
33745     
33746     indicator : 0,
33747     
33748     getAutoCreate : function()
33749     {
33750         var cfg = {
33751             tag : 'div',
33752             cls : 'roo-document-slider',
33753             cn : [
33754                 {
33755                     tag : 'div',
33756                     cls : 'roo-document-slider-header',
33757                     cn : [
33758                         {
33759                             tag : 'div',
33760                             cls : 'roo-document-slider-header-title'
33761                         }
33762                     ]
33763                 },
33764                 {
33765                     tag : 'div',
33766                     cls : 'roo-document-slider-body',
33767                     cn : [
33768                         {
33769                             tag : 'div',
33770                             cls : 'roo-document-slider-prev',
33771                             cn : [
33772                                 {
33773                                     tag : 'i',
33774                                     cls : 'fa fa-chevron-left'
33775                                 }
33776                             ]
33777                         },
33778                         {
33779                             tag : 'div',
33780                             cls : 'roo-document-slider-thumb',
33781                             cn : [
33782                                 {
33783                                     tag : 'img',
33784                                     cls : 'roo-document-slider-image'
33785                                 }
33786                             ]
33787                         },
33788                         {
33789                             tag : 'div',
33790                             cls : 'roo-document-slider-next',
33791                             cn : [
33792                                 {
33793                                     tag : 'i',
33794                                     cls : 'fa fa-chevron-right'
33795                                 }
33796                             ]
33797                         }
33798                     ]
33799                 }
33800             ]
33801         };
33802         
33803         return cfg;
33804     },
33805     
33806     initEvents : function()
33807     {
33808         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33809         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33810         
33811         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33812         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33813         
33814         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33815         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33816         
33817         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33818         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33819         
33820         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33821         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33822         
33823         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33824         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33825         
33826         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33827         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33828         
33829         this.thumbEl.on('click', this.onClick, this);
33830         
33831         this.prevIndicator.on('click', this.prev, this);
33832         
33833         this.nextIndicator.on('click', this.next, this);
33834         
33835     },
33836     
33837     initial : function()
33838     {
33839         if(this.files.length){
33840             this.indicator = 1;
33841             this.update()
33842         }
33843         
33844         this.fireEvent('initial', this);
33845     },
33846     
33847     update : function()
33848     {
33849         this.imageEl.attr('src', this.files[this.indicator - 1]);
33850         
33851         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33852         
33853         this.prevIndicator.show();
33854         
33855         if(this.indicator == 1){
33856             this.prevIndicator.hide();
33857         }
33858         
33859         this.nextIndicator.show();
33860         
33861         if(this.indicator == this.files.length){
33862             this.nextIndicator.hide();
33863         }
33864         
33865         this.thumbEl.scrollTo('top');
33866         
33867         this.fireEvent('update', this);
33868     },
33869     
33870     onClick : function(e)
33871     {
33872         e.preventDefault();
33873         
33874         this.fireEvent('click', this);
33875     },
33876     
33877     prev : function(e)
33878     {
33879         e.preventDefault();
33880         
33881         this.indicator = Math.max(1, this.indicator - 1);
33882         
33883         this.update();
33884     },
33885     
33886     next : function(e)
33887     {
33888         e.preventDefault();
33889         
33890         this.indicator = Math.min(this.files.length, this.indicator + 1);
33891         
33892         this.update();
33893     }
33894 });
33895 /*
33896  * - LGPL
33897  *
33898  * RadioSet
33899  *
33900  *
33901  */
33902
33903 /**
33904  * @class Roo.bootstrap.RadioSet
33905  * @extends Roo.bootstrap.Input
33906  * Bootstrap RadioSet class
33907  * @cfg {String} indicatorpos (left|right) default left
33908  * @cfg {Boolean} inline (true|false) inline the element (default true)
33909  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33910  * @constructor
33911  * Create a new RadioSet
33912  * @param {Object} config The config object
33913  */
33914
33915 Roo.bootstrap.RadioSet = function(config){
33916     
33917     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33918     
33919     this.radioes = [];
33920     
33921     Roo.bootstrap.RadioSet.register(this);
33922     
33923     this.addEvents({
33924         /**
33925         * @event check
33926         * Fires when the element is checked or unchecked.
33927         * @param {Roo.bootstrap.RadioSet} this This radio
33928         * @param {Roo.bootstrap.Radio} item The checked item
33929         */
33930        check : true,
33931        /**
33932         * @event click
33933         * Fires when the element is click.
33934         * @param {Roo.bootstrap.RadioSet} this This radio set
33935         * @param {Roo.bootstrap.Radio} item The checked item
33936         * @param {Roo.EventObject} e The event object
33937         */
33938        click : true
33939     });
33940     
33941 };
33942
33943 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33944
33945     radioes : false,
33946     
33947     inline : true,
33948     
33949     weight : '',
33950     
33951     indicatorpos : 'left',
33952     
33953     getAutoCreate : function()
33954     {
33955         var label = {
33956             tag : 'label',
33957             cls : 'roo-radio-set-label',
33958             cn : [
33959                 {
33960                     tag : 'span',
33961                     html : this.fieldLabel
33962                 }
33963             ]
33964         };
33965         
33966         if(this.indicatorpos == 'left'){
33967             label.cn.unshift({
33968                 tag : 'i',
33969                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33970                 tooltip : 'This field is required'
33971             });
33972         } else {
33973             label.cn.push({
33974                 tag : 'i',
33975                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33976                 tooltip : 'This field is required'
33977             });
33978         }
33979         
33980         var items = {
33981             tag : 'div',
33982             cls : 'roo-radio-set-items'
33983         };
33984         
33985         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33986         
33987         if (align === 'left' && this.fieldLabel.length) {
33988             
33989             items = {
33990                 cls : "roo-radio-set-right", 
33991                 cn: [
33992                     items
33993                 ]
33994             };
33995             
33996             if(this.labelWidth > 12){
33997                 label.style = "width: " + this.labelWidth + 'px';
33998             }
33999             
34000             if(this.labelWidth < 13 && this.labelmd == 0){
34001                 this.labelmd = this.labelWidth;
34002             }
34003             
34004             if(this.labellg > 0){
34005                 label.cls += ' col-lg-' + this.labellg;
34006                 items.cls += ' col-lg-' + (12 - this.labellg);
34007             }
34008             
34009             if(this.labelmd > 0){
34010                 label.cls += ' col-md-' + this.labelmd;
34011                 items.cls += ' col-md-' + (12 - this.labelmd);
34012             }
34013             
34014             if(this.labelsm > 0){
34015                 label.cls += ' col-sm-' + this.labelsm;
34016                 items.cls += ' col-sm-' + (12 - this.labelsm);
34017             }
34018             
34019             if(this.labelxs > 0){
34020                 label.cls += ' col-xs-' + this.labelxs;
34021                 items.cls += ' col-xs-' + (12 - this.labelxs);
34022             }
34023         }
34024         
34025         var cfg = {
34026             tag : 'div',
34027             cls : 'roo-radio-set',
34028             cn : [
34029                 {
34030                     tag : 'input',
34031                     cls : 'roo-radio-set-input',
34032                     type : 'hidden',
34033                     name : this.name,
34034                     value : this.value ? this.value :  ''
34035                 },
34036                 label,
34037                 items
34038             ]
34039         };
34040         
34041         if(this.weight.length){
34042             cfg.cls += ' roo-radio-' + this.weight;
34043         }
34044         
34045         if(this.inline) {
34046             cfg.cls += ' roo-radio-set-inline';
34047         }
34048         
34049         var settings=this;
34050         ['xs','sm','md','lg'].map(function(size){
34051             if (settings[size]) {
34052                 cfg.cls += ' col-' + size + '-' + settings[size];
34053             }
34054         });
34055         
34056         return cfg;
34057         
34058     },
34059
34060     initEvents : function()
34061     {
34062         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34063         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34064         
34065         if(!this.fieldLabel.length){
34066             this.labelEl.hide();
34067         }
34068         
34069         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34070         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34071         
34072         this.indicator = this.indicatorEl();
34073         
34074         if(this.indicator){
34075             this.indicator.addClass('invisible');
34076         }
34077         
34078         this.originalValue = this.getValue();
34079         
34080     },
34081     
34082     inputEl: function ()
34083     {
34084         return this.el.select('.roo-radio-set-input', true).first();
34085     },
34086     
34087     getChildContainer : function()
34088     {
34089         return this.itemsEl;
34090     },
34091     
34092     register : function(item)
34093     {
34094         this.radioes.push(item);
34095         
34096     },
34097     
34098     validate : function()
34099     {   
34100         if(this.getVisibilityEl().hasClass('hidden')){
34101             return true;
34102         }
34103         
34104         var valid = false;
34105         
34106         Roo.each(this.radioes, function(i){
34107             if(!i.checked){
34108                 return;
34109             }
34110             
34111             valid = true;
34112             return false;
34113         });
34114         
34115         if(this.allowBlank) {
34116             return true;
34117         }
34118         
34119         if(this.disabled || valid){
34120             this.markValid();
34121             return true;
34122         }
34123         
34124         this.markInvalid();
34125         return false;
34126         
34127     },
34128     
34129     markValid : function()
34130     {
34131         if(this.labelEl.isVisible(true)){
34132             this.indicatorEl().removeClass('visible');
34133             this.indicatorEl().addClass('invisible');
34134         }
34135         
34136         this.el.removeClass([this.invalidClass, this.validClass]);
34137         this.el.addClass(this.validClass);
34138         
34139         this.fireEvent('valid', this);
34140     },
34141     
34142     markInvalid : function(msg)
34143     {
34144         if(this.allowBlank || this.disabled){
34145             return;
34146         }
34147         
34148         if(this.labelEl.isVisible(true)){
34149             this.indicatorEl().removeClass('invisible');
34150             this.indicatorEl().addClass('visible');
34151         }
34152         
34153         this.el.removeClass([this.invalidClass, this.validClass]);
34154         this.el.addClass(this.invalidClass);
34155         
34156         this.fireEvent('invalid', this, msg);
34157         
34158     },
34159     
34160     setValue : function(v, suppressEvent)
34161     {   
34162         if(this.value === v){
34163             return;
34164         }
34165         
34166         this.value = v;
34167         
34168         if(this.rendered){
34169             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34170         }
34171         
34172         Roo.each(this.radioes, function(i){
34173             i.checked = false;
34174             i.el.removeClass('checked');
34175         });
34176         
34177         Roo.each(this.radioes, function(i){
34178             
34179             if(i.value === v || i.value.toString() === v.toString()){
34180                 i.checked = true;
34181                 i.el.addClass('checked');
34182                 
34183                 if(suppressEvent !== true){
34184                     this.fireEvent('check', this, i);
34185                 }
34186                 
34187                 return false;
34188             }
34189             
34190         }, this);
34191         
34192         this.validate();
34193     },
34194     
34195     clearInvalid : function(){
34196         
34197         if(!this.el || this.preventMark){
34198             return;
34199         }
34200         
34201         this.el.removeClass([this.invalidClass]);
34202         
34203         this.fireEvent('valid', this);
34204     }
34205     
34206 });
34207
34208 Roo.apply(Roo.bootstrap.RadioSet, {
34209     
34210     groups: {},
34211     
34212     register : function(set)
34213     {
34214         this.groups[set.name] = set;
34215     },
34216     
34217     get: function(name) 
34218     {
34219         if (typeof(this.groups[name]) == 'undefined') {
34220             return false;
34221         }
34222         
34223         return this.groups[name] ;
34224     }
34225     
34226 });
34227 /*
34228  * Based on:
34229  * Ext JS Library 1.1.1
34230  * Copyright(c) 2006-2007, Ext JS, LLC.
34231  *
34232  * Originally Released Under LGPL - original licence link has changed is not relivant.
34233  *
34234  * Fork - LGPL
34235  * <script type="text/javascript">
34236  */
34237
34238
34239 /**
34240  * @class Roo.bootstrap.SplitBar
34241  * @extends Roo.util.Observable
34242  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34243  * <br><br>
34244  * Usage:
34245  * <pre><code>
34246 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34247                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34248 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34249 split.minSize = 100;
34250 split.maxSize = 600;
34251 split.animate = true;
34252 split.on('moved', splitterMoved);
34253 </code></pre>
34254  * @constructor
34255  * Create a new SplitBar
34256  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34257  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34258  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34259  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34260                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34261                         position of the SplitBar).
34262  */
34263 Roo.bootstrap.SplitBar = function(cfg){
34264     
34265     /** @private */
34266     
34267     //{
34268     //  dragElement : elm
34269     //  resizingElement: el,
34270         // optional..
34271     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34272     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34273         // existingProxy ???
34274     //}
34275     
34276     this.el = Roo.get(cfg.dragElement, true);
34277     this.el.dom.unselectable = "on";
34278     /** @private */
34279     this.resizingEl = Roo.get(cfg.resizingElement, true);
34280
34281     /**
34282      * @private
34283      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34284      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34285      * @type Number
34286      */
34287     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34288     
34289     /**
34290      * The minimum size of the resizing element. (Defaults to 0)
34291      * @type Number
34292      */
34293     this.minSize = 0;
34294     
34295     /**
34296      * The maximum size of the resizing element. (Defaults to 2000)
34297      * @type Number
34298      */
34299     this.maxSize = 2000;
34300     
34301     /**
34302      * Whether to animate the transition to the new size
34303      * @type Boolean
34304      */
34305     this.animate = false;
34306     
34307     /**
34308      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34309      * @type Boolean
34310      */
34311     this.useShim = false;
34312     
34313     /** @private */
34314     this.shim = null;
34315     
34316     if(!cfg.existingProxy){
34317         /** @private */
34318         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34319     }else{
34320         this.proxy = Roo.get(cfg.existingProxy).dom;
34321     }
34322     /** @private */
34323     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34324     
34325     /** @private */
34326     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34327     
34328     /** @private */
34329     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34330     
34331     /** @private */
34332     this.dragSpecs = {};
34333     
34334     /**
34335      * @private The adapter to use to positon and resize elements
34336      */
34337     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34338     this.adapter.init(this);
34339     
34340     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34341         /** @private */
34342         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34343         this.el.addClass("roo-splitbar-h");
34344     }else{
34345         /** @private */
34346         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34347         this.el.addClass("roo-splitbar-v");
34348     }
34349     
34350     this.addEvents({
34351         /**
34352          * @event resize
34353          * Fires when the splitter is moved (alias for {@link #event-moved})
34354          * @param {Roo.bootstrap.SplitBar} this
34355          * @param {Number} newSize the new width or height
34356          */
34357         "resize" : true,
34358         /**
34359          * @event moved
34360          * Fires when the splitter is moved
34361          * @param {Roo.bootstrap.SplitBar} this
34362          * @param {Number} newSize the new width or height
34363          */
34364         "moved" : true,
34365         /**
34366          * @event beforeresize
34367          * Fires before the splitter is dragged
34368          * @param {Roo.bootstrap.SplitBar} this
34369          */
34370         "beforeresize" : true,
34371
34372         "beforeapply" : true
34373     });
34374
34375     Roo.util.Observable.call(this);
34376 };
34377
34378 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34379     onStartProxyDrag : function(x, y){
34380         this.fireEvent("beforeresize", this);
34381         if(!this.overlay){
34382             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34383             o.unselectable();
34384             o.enableDisplayMode("block");
34385             // all splitbars share the same overlay
34386             Roo.bootstrap.SplitBar.prototype.overlay = o;
34387         }
34388         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34389         this.overlay.show();
34390         Roo.get(this.proxy).setDisplayed("block");
34391         var size = this.adapter.getElementSize(this);
34392         this.activeMinSize = this.getMinimumSize();;
34393         this.activeMaxSize = this.getMaximumSize();;
34394         var c1 = size - this.activeMinSize;
34395         var c2 = Math.max(this.activeMaxSize - size, 0);
34396         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34397             this.dd.resetConstraints();
34398             this.dd.setXConstraint(
34399                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34400                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34401             );
34402             this.dd.setYConstraint(0, 0);
34403         }else{
34404             this.dd.resetConstraints();
34405             this.dd.setXConstraint(0, 0);
34406             this.dd.setYConstraint(
34407                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34408                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34409             );
34410          }
34411         this.dragSpecs.startSize = size;
34412         this.dragSpecs.startPoint = [x, y];
34413         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34414     },
34415     
34416     /** 
34417      * @private Called after the drag operation by the DDProxy
34418      */
34419     onEndProxyDrag : function(e){
34420         Roo.get(this.proxy).setDisplayed(false);
34421         var endPoint = Roo.lib.Event.getXY(e);
34422         if(this.overlay){
34423             this.overlay.hide();
34424         }
34425         var newSize;
34426         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34427             newSize = this.dragSpecs.startSize + 
34428                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34429                     endPoint[0] - this.dragSpecs.startPoint[0] :
34430                     this.dragSpecs.startPoint[0] - endPoint[0]
34431                 );
34432         }else{
34433             newSize = this.dragSpecs.startSize + 
34434                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34435                     endPoint[1] - this.dragSpecs.startPoint[1] :
34436                     this.dragSpecs.startPoint[1] - endPoint[1]
34437                 );
34438         }
34439         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34440         if(newSize != this.dragSpecs.startSize){
34441             if(this.fireEvent('beforeapply', this, newSize) !== false){
34442                 this.adapter.setElementSize(this, newSize);
34443                 this.fireEvent("moved", this, newSize);
34444                 this.fireEvent("resize", this, newSize);
34445             }
34446         }
34447     },
34448     
34449     /**
34450      * Get the adapter this SplitBar uses
34451      * @return The adapter object
34452      */
34453     getAdapter : function(){
34454         return this.adapter;
34455     },
34456     
34457     /**
34458      * Set the adapter this SplitBar uses
34459      * @param {Object} adapter A SplitBar adapter object
34460      */
34461     setAdapter : function(adapter){
34462         this.adapter = adapter;
34463         this.adapter.init(this);
34464     },
34465     
34466     /**
34467      * Gets the minimum size for the resizing element
34468      * @return {Number} The minimum size
34469      */
34470     getMinimumSize : function(){
34471         return this.minSize;
34472     },
34473     
34474     /**
34475      * Sets the minimum size for the resizing element
34476      * @param {Number} minSize The minimum size
34477      */
34478     setMinimumSize : function(minSize){
34479         this.minSize = minSize;
34480     },
34481     
34482     /**
34483      * Gets the maximum size for the resizing element
34484      * @return {Number} The maximum size
34485      */
34486     getMaximumSize : function(){
34487         return this.maxSize;
34488     },
34489     
34490     /**
34491      * Sets the maximum size for the resizing element
34492      * @param {Number} maxSize The maximum size
34493      */
34494     setMaximumSize : function(maxSize){
34495         this.maxSize = maxSize;
34496     },
34497     
34498     /**
34499      * Sets the initialize size for the resizing element
34500      * @param {Number} size The initial size
34501      */
34502     setCurrentSize : function(size){
34503         var oldAnimate = this.animate;
34504         this.animate = false;
34505         this.adapter.setElementSize(this, size);
34506         this.animate = oldAnimate;
34507     },
34508     
34509     /**
34510      * Destroy this splitbar. 
34511      * @param {Boolean} removeEl True to remove the element
34512      */
34513     destroy : function(removeEl){
34514         if(this.shim){
34515             this.shim.remove();
34516         }
34517         this.dd.unreg();
34518         this.proxy.parentNode.removeChild(this.proxy);
34519         if(removeEl){
34520             this.el.remove();
34521         }
34522     }
34523 });
34524
34525 /**
34526  * @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.
34527  */
34528 Roo.bootstrap.SplitBar.createProxy = function(dir){
34529     var proxy = new Roo.Element(document.createElement("div"));
34530     proxy.unselectable();
34531     var cls = 'roo-splitbar-proxy';
34532     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34533     document.body.appendChild(proxy.dom);
34534     return proxy.dom;
34535 };
34536
34537 /** 
34538  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34539  * Default Adapter. It assumes the splitter and resizing element are not positioned
34540  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34541  */
34542 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34543 };
34544
34545 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34546     // do nothing for now
34547     init : function(s){
34548     
34549     },
34550     /**
34551      * Called before drag operations to get the current size of the resizing element. 
34552      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34553      */
34554      getElementSize : function(s){
34555         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34556             return s.resizingEl.getWidth();
34557         }else{
34558             return s.resizingEl.getHeight();
34559         }
34560     },
34561     
34562     /**
34563      * Called after drag operations to set the size of the resizing element.
34564      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34565      * @param {Number} newSize The new size to set
34566      * @param {Function} onComplete A function to be invoked when resizing is complete
34567      */
34568     setElementSize : function(s, newSize, onComplete){
34569         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34570             if(!s.animate){
34571                 s.resizingEl.setWidth(newSize);
34572                 if(onComplete){
34573                     onComplete(s, newSize);
34574                 }
34575             }else{
34576                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34577             }
34578         }else{
34579             
34580             if(!s.animate){
34581                 s.resizingEl.setHeight(newSize);
34582                 if(onComplete){
34583                     onComplete(s, newSize);
34584                 }
34585             }else{
34586                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34587             }
34588         }
34589     }
34590 };
34591
34592 /** 
34593  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34594  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34595  * Adapter that  moves the splitter element to align with the resized sizing element. 
34596  * Used with an absolute positioned SplitBar.
34597  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34598  * document.body, make sure you assign an id to the body element.
34599  */
34600 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34601     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34602     this.container = Roo.get(container);
34603 };
34604
34605 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34606     init : function(s){
34607         this.basic.init(s);
34608     },
34609     
34610     getElementSize : function(s){
34611         return this.basic.getElementSize(s);
34612     },
34613     
34614     setElementSize : function(s, newSize, onComplete){
34615         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34616     },
34617     
34618     moveSplitter : function(s){
34619         var yes = Roo.bootstrap.SplitBar;
34620         switch(s.placement){
34621             case yes.LEFT:
34622                 s.el.setX(s.resizingEl.getRight());
34623                 break;
34624             case yes.RIGHT:
34625                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34626                 break;
34627             case yes.TOP:
34628                 s.el.setY(s.resizingEl.getBottom());
34629                 break;
34630             case yes.BOTTOM:
34631                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34632                 break;
34633         }
34634     }
34635 };
34636
34637 /**
34638  * Orientation constant - Create a vertical SplitBar
34639  * @static
34640  * @type Number
34641  */
34642 Roo.bootstrap.SplitBar.VERTICAL = 1;
34643
34644 /**
34645  * Orientation constant - Create a horizontal SplitBar
34646  * @static
34647  * @type Number
34648  */
34649 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34650
34651 /**
34652  * Placement constant - The resizing element is to the left of the splitter element
34653  * @static
34654  * @type Number
34655  */
34656 Roo.bootstrap.SplitBar.LEFT = 1;
34657
34658 /**
34659  * Placement constant - The resizing element is to the right of the splitter element
34660  * @static
34661  * @type Number
34662  */
34663 Roo.bootstrap.SplitBar.RIGHT = 2;
34664
34665 /**
34666  * Placement constant - The resizing element is positioned above the splitter element
34667  * @static
34668  * @type Number
34669  */
34670 Roo.bootstrap.SplitBar.TOP = 3;
34671
34672 /**
34673  * Placement constant - The resizing element is positioned under splitter element
34674  * @static
34675  * @type Number
34676  */
34677 Roo.bootstrap.SplitBar.BOTTOM = 4;
34678 Roo.namespace("Roo.bootstrap.layout");/*
34679  * Based on:
34680  * Ext JS Library 1.1.1
34681  * Copyright(c) 2006-2007, Ext JS, LLC.
34682  *
34683  * Originally Released Under LGPL - original licence link has changed is not relivant.
34684  *
34685  * Fork - LGPL
34686  * <script type="text/javascript">
34687  */
34688
34689 /**
34690  * @class Roo.bootstrap.layout.Manager
34691  * @extends Roo.bootstrap.Component
34692  * Base class for layout managers.
34693  */
34694 Roo.bootstrap.layout.Manager = function(config)
34695 {
34696     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34697
34698
34699
34700
34701
34702     /** false to disable window resize monitoring @type Boolean */
34703     this.monitorWindowResize = true;
34704     this.regions = {};
34705     this.addEvents({
34706         /**
34707          * @event layout
34708          * Fires when a layout is performed.
34709          * @param {Roo.LayoutManager} this
34710          */
34711         "layout" : true,
34712         /**
34713          * @event regionresized
34714          * Fires when the user resizes a region.
34715          * @param {Roo.LayoutRegion} region The resized region
34716          * @param {Number} newSize The new size (width for east/west, height for north/south)
34717          */
34718         "regionresized" : true,
34719         /**
34720          * @event regioncollapsed
34721          * Fires when a region is collapsed.
34722          * @param {Roo.LayoutRegion} region The collapsed region
34723          */
34724         "regioncollapsed" : true,
34725         /**
34726          * @event regionexpanded
34727          * Fires when a region is expanded.
34728          * @param {Roo.LayoutRegion} region The expanded region
34729          */
34730         "regionexpanded" : true
34731     });
34732     this.updating = false;
34733
34734     if (config.el) {
34735         this.el = Roo.get(config.el);
34736         this.initEvents();
34737     }
34738
34739 };
34740
34741 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34742
34743
34744     regions : null,
34745
34746     monitorWindowResize : true,
34747
34748
34749     updating : false,
34750
34751
34752     onRender : function(ct, position)
34753     {
34754         if(!this.el){
34755             this.el = Roo.get(ct);
34756             this.initEvents();
34757         }
34758         //this.fireEvent('render',this);
34759     },
34760
34761
34762     initEvents: function()
34763     {
34764
34765
34766         // ie scrollbar fix
34767         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34768             document.body.scroll = "no";
34769         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34770             this.el.position('relative');
34771         }
34772         this.id = this.el.id;
34773         this.el.addClass("roo-layout-container");
34774         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34775         if(this.el.dom != document.body ) {
34776             this.el.on('resize', this.layout,this);
34777             this.el.on('show', this.layout,this);
34778         }
34779
34780     },
34781
34782     /**
34783      * Returns true if this layout is currently being updated
34784      * @return {Boolean}
34785      */
34786     isUpdating : function(){
34787         return this.updating;
34788     },
34789
34790     /**
34791      * Suspend the LayoutManager from doing auto-layouts while
34792      * making multiple add or remove calls
34793      */
34794     beginUpdate : function(){
34795         this.updating = true;
34796     },
34797
34798     /**
34799      * Restore auto-layouts and optionally disable the manager from performing a layout
34800      * @param {Boolean} noLayout true to disable a layout update
34801      */
34802     endUpdate : function(noLayout){
34803         this.updating = false;
34804         if(!noLayout){
34805             this.layout();
34806         }
34807     },
34808
34809     layout: function(){
34810         // abstract...
34811     },
34812
34813     onRegionResized : function(region, newSize){
34814         this.fireEvent("regionresized", region, newSize);
34815         this.layout();
34816     },
34817
34818     onRegionCollapsed : function(region){
34819         this.fireEvent("regioncollapsed", region);
34820     },
34821
34822     onRegionExpanded : function(region){
34823         this.fireEvent("regionexpanded", region);
34824     },
34825
34826     /**
34827      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34828      * performs box-model adjustments.
34829      * @return {Object} The size as an object {width: (the width), height: (the height)}
34830      */
34831     getViewSize : function()
34832     {
34833         var size;
34834         if(this.el.dom != document.body){
34835             size = this.el.getSize();
34836         }else{
34837             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34838         }
34839         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34840         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34841         return size;
34842     },
34843
34844     /**
34845      * Returns the Element this layout is bound to.
34846      * @return {Roo.Element}
34847      */
34848     getEl : function(){
34849         return this.el;
34850     },
34851
34852     /**
34853      * Returns the specified region.
34854      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34855      * @return {Roo.LayoutRegion}
34856      */
34857     getRegion : function(target){
34858         return this.regions[target.toLowerCase()];
34859     },
34860
34861     onWindowResize : function(){
34862         if(this.monitorWindowResize){
34863             this.layout();
34864         }
34865     }
34866 });
34867 /*
34868  * Based on:
34869  * Ext JS Library 1.1.1
34870  * Copyright(c) 2006-2007, Ext JS, LLC.
34871  *
34872  * Originally Released Under LGPL - original licence link has changed is not relivant.
34873  *
34874  * Fork - LGPL
34875  * <script type="text/javascript">
34876  */
34877 /**
34878  * @class Roo.bootstrap.layout.Border
34879  * @extends Roo.bootstrap.layout.Manager
34880  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34881  * please see: examples/bootstrap/nested.html<br><br>
34882  
34883 <b>The container the layout is rendered into can be either the body element or any other element.
34884 If it is not the body element, the container needs to either be an absolute positioned element,
34885 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34886 the container size if it is not the body element.</b>
34887
34888 * @constructor
34889 * Create a new Border
34890 * @param {Object} config Configuration options
34891  */
34892 Roo.bootstrap.layout.Border = function(config){
34893     config = config || {};
34894     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34895     
34896     
34897     
34898     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34899         if(config[region]){
34900             config[region].region = region;
34901             this.addRegion(config[region]);
34902         }
34903     },this);
34904     
34905 };
34906
34907 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34908
34909 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34910     /**
34911      * Creates and adds a new region if it doesn't already exist.
34912      * @param {String} target The target region key (north, south, east, west or center).
34913      * @param {Object} config The regions config object
34914      * @return {BorderLayoutRegion} The new region
34915      */
34916     addRegion : function(config)
34917     {
34918         if(!this.regions[config.region]){
34919             var r = this.factory(config);
34920             this.bindRegion(r);
34921         }
34922         return this.regions[config.region];
34923     },
34924
34925     // private (kinda)
34926     bindRegion : function(r){
34927         this.regions[r.config.region] = r;
34928         
34929         r.on("visibilitychange",    this.layout, this);
34930         r.on("paneladded",          this.layout, this);
34931         r.on("panelremoved",        this.layout, this);
34932         r.on("invalidated",         this.layout, this);
34933         r.on("resized",             this.onRegionResized, this);
34934         r.on("collapsed",           this.onRegionCollapsed, this);
34935         r.on("expanded",            this.onRegionExpanded, this);
34936     },
34937
34938     /**
34939      * Performs a layout update.
34940      */
34941     layout : function()
34942     {
34943         if(this.updating) {
34944             return;
34945         }
34946         
34947         // render all the rebions if they have not been done alreayd?
34948         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34949             if(this.regions[region] && !this.regions[region].bodyEl){
34950                 this.regions[region].onRender(this.el)
34951             }
34952         },this);
34953         
34954         var size = this.getViewSize();
34955         var w = size.width;
34956         var h = size.height;
34957         var centerW = w;
34958         var centerH = h;
34959         var centerY = 0;
34960         var centerX = 0;
34961         //var x = 0, y = 0;
34962
34963         var rs = this.regions;
34964         var north = rs["north"];
34965         var south = rs["south"]; 
34966         var west = rs["west"];
34967         var east = rs["east"];
34968         var center = rs["center"];
34969         //if(this.hideOnLayout){ // not supported anymore
34970             //c.el.setStyle("display", "none");
34971         //}
34972         if(north && north.isVisible()){
34973             var b = north.getBox();
34974             var m = north.getMargins();
34975             b.width = w - (m.left+m.right);
34976             b.x = m.left;
34977             b.y = m.top;
34978             centerY = b.height + b.y + m.bottom;
34979             centerH -= centerY;
34980             north.updateBox(this.safeBox(b));
34981         }
34982         if(south && south.isVisible()){
34983             var b = south.getBox();
34984             var m = south.getMargins();
34985             b.width = w - (m.left+m.right);
34986             b.x = m.left;
34987             var totalHeight = (b.height + m.top + m.bottom);
34988             b.y = h - totalHeight + m.top;
34989             centerH -= totalHeight;
34990             south.updateBox(this.safeBox(b));
34991         }
34992         if(west && west.isVisible()){
34993             var b = west.getBox();
34994             var m = west.getMargins();
34995             b.height = centerH - (m.top+m.bottom);
34996             b.x = m.left;
34997             b.y = centerY + m.top;
34998             var totalWidth = (b.width + m.left + m.right);
34999             centerX += totalWidth;
35000             centerW -= totalWidth;
35001             west.updateBox(this.safeBox(b));
35002         }
35003         if(east && east.isVisible()){
35004             var b = east.getBox();
35005             var m = east.getMargins();
35006             b.height = centerH - (m.top+m.bottom);
35007             var totalWidth = (b.width + m.left + m.right);
35008             b.x = w - totalWidth + m.left;
35009             b.y = centerY + m.top;
35010             centerW -= totalWidth;
35011             east.updateBox(this.safeBox(b));
35012         }
35013         if(center){
35014             var m = center.getMargins();
35015             var centerBox = {
35016                 x: centerX + m.left,
35017                 y: centerY + m.top,
35018                 width: centerW - (m.left+m.right),
35019                 height: centerH - (m.top+m.bottom)
35020             };
35021             //if(this.hideOnLayout){
35022                 //center.el.setStyle("display", "block");
35023             //}
35024             center.updateBox(this.safeBox(centerBox));
35025         }
35026         this.el.repaint();
35027         this.fireEvent("layout", this);
35028     },
35029
35030     // private
35031     safeBox : function(box){
35032         box.width = Math.max(0, box.width);
35033         box.height = Math.max(0, box.height);
35034         return box;
35035     },
35036
35037     /**
35038      * Adds a ContentPanel (or subclass) to this layout.
35039      * @param {String} target The target region key (north, south, east, west or center).
35040      * @param {Roo.ContentPanel} panel The panel to add
35041      * @return {Roo.ContentPanel} The added panel
35042      */
35043     add : function(target, panel){
35044          
35045         target = target.toLowerCase();
35046         return this.regions[target].add(panel);
35047     },
35048
35049     /**
35050      * Remove a ContentPanel (or subclass) to this layout.
35051      * @param {String} target The target region key (north, south, east, west or center).
35052      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35053      * @return {Roo.ContentPanel} The removed panel
35054      */
35055     remove : function(target, panel){
35056         target = target.toLowerCase();
35057         return this.regions[target].remove(panel);
35058     },
35059
35060     /**
35061      * Searches all regions for a panel with the specified id
35062      * @param {String} panelId
35063      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35064      */
35065     findPanel : function(panelId){
35066         var rs = this.regions;
35067         for(var target in rs){
35068             if(typeof rs[target] != "function"){
35069                 var p = rs[target].getPanel(panelId);
35070                 if(p){
35071                     return p;
35072                 }
35073             }
35074         }
35075         return null;
35076     },
35077
35078     /**
35079      * Searches all regions for a panel with the specified id and activates (shows) it.
35080      * @param {String/ContentPanel} panelId The panels id or the panel itself
35081      * @return {Roo.ContentPanel} The shown panel or null
35082      */
35083     showPanel : function(panelId) {
35084       var rs = this.regions;
35085       for(var target in rs){
35086          var r = rs[target];
35087          if(typeof r != "function"){
35088             if(r.hasPanel(panelId)){
35089                return r.showPanel(panelId);
35090             }
35091          }
35092       }
35093       return null;
35094    },
35095
35096    /**
35097      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35098      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35099      */
35100    /*
35101     restoreState : function(provider){
35102         if(!provider){
35103             provider = Roo.state.Manager;
35104         }
35105         var sm = new Roo.LayoutStateManager();
35106         sm.init(this, provider);
35107     },
35108 */
35109  
35110  
35111     /**
35112      * Adds a xtype elements to the layout.
35113      * <pre><code>
35114
35115 layout.addxtype({
35116        xtype : 'ContentPanel',
35117        region: 'west',
35118        items: [ .... ]
35119    }
35120 );
35121
35122 layout.addxtype({
35123         xtype : 'NestedLayoutPanel',
35124         region: 'west',
35125         layout: {
35126            center: { },
35127            west: { }   
35128         },
35129         items : [ ... list of content panels or nested layout panels.. ]
35130    }
35131 );
35132 </code></pre>
35133      * @param {Object} cfg Xtype definition of item to add.
35134      */
35135     addxtype : function(cfg)
35136     {
35137         // basically accepts a pannel...
35138         // can accept a layout region..!?!?
35139         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35140         
35141         
35142         // theory?  children can only be panels??
35143         
35144         //if (!cfg.xtype.match(/Panel$/)) {
35145         //    return false;
35146         //}
35147         var ret = false;
35148         
35149         if (typeof(cfg.region) == 'undefined') {
35150             Roo.log("Failed to add Panel, region was not set");
35151             Roo.log(cfg);
35152             return false;
35153         }
35154         var region = cfg.region;
35155         delete cfg.region;
35156         
35157           
35158         var xitems = [];
35159         if (cfg.items) {
35160             xitems = cfg.items;
35161             delete cfg.items;
35162         }
35163         var nb = false;
35164         
35165         switch(cfg.xtype) 
35166         {
35167             case 'Content':  // ContentPanel (el, cfg)
35168             case 'Scroll':  // ContentPanel (el, cfg)
35169             case 'View': 
35170                 cfg.autoCreate = true;
35171                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35172                 //} else {
35173                 //    var el = this.el.createChild();
35174                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35175                 //}
35176                 
35177                 this.add(region, ret);
35178                 break;
35179             
35180             /*
35181             case 'TreePanel': // our new panel!
35182                 cfg.el = this.el.createChild();
35183                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35184                 this.add(region, ret);
35185                 break;
35186             */
35187             
35188             case 'Nest': 
35189                 // create a new Layout (which is  a Border Layout...
35190                 
35191                 var clayout = cfg.layout;
35192                 clayout.el  = this.el.createChild();
35193                 clayout.items   = clayout.items  || [];
35194                 
35195                 delete cfg.layout;
35196                 
35197                 // replace this exitems with the clayout ones..
35198                 xitems = clayout.items;
35199                  
35200                 // force background off if it's in center...
35201                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35202                     cfg.background = false;
35203                 }
35204                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35205                 
35206                 
35207                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35208                 //console.log('adding nested layout panel '  + cfg.toSource());
35209                 this.add(region, ret);
35210                 nb = {}; /// find first...
35211                 break;
35212             
35213             case 'Grid':
35214                 
35215                 // needs grid and region
35216                 
35217                 //var el = this.getRegion(region).el.createChild();
35218                 /*
35219                  *var el = this.el.createChild();
35220                 // create the grid first...
35221                 cfg.grid.container = el;
35222                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35223                 */
35224                 
35225                 if (region == 'center' && this.active ) {
35226                     cfg.background = false;
35227                 }
35228                 
35229                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35230                 
35231                 this.add(region, ret);
35232                 /*
35233                 if (cfg.background) {
35234                     // render grid on panel activation (if panel background)
35235                     ret.on('activate', function(gp) {
35236                         if (!gp.grid.rendered) {
35237                     //        gp.grid.render(el);
35238                         }
35239                     });
35240                 } else {
35241                   //  cfg.grid.render(el);
35242                 }
35243                 */
35244                 break;
35245            
35246            
35247             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35248                 // it was the old xcomponent building that caused this before.
35249                 // espeically if border is the top element in the tree.
35250                 ret = this;
35251                 break; 
35252                 
35253                     
35254                 
35255                 
35256                 
35257             default:
35258                 /*
35259                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35260                     
35261                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35262                     this.add(region, ret);
35263                 } else {
35264                 */
35265                     Roo.log(cfg);
35266                     throw "Can not add '" + cfg.xtype + "' to Border";
35267                     return null;
35268              
35269                                 
35270              
35271         }
35272         this.beginUpdate();
35273         // add children..
35274         var region = '';
35275         var abn = {};
35276         Roo.each(xitems, function(i)  {
35277             region = nb && i.region ? i.region : false;
35278             
35279             var add = ret.addxtype(i);
35280            
35281             if (region) {
35282                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35283                 if (!i.background) {
35284                     abn[region] = nb[region] ;
35285                 }
35286             }
35287             
35288         });
35289         this.endUpdate();
35290
35291         // make the last non-background panel active..
35292         //if (nb) { Roo.log(abn); }
35293         if (nb) {
35294             
35295             for(var r in abn) {
35296                 region = this.getRegion(r);
35297                 if (region) {
35298                     // tried using nb[r], but it does not work..
35299                      
35300                     region.showPanel(abn[r]);
35301                    
35302                 }
35303             }
35304         }
35305         return ret;
35306         
35307     },
35308     
35309     
35310 // private
35311     factory : function(cfg)
35312     {
35313         
35314         var validRegions = Roo.bootstrap.layout.Border.regions;
35315
35316         var target = cfg.region;
35317         cfg.mgr = this;
35318         
35319         var r = Roo.bootstrap.layout;
35320         Roo.log(target);
35321         switch(target){
35322             case "north":
35323                 return new r.North(cfg);
35324             case "south":
35325                 return new r.South(cfg);
35326             case "east":
35327                 return new r.East(cfg);
35328             case "west":
35329                 return new r.West(cfg);
35330             case "center":
35331                 return new r.Center(cfg);
35332         }
35333         throw 'Layout region "'+target+'" not supported.';
35334     }
35335     
35336     
35337 });
35338  /*
35339  * Based on:
35340  * Ext JS Library 1.1.1
35341  * Copyright(c) 2006-2007, Ext JS, LLC.
35342  *
35343  * Originally Released Under LGPL - original licence link has changed is not relivant.
35344  *
35345  * Fork - LGPL
35346  * <script type="text/javascript">
35347  */
35348  
35349 /**
35350  * @class Roo.bootstrap.layout.Basic
35351  * @extends Roo.util.Observable
35352  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35353  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35354  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35355  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35356  * @cfg {string}   region  the region that it inhabits..
35357  * @cfg {bool}   skipConfig skip config?
35358  * 
35359
35360  */
35361 Roo.bootstrap.layout.Basic = function(config){
35362     
35363     this.mgr = config.mgr;
35364     
35365     this.position = config.region;
35366     
35367     var skipConfig = config.skipConfig;
35368     
35369     this.events = {
35370         /**
35371          * @scope Roo.BasicLayoutRegion
35372          */
35373         
35374         /**
35375          * @event beforeremove
35376          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35377          * @param {Roo.LayoutRegion} this
35378          * @param {Roo.ContentPanel} panel The panel
35379          * @param {Object} e The cancel event object
35380          */
35381         "beforeremove" : true,
35382         /**
35383          * @event invalidated
35384          * Fires when the layout for this region is changed.
35385          * @param {Roo.LayoutRegion} this
35386          */
35387         "invalidated" : true,
35388         /**
35389          * @event visibilitychange
35390          * Fires when this region is shown or hidden 
35391          * @param {Roo.LayoutRegion} this
35392          * @param {Boolean} visibility true or false
35393          */
35394         "visibilitychange" : true,
35395         /**
35396          * @event paneladded
35397          * Fires when a panel is added. 
35398          * @param {Roo.LayoutRegion} this
35399          * @param {Roo.ContentPanel} panel The panel
35400          */
35401         "paneladded" : true,
35402         /**
35403          * @event panelremoved
35404          * Fires when a panel is removed. 
35405          * @param {Roo.LayoutRegion} this
35406          * @param {Roo.ContentPanel} panel The panel
35407          */
35408         "panelremoved" : true,
35409         /**
35410          * @event beforecollapse
35411          * Fires when this region before collapse.
35412          * @param {Roo.LayoutRegion} this
35413          */
35414         "beforecollapse" : true,
35415         /**
35416          * @event collapsed
35417          * Fires when this region is collapsed.
35418          * @param {Roo.LayoutRegion} this
35419          */
35420         "collapsed" : true,
35421         /**
35422          * @event expanded
35423          * Fires when this region is expanded.
35424          * @param {Roo.LayoutRegion} this
35425          */
35426         "expanded" : true,
35427         /**
35428          * @event slideshow
35429          * Fires when this region is slid into view.
35430          * @param {Roo.LayoutRegion} this
35431          */
35432         "slideshow" : true,
35433         /**
35434          * @event slidehide
35435          * Fires when this region slides out of view. 
35436          * @param {Roo.LayoutRegion} this
35437          */
35438         "slidehide" : true,
35439         /**
35440          * @event panelactivated
35441          * Fires when a panel is activated. 
35442          * @param {Roo.LayoutRegion} this
35443          * @param {Roo.ContentPanel} panel The activated panel
35444          */
35445         "panelactivated" : true,
35446         /**
35447          * @event resized
35448          * Fires when the user resizes this region. 
35449          * @param {Roo.LayoutRegion} this
35450          * @param {Number} newSize The new size (width for east/west, height for north/south)
35451          */
35452         "resized" : true
35453     };
35454     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35455     this.panels = new Roo.util.MixedCollection();
35456     this.panels.getKey = this.getPanelId.createDelegate(this);
35457     this.box = null;
35458     this.activePanel = null;
35459     // ensure listeners are added...
35460     
35461     if (config.listeners || config.events) {
35462         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35463             listeners : config.listeners || {},
35464             events : config.events || {}
35465         });
35466     }
35467     
35468     if(skipConfig !== true){
35469         this.applyConfig(config);
35470     }
35471 };
35472
35473 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35474 {
35475     getPanelId : function(p){
35476         return p.getId();
35477     },
35478     
35479     applyConfig : function(config){
35480         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35481         this.config = config;
35482         
35483     },
35484     
35485     /**
35486      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35487      * the width, for horizontal (north, south) the height.
35488      * @param {Number} newSize The new width or height
35489      */
35490     resizeTo : function(newSize){
35491         var el = this.el ? this.el :
35492                  (this.activePanel ? this.activePanel.getEl() : null);
35493         if(el){
35494             switch(this.position){
35495                 case "east":
35496                 case "west":
35497                     el.setWidth(newSize);
35498                     this.fireEvent("resized", this, newSize);
35499                 break;
35500                 case "north":
35501                 case "south":
35502                     el.setHeight(newSize);
35503                     this.fireEvent("resized", this, newSize);
35504                 break;                
35505             }
35506         }
35507     },
35508     
35509     getBox : function(){
35510         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35511     },
35512     
35513     getMargins : function(){
35514         return this.margins;
35515     },
35516     
35517     updateBox : function(box){
35518         this.box = box;
35519         var el = this.activePanel.getEl();
35520         el.dom.style.left = box.x + "px";
35521         el.dom.style.top = box.y + "px";
35522         this.activePanel.setSize(box.width, box.height);
35523     },
35524     
35525     /**
35526      * Returns the container element for this region.
35527      * @return {Roo.Element}
35528      */
35529     getEl : function(){
35530         return this.activePanel;
35531     },
35532     
35533     /**
35534      * Returns true if this region is currently visible.
35535      * @return {Boolean}
35536      */
35537     isVisible : function(){
35538         return this.activePanel ? true : false;
35539     },
35540     
35541     setActivePanel : function(panel){
35542         panel = this.getPanel(panel);
35543         if(this.activePanel && this.activePanel != panel){
35544             this.activePanel.setActiveState(false);
35545             this.activePanel.getEl().setLeftTop(-10000,-10000);
35546         }
35547         this.activePanel = panel;
35548         panel.setActiveState(true);
35549         if(this.box){
35550             panel.setSize(this.box.width, this.box.height);
35551         }
35552         this.fireEvent("panelactivated", this, panel);
35553         this.fireEvent("invalidated");
35554     },
35555     
35556     /**
35557      * Show the specified panel.
35558      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35559      * @return {Roo.ContentPanel} The shown panel or null
35560      */
35561     showPanel : function(panel){
35562         panel = this.getPanel(panel);
35563         if(panel){
35564             this.setActivePanel(panel);
35565         }
35566         return panel;
35567     },
35568     
35569     /**
35570      * Get the active panel for this region.
35571      * @return {Roo.ContentPanel} The active panel or null
35572      */
35573     getActivePanel : function(){
35574         return this.activePanel;
35575     },
35576     
35577     /**
35578      * Add the passed ContentPanel(s)
35579      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35580      * @return {Roo.ContentPanel} The panel added (if only one was added)
35581      */
35582     add : function(panel){
35583         if(arguments.length > 1){
35584             for(var i = 0, len = arguments.length; i < len; i++) {
35585                 this.add(arguments[i]);
35586             }
35587             return null;
35588         }
35589         if(this.hasPanel(panel)){
35590             this.showPanel(panel);
35591             return panel;
35592         }
35593         var el = panel.getEl();
35594         if(el.dom.parentNode != this.mgr.el.dom){
35595             this.mgr.el.dom.appendChild(el.dom);
35596         }
35597         if(panel.setRegion){
35598             panel.setRegion(this);
35599         }
35600         this.panels.add(panel);
35601         el.setStyle("position", "absolute");
35602         if(!panel.background){
35603             this.setActivePanel(panel);
35604             if(this.config.initialSize && this.panels.getCount()==1){
35605                 this.resizeTo(this.config.initialSize);
35606             }
35607         }
35608         this.fireEvent("paneladded", this, panel);
35609         return panel;
35610     },
35611     
35612     /**
35613      * Returns true if the panel is in this region.
35614      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35615      * @return {Boolean}
35616      */
35617     hasPanel : function(panel){
35618         if(typeof panel == "object"){ // must be panel obj
35619             panel = panel.getId();
35620         }
35621         return this.getPanel(panel) ? true : false;
35622     },
35623     
35624     /**
35625      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35626      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35627      * @param {Boolean} preservePanel Overrides the config preservePanel option
35628      * @return {Roo.ContentPanel} The panel that was removed
35629      */
35630     remove : function(panel, preservePanel){
35631         panel = this.getPanel(panel);
35632         if(!panel){
35633             return null;
35634         }
35635         var e = {};
35636         this.fireEvent("beforeremove", this, panel, e);
35637         if(e.cancel === true){
35638             return null;
35639         }
35640         var panelId = panel.getId();
35641         this.panels.removeKey(panelId);
35642         return panel;
35643     },
35644     
35645     /**
35646      * Returns the panel specified or null if it's not in this region.
35647      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35648      * @return {Roo.ContentPanel}
35649      */
35650     getPanel : function(id){
35651         if(typeof id == "object"){ // must be panel obj
35652             return id;
35653         }
35654         return this.panels.get(id);
35655     },
35656     
35657     /**
35658      * Returns this regions position (north/south/east/west/center).
35659      * @return {String} 
35660      */
35661     getPosition: function(){
35662         return this.position;    
35663     }
35664 });/*
35665  * Based on:
35666  * Ext JS Library 1.1.1
35667  * Copyright(c) 2006-2007, Ext JS, LLC.
35668  *
35669  * Originally Released Under LGPL - original licence link has changed is not relivant.
35670  *
35671  * Fork - LGPL
35672  * <script type="text/javascript">
35673  */
35674  
35675 /**
35676  * @class Roo.bootstrap.layout.Region
35677  * @extends Roo.bootstrap.layout.Basic
35678  * This class represents a region in a layout manager.
35679  
35680  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35681  * @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})
35682  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35683  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35684  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35685  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35686  * @cfg {String}    title           The title for the region (overrides panel titles)
35687  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35688  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35689  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35690  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35691  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35692  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35693  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35694  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35695  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35696  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35697
35698  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35699  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35700  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35701  * @cfg {Number}    width           For East/West panels
35702  * @cfg {Number}    height          For North/South panels
35703  * @cfg {Boolean}   split           To show the splitter
35704  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35705  * 
35706  * @cfg {string}   cls             Extra CSS classes to add to region
35707  * 
35708  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35709  * @cfg {string}   region  the region that it inhabits..
35710  *
35711
35712  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35713  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35714
35715  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35716  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35717  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35718  */
35719 Roo.bootstrap.layout.Region = function(config)
35720 {
35721     this.applyConfig(config);
35722
35723     var mgr = config.mgr;
35724     var pos = config.region;
35725     config.skipConfig = true;
35726     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35727     
35728     if (mgr.el) {
35729         this.onRender(mgr.el);   
35730     }
35731      
35732     this.visible = true;
35733     this.collapsed = false;
35734     this.unrendered_panels = [];
35735 };
35736
35737 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35738
35739     position: '', // set by wrapper (eg. north/south etc..)
35740     unrendered_panels : null,  // unrendered panels.
35741     createBody : function(){
35742         /** This region's body element 
35743         * @type Roo.Element */
35744         this.bodyEl = this.el.createChild({
35745                 tag: "div",
35746                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35747         });
35748     },
35749
35750     onRender: function(ctr, pos)
35751     {
35752         var dh = Roo.DomHelper;
35753         /** This region's container element 
35754         * @type Roo.Element */
35755         this.el = dh.append(ctr.dom, {
35756                 tag: "div",
35757                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35758             }, true);
35759         /** This region's title element 
35760         * @type Roo.Element */
35761     
35762         this.titleEl = dh.append(this.el.dom,
35763             {
35764                     tag: "div",
35765                     unselectable: "on",
35766                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35767                     children:[
35768                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35769                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35770                     ]}, true);
35771         
35772         this.titleEl.enableDisplayMode();
35773         /** This region's title text element 
35774         * @type HTMLElement */
35775         this.titleTextEl = this.titleEl.dom.firstChild;
35776         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35777         /*
35778         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35779         this.closeBtn.enableDisplayMode();
35780         this.closeBtn.on("click", this.closeClicked, this);
35781         this.closeBtn.hide();
35782     */
35783         this.createBody(this.config);
35784         if(this.config.hideWhenEmpty){
35785             this.hide();
35786             this.on("paneladded", this.validateVisibility, this);
35787             this.on("panelremoved", this.validateVisibility, this);
35788         }
35789         if(this.autoScroll){
35790             this.bodyEl.setStyle("overflow", "auto");
35791         }else{
35792             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35793         }
35794         //if(c.titlebar !== false){
35795             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35796                 this.titleEl.hide();
35797             }else{
35798                 this.titleEl.show();
35799                 if(this.config.title){
35800                     this.titleTextEl.innerHTML = this.config.title;
35801                 }
35802             }
35803         //}
35804         if(this.config.collapsed){
35805             this.collapse(true);
35806         }
35807         if(this.config.hidden){
35808             this.hide();
35809         }
35810         
35811         if (this.unrendered_panels && this.unrendered_panels.length) {
35812             for (var i =0;i< this.unrendered_panels.length; i++) {
35813                 this.add(this.unrendered_panels[i]);
35814             }
35815             this.unrendered_panels = null;
35816             
35817         }
35818         
35819     },
35820     
35821     applyConfig : function(c)
35822     {
35823         /*
35824          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35825             var dh = Roo.DomHelper;
35826             if(c.titlebar !== false){
35827                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35828                 this.collapseBtn.on("click", this.collapse, this);
35829                 this.collapseBtn.enableDisplayMode();
35830                 /*
35831                 if(c.showPin === true || this.showPin){
35832                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35833                     this.stickBtn.enableDisplayMode();
35834                     this.stickBtn.on("click", this.expand, this);
35835                     this.stickBtn.hide();
35836                 }
35837                 
35838             }
35839             */
35840             /** This region's collapsed element
35841             * @type Roo.Element */
35842             /*
35843              *
35844             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35845                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35846             ]}, true);
35847             
35848             if(c.floatable !== false){
35849                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35850                this.collapsedEl.on("click", this.collapseClick, this);
35851             }
35852
35853             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35854                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35855                    id: "message", unselectable: "on", style:{"float":"left"}});
35856                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35857              }
35858             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35859             this.expandBtn.on("click", this.expand, this);
35860             
35861         }
35862         
35863         if(this.collapseBtn){
35864             this.collapseBtn.setVisible(c.collapsible == true);
35865         }
35866         
35867         this.cmargins = c.cmargins || this.cmargins ||
35868                          (this.position == "west" || this.position == "east" ?
35869                              {top: 0, left: 2, right:2, bottom: 0} :
35870                              {top: 2, left: 0, right:0, bottom: 2});
35871         */
35872         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35873         
35874         
35875         this.bottomTabs = c.tabPosition != "top";
35876         
35877         this.autoScroll = c.autoScroll || false;
35878         
35879         
35880        
35881         
35882         this.duration = c.duration || .30;
35883         this.slideDuration = c.slideDuration || .45;
35884         this.config = c;
35885        
35886     },
35887     /**
35888      * Returns true if this region is currently visible.
35889      * @return {Boolean}
35890      */
35891     isVisible : function(){
35892         return this.visible;
35893     },
35894
35895     /**
35896      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35897      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35898      */
35899     //setCollapsedTitle : function(title){
35900     //    title = title || "&#160;";
35901      //   if(this.collapsedTitleTextEl){
35902       //      this.collapsedTitleTextEl.innerHTML = title;
35903        // }
35904     //},
35905
35906     getBox : function(){
35907         var b;
35908       //  if(!this.collapsed){
35909             b = this.el.getBox(false, true);
35910        // }else{
35911           //  b = this.collapsedEl.getBox(false, true);
35912         //}
35913         return b;
35914     },
35915
35916     getMargins : function(){
35917         return this.margins;
35918         //return this.collapsed ? this.cmargins : this.margins;
35919     },
35920 /*
35921     highlight : function(){
35922         this.el.addClass("x-layout-panel-dragover");
35923     },
35924
35925     unhighlight : function(){
35926         this.el.removeClass("x-layout-panel-dragover");
35927     },
35928 */
35929     updateBox : function(box)
35930     {
35931         if (!this.bodyEl) {
35932             return; // not rendered yet..
35933         }
35934         
35935         this.box = box;
35936         if(!this.collapsed){
35937             this.el.dom.style.left = box.x + "px";
35938             this.el.dom.style.top = box.y + "px";
35939             this.updateBody(box.width, box.height);
35940         }else{
35941             this.collapsedEl.dom.style.left = box.x + "px";
35942             this.collapsedEl.dom.style.top = box.y + "px";
35943             this.collapsedEl.setSize(box.width, box.height);
35944         }
35945         if(this.tabs){
35946             this.tabs.autoSizeTabs();
35947         }
35948     },
35949
35950     updateBody : function(w, h)
35951     {
35952         if(w !== null){
35953             this.el.setWidth(w);
35954             w -= this.el.getBorderWidth("rl");
35955             if(this.config.adjustments){
35956                 w += this.config.adjustments[0];
35957             }
35958         }
35959         if(h !== null && h > 0){
35960             this.el.setHeight(h);
35961             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35962             h -= this.el.getBorderWidth("tb");
35963             if(this.config.adjustments){
35964                 h += this.config.adjustments[1];
35965             }
35966             this.bodyEl.setHeight(h);
35967             if(this.tabs){
35968                 h = this.tabs.syncHeight(h);
35969             }
35970         }
35971         if(this.panelSize){
35972             w = w !== null ? w : this.panelSize.width;
35973             h = h !== null ? h : this.panelSize.height;
35974         }
35975         if(this.activePanel){
35976             var el = this.activePanel.getEl();
35977             w = w !== null ? w : el.getWidth();
35978             h = h !== null ? h : el.getHeight();
35979             this.panelSize = {width: w, height: h};
35980             this.activePanel.setSize(w, h);
35981         }
35982         if(Roo.isIE && this.tabs){
35983             this.tabs.el.repaint();
35984         }
35985     },
35986
35987     /**
35988      * Returns the container element for this region.
35989      * @return {Roo.Element}
35990      */
35991     getEl : function(){
35992         return this.el;
35993     },
35994
35995     /**
35996      * Hides this region.
35997      */
35998     hide : function(){
35999         //if(!this.collapsed){
36000             this.el.dom.style.left = "-2000px";
36001             this.el.hide();
36002         //}else{
36003          //   this.collapsedEl.dom.style.left = "-2000px";
36004          //   this.collapsedEl.hide();
36005        // }
36006         this.visible = false;
36007         this.fireEvent("visibilitychange", this, false);
36008     },
36009
36010     /**
36011      * Shows this region if it was previously hidden.
36012      */
36013     show : function(){
36014         //if(!this.collapsed){
36015             this.el.show();
36016         //}else{
36017         //    this.collapsedEl.show();
36018        // }
36019         this.visible = true;
36020         this.fireEvent("visibilitychange", this, true);
36021     },
36022 /*
36023     closeClicked : function(){
36024         if(this.activePanel){
36025             this.remove(this.activePanel);
36026         }
36027     },
36028
36029     collapseClick : function(e){
36030         if(this.isSlid){
36031            e.stopPropagation();
36032            this.slideIn();
36033         }else{
36034            e.stopPropagation();
36035            this.slideOut();
36036         }
36037     },
36038 */
36039     /**
36040      * Collapses this region.
36041      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36042      */
36043     /*
36044     collapse : function(skipAnim, skipCheck = false){
36045         if(this.collapsed) {
36046             return;
36047         }
36048         
36049         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36050             
36051             this.collapsed = true;
36052             if(this.split){
36053                 this.split.el.hide();
36054             }
36055             if(this.config.animate && skipAnim !== true){
36056                 this.fireEvent("invalidated", this);
36057                 this.animateCollapse();
36058             }else{
36059                 this.el.setLocation(-20000,-20000);
36060                 this.el.hide();
36061                 this.collapsedEl.show();
36062                 this.fireEvent("collapsed", this);
36063                 this.fireEvent("invalidated", this);
36064             }
36065         }
36066         
36067     },
36068 */
36069     animateCollapse : function(){
36070         // overridden
36071     },
36072
36073     /**
36074      * Expands this region if it was previously collapsed.
36075      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36076      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36077      */
36078     /*
36079     expand : function(e, skipAnim){
36080         if(e) {
36081             e.stopPropagation();
36082         }
36083         if(!this.collapsed || this.el.hasActiveFx()) {
36084             return;
36085         }
36086         if(this.isSlid){
36087             this.afterSlideIn();
36088             skipAnim = true;
36089         }
36090         this.collapsed = false;
36091         if(this.config.animate && skipAnim !== true){
36092             this.animateExpand();
36093         }else{
36094             this.el.show();
36095             if(this.split){
36096                 this.split.el.show();
36097             }
36098             this.collapsedEl.setLocation(-2000,-2000);
36099             this.collapsedEl.hide();
36100             this.fireEvent("invalidated", this);
36101             this.fireEvent("expanded", this);
36102         }
36103     },
36104 */
36105     animateExpand : function(){
36106         // overridden
36107     },
36108
36109     initTabs : function()
36110     {
36111         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36112         
36113         var ts = new Roo.bootstrap.panel.Tabs({
36114                 el: this.bodyEl.dom,
36115                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36116                 disableTooltips: this.config.disableTabTips,
36117                 toolbar : this.config.toolbar
36118             });
36119         
36120         if(this.config.hideTabs){
36121             ts.stripWrap.setDisplayed(false);
36122         }
36123         this.tabs = ts;
36124         ts.resizeTabs = this.config.resizeTabs === true;
36125         ts.minTabWidth = this.config.minTabWidth || 40;
36126         ts.maxTabWidth = this.config.maxTabWidth || 250;
36127         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36128         ts.monitorResize = false;
36129         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36130         ts.bodyEl.addClass('roo-layout-tabs-body');
36131         this.panels.each(this.initPanelAsTab, this);
36132     },
36133
36134     initPanelAsTab : function(panel){
36135         var ti = this.tabs.addTab(
36136             panel.getEl().id,
36137             panel.getTitle(),
36138             null,
36139             this.config.closeOnTab && panel.isClosable(),
36140             panel.tpl
36141         );
36142         if(panel.tabTip !== undefined){
36143             ti.setTooltip(panel.tabTip);
36144         }
36145         ti.on("activate", function(){
36146               this.setActivePanel(panel);
36147         }, this);
36148         
36149         if(this.config.closeOnTab){
36150             ti.on("beforeclose", function(t, e){
36151                 e.cancel = true;
36152                 this.remove(panel);
36153             }, this);
36154         }
36155         
36156         panel.tabItem = ti;
36157         
36158         return ti;
36159     },
36160
36161     updatePanelTitle : function(panel, title)
36162     {
36163         if(this.activePanel == panel){
36164             this.updateTitle(title);
36165         }
36166         if(this.tabs){
36167             var ti = this.tabs.getTab(panel.getEl().id);
36168             ti.setText(title);
36169             if(panel.tabTip !== undefined){
36170                 ti.setTooltip(panel.tabTip);
36171             }
36172         }
36173     },
36174
36175     updateTitle : function(title){
36176         if(this.titleTextEl && !this.config.title){
36177             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36178         }
36179     },
36180
36181     setActivePanel : function(panel)
36182     {
36183         panel = this.getPanel(panel);
36184         if(this.activePanel && this.activePanel != panel){
36185             if(this.activePanel.setActiveState(false) === false){
36186                 return;
36187             }
36188         }
36189         this.activePanel = panel;
36190         panel.setActiveState(true);
36191         if(this.panelSize){
36192             panel.setSize(this.panelSize.width, this.panelSize.height);
36193         }
36194         if(this.closeBtn){
36195             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36196         }
36197         this.updateTitle(panel.getTitle());
36198         if(this.tabs){
36199             this.fireEvent("invalidated", this);
36200         }
36201         this.fireEvent("panelactivated", this, panel);
36202     },
36203
36204     /**
36205      * Shows the specified panel.
36206      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36207      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36208      */
36209     showPanel : function(panel)
36210     {
36211         panel = this.getPanel(panel);
36212         if(panel){
36213             if(this.tabs){
36214                 var tab = this.tabs.getTab(panel.getEl().id);
36215                 if(tab.isHidden()){
36216                     this.tabs.unhideTab(tab.id);
36217                 }
36218                 tab.activate();
36219             }else{
36220                 this.setActivePanel(panel);
36221             }
36222         }
36223         return panel;
36224     },
36225
36226     /**
36227      * Get the active panel for this region.
36228      * @return {Roo.ContentPanel} The active panel or null
36229      */
36230     getActivePanel : function(){
36231         return this.activePanel;
36232     },
36233
36234     validateVisibility : function(){
36235         if(this.panels.getCount() < 1){
36236             this.updateTitle("&#160;");
36237             this.closeBtn.hide();
36238             this.hide();
36239         }else{
36240             if(!this.isVisible()){
36241                 this.show();
36242             }
36243         }
36244     },
36245
36246     /**
36247      * Adds the passed ContentPanel(s) to this region.
36248      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36249      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36250      */
36251     add : function(panel)
36252     {
36253         if(arguments.length > 1){
36254             for(var i = 0, len = arguments.length; i < len; i++) {
36255                 this.add(arguments[i]);
36256             }
36257             return null;
36258         }
36259         
36260         // if we have not been rendered yet, then we can not really do much of this..
36261         if (!this.bodyEl) {
36262             this.unrendered_panels.push(panel);
36263             return panel;
36264         }
36265         
36266         
36267         
36268         
36269         if(this.hasPanel(panel)){
36270             this.showPanel(panel);
36271             return panel;
36272         }
36273         panel.setRegion(this);
36274         this.panels.add(panel);
36275        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36276             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36277             // and hide them... ???
36278             this.bodyEl.dom.appendChild(panel.getEl().dom);
36279             if(panel.background !== true){
36280                 this.setActivePanel(panel);
36281             }
36282             this.fireEvent("paneladded", this, panel);
36283             return panel;
36284         }
36285         */
36286         if(!this.tabs){
36287             this.initTabs();
36288         }else{
36289             this.initPanelAsTab(panel);
36290         }
36291         
36292         
36293         if(panel.background !== true){
36294             this.tabs.activate(panel.getEl().id);
36295         }
36296         this.fireEvent("paneladded", this, panel);
36297         return panel;
36298     },
36299
36300     /**
36301      * Hides the tab for the specified panel.
36302      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36303      */
36304     hidePanel : function(panel){
36305         if(this.tabs && (panel = this.getPanel(panel))){
36306             this.tabs.hideTab(panel.getEl().id);
36307         }
36308     },
36309
36310     /**
36311      * Unhides the tab for a previously hidden panel.
36312      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36313      */
36314     unhidePanel : function(panel){
36315         if(this.tabs && (panel = this.getPanel(panel))){
36316             this.tabs.unhideTab(panel.getEl().id);
36317         }
36318     },
36319
36320     clearPanels : function(){
36321         while(this.panels.getCount() > 0){
36322              this.remove(this.panels.first());
36323         }
36324     },
36325
36326     /**
36327      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36328      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36329      * @param {Boolean} preservePanel Overrides the config preservePanel option
36330      * @return {Roo.ContentPanel} The panel that was removed
36331      */
36332     remove : function(panel, preservePanel)
36333     {
36334         panel = this.getPanel(panel);
36335         if(!panel){
36336             return null;
36337         }
36338         var e = {};
36339         this.fireEvent("beforeremove", this, panel, e);
36340         if(e.cancel === true){
36341             return null;
36342         }
36343         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36344         var panelId = panel.getId();
36345         this.panels.removeKey(panelId);
36346         if(preservePanel){
36347             document.body.appendChild(panel.getEl().dom);
36348         }
36349         if(this.tabs){
36350             this.tabs.removeTab(panel.getEl().id);
36351         }else if (!preservePanel){
36352             this.bodyEl.dom.removeChild(panel.getEl().dom);
36353         }
36354         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36355             var p = this.panels.first();
36356             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36357             tempEl.appendChild(p.getEl().dom);
36358             this.bodyEl.update("");
36359             this.bodyEl.dom.appendChild(p.getEl().dom);
36360             tempEl = null;
36361             this.updateTitle(p.getTitle());
36362             this.tabs = null;
36363             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36364             this.setActivePanel(p);
36365         }
36366         panel.setRegion(null);
36367         if(this.activePanel == panel){
36368             this.activePanel = null;
36369         }
36370         if(this.config.autoDestroy !== false && preservePanel !== true){
36371             try{panel.destroy();}catch(e){}
36372         }
36373         this.fireEvent("panelremoved", this, panel);
36374         return panel;
36375     },
36376
36377     /**
36378      * Returns the TabPanel component used by this region
36379      * @return {Roo.TabPanel}
36380      */
36381     getTabs : function(){
36382         return this.tabs;
36383     },
36384
36385     createTool : function(parentEl, className){
36386         var btn = Roo.DomHelper.append(parentEl, {
36387             tag: "div",
36388             cls: "x-layout-tools-button",
36389             children: [ {
36390                 tag: "div",
36391                 cls: "roo-layout-tools-button-inner " + className,
36392                 html: "&#160;"
36393             }]
36394         }, true);
36395         btn.addClassOnOver("roo-layout-tools-button-over");
36396         return btn;
36397     }
36398 });/*
36399  * Based on:
36400  * Ext JS Library 1.1.1
36401  * Copyright(c) 2006-2007, Ext JS, LLC.
36402  *
36403  * Originally Released Under LGPL - original licence link has changed is not relivant.
36404  *
36405  * Fork - LGPL
36406  * <script type="text/javascript">
36407  */
36408  
36409
36410
36411 /**
36412  * @class Roo.SplitLayoutRegion
36413  * @extends Roo.LayoutRegion
36414  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36415  */
36416 Roo.bootstrap.layout.Split = function(config){
36417     this.cursor = config.cursor;
36418     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36419 };
36420
36421 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36422 {
36423     splitTip : "Drag to resize.",
36424     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36425     useSplitTips : false,
36426
36427     applyConfig : function(config){
36428         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36429     },
36430     
36431     onRender : function(ctr,pos) {
36432         
36433         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36434         if(!this.config.split){
36435             return;
36436         }
36437         if(!this.split){
36438             
36439             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36440                             tag: "div",
36441                             id: this.el.id + "-split",
36442                             cls: "roo-layout-split roo-layout-split-"+this.position,
36443                             html: "&#160;"
36444             });
36445             /** The SplitBar for this region 
36446             * @type Roo.SplitBar */
36447             // does not exist yet...
36448             Roo.log([this.position, this.orientation]);
36449             
36450             this.split = new Roo.bootstrap.SplitBar({
36451                 dragElement : splitEl,
36452                 resizingElement: this.el,
36453                 orientation : this.orientation
36454             });
36455             
36456             this.split.on("moved", this.onSplitMove, this);
36457             this.split.useShim = this.config.useShim === true;
36458             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36459             if(this.useSplitTips){
36460                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36461             }
36462             //if(config.collapsible){
36463             //    this.split.el.on("dblclick", this.collapse,  this);
36464             //}
36465         }
36466         if(typeof this.config.minSize != "undefined"){
36467             this.split.minSize = this.config.minSize;
36468         }
36469         if(typeof this.config.maxSize != "undefined"){
36470             this.split.maxSize = this.config.maxSize;
36471         }
36472         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36473             this.hideSplitter();
36474         }
36475         
36476     },
36477
36478     getHMaxSize : function(){
36479          var cmax = this.config.maxSize || 10000;
36480          var center = this.mgr.getRegion("center");
36481          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36482     },
36483
36484     getVMaxSize : function(){
36485          var cmax = this.config.maxSize || 10000;
36486          var center = this.mgr.getRegion("center");
36487          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36488     },
36489
36490     onSplitMove : function(split, newSize){
36491         this.fireEvent("resized", this, newSize);
36492     },
36493     
36494     /** 
36495      * Returns the {@link Roo.SplitBar} for this region.
36496      * @return {Roo.SplitBar}
36497      */
36498     getSplitBar : function(){
36499         return this.split;
36500     },
36501     
36502     hide : function(){
36503         this.hideSplitter();
36504         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36505     },
36506
36507     hideSplitter : function(){
36508         if(this.split){
36509             this.split.el.setLocation(-2000,-2000);
36510             this.split.el.hide();
36511         }
36512     },
36513
36514     show : function(){
36515         if(this.split){
36516             this.split.el.show();
36517         }
36518         Roo.bootstrap.layout.Split.superclass.show.call(this);
36519     },
36520     
36521     beforeSlide: function(){
36522         if(Roo.isGecko){// firefox overflow auto bug workaround
36523             this.bodyEl.clip();
36524             if(this.tabs) {
36525                 this.tabs.bodyEl.clip();
36526             }
36527             if(this.activePanel){
36528                 this.activePanel.getEl().clip();
36529                 
36530                 if(this.activePanel.beforeSlide){
36531                     this.activePanel.beforeSlide();
36532                 }
36533             }
36534         }
36535     },
36536     
36537     afterSlide : function(){
36538         if(Roo.isGecko){// firefox overflow auto bug workaround
36539             this.bodyEl.unclip();
36540             if(this.tabs) {
36541                 this.tabs.bodyEl.unclip();
36542             }
36543             if(this.activePanel){
36544                 this.activePanel.getEl().unclip();
36545                 if(this.activePanel.afterSlide){
36546                     this.activePanel.afterSlide();
36547                 }
36548             }
36549         }
36550     },
36551
36552     initAutoHide : function(){
36553         if(this.autoHide !== false){
36554             if(!this.autoHideHd){
36555                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36556                 this.autoHideHd = {
36557                     "mouseout": function(e){
36558                         if(!e.within(this.el, true)){
36559                             st.delay(500);
36560                         }
36561                     },
36562                     "mouseover" : function(e){
36563                         st.cancel();
36564                     },
36565                     scope : this
36566                 };
36567             }
36568             this.el.on(this.autoHideHd);
36569         }
36570     },
36571
36572     clearAutoHide : function(){
36573         if(this.autoHide !== false){
36574             this.el.un("mouseout", this.autoHideHd.mouseout);
36575             this.el.un("mouseover", this.autoHideHd.mouseover);
36576         }
36577     },
36578
36579     clearMonitor : function(){
36580         Roo.get(document).un("click", this.slideInIf, this);
36581     },
36582
36583     // these names are backwards but not changed for compat
36584     slideOut : function(){
36585         if(this.isSlid || this.el.hasActiveFx()){
36586             return;
36587         }
36588         this.isSlid = true;
36589         if(this.collapseBtn){
36590             this.collapseBtn.hide();
36591         }
36592         this.closeBtnState = this.closeBtn.getStyle('display');
36593         this.closeBtn.hide();
36594         if(this.stickBtn){
36595             this.stickBtn.show();
36596         }
36597         this.el.show();
36598         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36599         this.beforeSlide();
36600         this.el.setStyle("z-index", 10001);
36601         this.el.slideIn(this.getSlideAnchor(), {
36602             callback: function(){
36603                 this.afterSlide();
36604                 this.initAutoHide();
36605                 Roo.get(document).on("click", this.slideInIf, this);
36606                 this.fireEvent("slideshow", this);
36607             },
36608             scope: this,
36609             block: true
36610         });
36611     },
36612
36613     afterSlideIn : function(){
36614         this.clearAutoHide();
36615         this.isSlid = false;
36616         this.clearMonitor();
36617         this.el.setStyle("z-index", "");
36618         if(this.collapseBtn){
36619             this.collapseBtn.show();
36620         }
36621         this.closeBtn.setStyle('display', this.closeBtnState);
36622         if(this.stickBtn){
36623             this.stickBtn.hide();
36624         }
36625         this.fireEvent("slidehide", this);
36626     },
36627
36628     slideIn : function(cb){
36629         if(!this.isSlid || this.el.hasActiveFx()){
36630             Roo.callback(cb);
36631             return;
36632         }
36633         this.isSlid = false;
36634         this.beforeSlide();
36635         this.el.slideOut(this.getSlideAnchor(), {
36636             callback: function(){
36637                 this.el.setLeftTop(-10000, -10000);
36638                 this.afterSlide();
36639                 this.afterSlideIn();
36640                 Roo.callback(cb);
36641             },
36642             scope: this,
36643             block: true
36644         });
36645     },
36646     
36647     slideInIf : function(e){
36648         if(!e.within(this.el)){
36649             this.slideIn();
36650         }
36651     },
36652
36653     animateCollapse : function(){
36654         this.beforeSlide();
36655         this.el.setStyle("z-index", 20000);
36656         var anchor = this.getSlideAnchor();
36657         this.el.slideOut(anchor, {
36658             callback : function(){
36659                 this.el.setStyle("z-index", "");
36660                 this.collapsedEl.slideIn(anchor, {duration:.3});
36661                 this.afterSlide();
36662                 this.el.setLocation(-10000,-10000);
36663                 this.el.hide();
36664                 this.fireEvent("collapsed", this);
36665             },
36666             scope: this,
36667             block: true
36668         });
36669     },
36670
36671     animateExpand : function(){
36672         this.beforeSlide();
36673         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36674         this.el.setStyle("z-index", 20000);
36675         this.collapsedEl.hide({
36676             duration:.1
36677         });
36678         this.el.slideIn(this.getSlideAnchor(), {
36679             callback : function(){
36680                 this.el.setStyle("z-index", "");
36681                 this.afterSlide();
36682                 if(this.split){
36683                     this.split.el.show();
36684                 }
36685                 this.fireEvent("invalidated", this);
36686                 this.fireEvent("expanded", this);
36687             },
36688             scope: this,
36689             block: true
36690         });
36691     },
36692
36693     anchors : {
36694         "west" : "left",
36695         "east" : "right",
36696         "north" : "top",
36697         "south" : "bottom"
36698     },
36699
36700     sanchors : {
36701         "west" : "l",
36702         "east" : "r",
36703         "north" : "t",
36704         "south" : "b"
36705     },
36706
36707     canchors : {
36708         "west" : "tl-tr",
36709         "east" : "tr-tl",
36710         "north" : "tl-bl",
36711         "south" : "bl-tl"
36712     },
36713
36714     getAnchor : function(){
36715         return this.anchors[this.position];
36716     },
36717
36718     getCollapseAnchor : function(){
36719         return this.canchors[this.position];
36720     },
36721
36722     getSlideAnchor : function(){
36723         return this.sanchors[this.position];
36724     },
36725
36726     getAlignAdj : function(){
36727         var cm = this.cmargins;
36728         switch(this.position){
36729             case "west":
36730                 return [0, 0];
36731             break;
36732             case "east":
36733                 return [0, 0];
36734             break;
36735             case "north":
36736                 return [0, 0];
36737             break;
36738             case "south":
36739                 return [0, 0];
36740             break;
36741         }
36742     },
36743
36744     getExpandAdj : function(){
36745         var c = this.collapsedEl, cm = this.cmargins;
36746         switch(this.position){
36747             case "west":
36748                 return [-(cm.right+c.getWidth()+cm.left), 0];
36749             break;
36750             case "east":
36751                 return [cm.right+c.getWidth()+cm.left, 0];
36752             break;
36753             case "north":
36754                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36755             break;
36756             case "south":
36757                 return [0, cm.top+cm.bottom+c.getHeight()];
36758             break;
36759         }
36760     }
36761 });/*
36762  * Based on:
36763  * Ext JS Library 1.1.1
36764  * Copyright(c) 2006-2007, Ext JS, LLC.
36765  *
36766  * Originally Released Under LGPL - original licence link has changed is not relivant.
36767  *
36768  * Fork - LGPL
36769  * <script type="text/javascript">
36770  */
36771 /*
36772  * These classes are private internal classes
36773  */
36774 Roo.bootstrap.layout.Center = function(config){
36775     config.region = "center";
36776     Roo.bootstrap.layout.Region.call(this, config);
36777     this.visible = true;
36778     this.minWidth = config.minWidth || 20;
36779     this.minHeight = config.minHeight || 20;
36780 };
36781
36782 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36783     hide : function(){
36784         // center panel can't be hidden
36785     },
36786     
36787     show : function(){
36788         // center panel can't be hidden
36789     },
36790     
36791     getMinWidth: function(){
36792         return this.minWidth;
36793     },
36794     
36795     getMinHeight: function(){
36796         return this.minHeight;
36797     }
36798 });
36799
36800
36801
36802
36803  
36804
36805
36806
36807
36808
36809 Roo.bootstrap.layout.North = function(config)
36810 {
36811     config.region = 'north';
36812     config.cursor = 'n-resize';
36813     
36814     Roo.bootstrap.layout.Split.call(this, config);
36815     
36816     
36817     if(this.split){
36818         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36819         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36820         this.split.el.addClass("roo-layout-split-v");
36821     }
36822     var size = config.initialSize || config.height;
36823     if(typeof size != "undefined"){
36824         this.el.setHeight(size);
36825     }
36826 };
36827 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36828 {
36829     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36830     
36831     
36832     
36833     getBox : function(){
36834         if(this.collapsed){
36835             return this.collapsedEl.getBox();
36836         }
36837         var box = this.el.getBox();
36838         if(this.split){
36839             box.height += this.split.el.getHeight();
36840         }
36841         return box;
36842     },
36843     
36844     updateBox : function(box){
36845         if(this.split && !this.collapsed){
36846             box.height -= this.split.el.getHeight();
36847             this.split.el.setLeft(box.x);
36848             this.split.el.setTop(box.y+box.height);
36849             this.split.el.setWidth(box.width);
36850         }
36851         if(this.collapsed){
36852             this.updateBody(box.width, null);
36853         }
36854         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36855     }
36856 });
36857
36858
36859
36860
36861
36862 Roo.bootstrap.layout.South = function(config){
36863     config.region = 'south';
36864     config.cursor = 's-resize';
36865     Roo.bootstrap.layout.Split.call(this, config);
36866     if(this.split){
36867         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36868         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36869         this.split.el.addClass("roo-layout-split-v");
36870     }
36871     var size = config.initialSize || config.height;
36872     if(typeof size != "undefined"){
36873         this.el.setHeight(size);
36874     }
36875 };
36876
36877 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36878     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36879     getBox : function(){
36880         if(this.collapsed){
36881             return this.collapsedEl.getBox();
36882         }
36883         var box = this.el.getBox();
36884         if(this.split){
36885             var sh = this.split.el.getHeight();
36886             box.height += sh;
36887             box.y -= sh;
36888         }
36889         return box;
36890     },
36891     
36892     updateBox : function(box){
36893         if(this.split && !this.collapsed){
36894             var sh = this.split.el.getHeight();
36895             box.height -= sh;
36896             box.y += sh;
36897             this.split.el.setLeft(box.x);
36898             this.split.el.setTop(box.y-sh);
36899             this.split.el.setWidth(box.width);
36900         }
36901         if(this.collapsed){
36902             this.updateBody(box.width, null);
36903         }
36904         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36905     }
36906 });
36907
36908 Roo.bootstrap.layout.East = function(config){
36909     config.region = "east";
36910     config.cursor = "e-resize";
36911     Roo.bootstrap.layout.Split.call(this, config);
36912     if(this.split){
36913         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36914         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36915         this.split.el.addClass("roo-layout-split-h");
36916     }
36917     var size = config.initialSize || config.width;
36918     if(typeof size != "undefined"){
36919         this.el.setWidth(size);
36920     }
36921 };
36922 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36923     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36924     getBox : function(){
36925         if(this.collapsed){
36926             return this.collapsedEl.getBox();
36927         }
36928         var box = this.el.getBox();
36929         if(this.split){
36930             var sw = this.split.el.getWidth();
36931             box.width += sw;
36932             box.x -= sw;
36933         }
36934         return box;
36935     },
36936
36937     updateBox : function(box){
36938         if(this.split && !this.collapsed){
36939             var sw = this.split.el.getWidth();
36940             box.width -= sw;
36941             this.split.el.setLeft(box.x);
36942             this.split.el.setTop(box.y);
36943             this.split.el.setHeight(box.height);
36944             box.x += sw;
36945         }
36946         if(this.collapsed){
36947             this.updateBody(null, box.height);
36948         }
36949         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36950     }
36951 });
36952
36953 Roo.bootstrap.layout.West = function(config){
36954     config.region = "west";
36955     config.cursor = "w-resize";
36956     
36957     Roo.bootstrap.layout.Split.call(this, config);
36958     if(this.split){
36959         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36960         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36961         this.split.el.addClass("roo-layout-split-h");
36962     }
36963     
36964 };
36965 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36966     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36967     
36968     onRender: function(ctr, pos)
36969     {
36970         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36971         var size = this.config.initialSize || this.config.width;
36972         if(typeof size != "undefined"){
36973             this.el.setWidth(size);
36974         }
36975     },
36976     
36977     getBox : function(){
36978         if(this.collapsed){
36979             return this.collapsedEl.getBox();
36980         }
36981         var box = this.el.getBox();
36982         if(this.split){
36983             box.width += this.split.el.getWidth();
36984         }
36985         return box;
36986     },
36987     
36988     updateBox : function(box){
36989         if(this.split && !this.collapsed){
36990             var sw = this.split.el.getWidth();
36991             box.width -= sw;
36992             this.split.el.setLeft(box.x+box.width);
36993             this.split.el.setTop(box.y);
36994             this.split.el.setHeight(box.height);
36995         }
36996         if(this.collapsed){
36997             this.updateBody(null, box.height);
36998         }
36999         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37000     }
37001 });
37002 Roo.namespace("Roo.bootstrap.panel");/*
37003  * Based on:
37004  * Ext JS Library 1.1.1
37005  * Copyright(c) 2006-2007, Ext JS, LLC.
37006  *
37007  * Originally Released Under LGPL - original licence link has changed is not relivant.
37008  *
37009  * Fork - LGPL
37010  * <script type="text/javascript">
37011  */
37012 /**
37013  * @class Roo.ContentPanel
37014  * @extends Roo.util.Observable
37015  * A basic ContentPanel element.
37016  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37017  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37018  * @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
37019  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37020  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37021  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37022  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37023  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37024  * @cfg {String} title          The title for this panel
37025  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37026  * @cfg {String} url            Calls {@link #setUrl} with this value
37027  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37028  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37029  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37030  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37031  * @cfg {Boolean} badges render the badges
37032
37033  * @constructor
37034  * Create a new ContentPanel.
37035  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37036  * @param {String/Object} config A string to set only the title or a config object
37037  * @param {String} content (optional) Set the HTML content for this panel
37038  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37039  */
37040 Roo.bootstrap.panel.Content = function( config){
37041     
37042     this.tpl = config.tpl || false;
37043     
37044     var el = config.el;
37045     var content = config.content;
37046
37047     if(config.autoCreate){ // xtype is available if this is called from factory
37048         el = Roo.id();
37049     }
37050     this.el = Roo.get(el);
37051     if(!this.el && config && config.autoCreate){
37052         if(typeof config.autoCreate == "object"){
37053             if(!config.autoCreate.id){
37054                 config.autoCreate.id = config.id||el;
37055             }
37056             this.el = Roo.DomHelper.append(document.body,
37057                         config.autoCreate, true);
37058         }else{
37059             var elcfg =  {   tag: "div",
37060                             cls: "roo-layout-inactive-content",
37061                             id: config.id||el
37062                             };
37063             if (config.html) {
37064                 elcfg.html = config.html;
37065                 
37066             }
37067                         
37068             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37069         }
37070     } 
37071     this.closable = false;
37072     this.loaded = false;
37073     this.active = false;
37074    
37075       
37076     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37077         
37078         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37079         
37080         this.wrapEl = this.el; //this.el.wrap();
37081         var ti = [];
37082         if (config.toolbar.items) {
37083             ti = config.toolbar.items ;
37084             delete config.toolbar.items ;
37085         }
37086         
37087         var nitems = [];
37088         this.toolbar.render(this.wrapEl, 'before');
37089         for(var i =0;i < ti.length;i++) {
37090           //  Roo.log(['add child', items[i]]);
37091             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37092         }
37093         this.toolbar.items = nitems;
37094         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37095         delete config.toolbar;
37096         
37097     }
37098     /*
37099     // xtype created footer. - not sure if will work as we normally have to render first..
37100     if (this.footer && !this.footer.el && this.footer.xtype) {
37101         if (!this.wrapEl) {
37102             this.wrapEl = this.el.wrap();
37103         }
37104     
37105         this.footer.container = this.wrapEl.createChild();
37106          
37107         this.footer = Roo.factory(this.footer, Roo);
37108         
37109     }
37110     */
37111     
37112      if(typeof config == "string"){
37113         this.title = config;
37114     }else{
37115         Roo.apply(this, config);
37116     }
37117     
37118     if(this.resizeEl){
37119         this.resizeEl = Roo.get(this.resizeEl, true);
37120     }else{
37121         this.resizeEl = this.el;
37122     }
37123     // handle view.xtype
37124     
37125  
37126     
37127     
37128     this.addEvents({
37129         /**
37130          * @event activate
37131          * Fires when this panel is activated. 
37132          * @param {Roo.ContentPanel} this
37133          */
37134         "activate" : true,
37135         /**
37136          * @event deactivate
37137          * Fires when this panel is activated. 
37138          * @param {Roo.ContentPanel} this
37139          */
37140         "deactivate" : true,
37141
37142         /**
37143          * @event resize
37144          * Fires when this panel is resized if fitToFrame is true.
37145          * @param {Roo.ContentPanel} this
37146          * @param {Number} width The width after any component adjustments
37147          * @param {Number} height The height after any component adjustments
37148          */
37149         "resize" : true,
37150         
37151          /**
37152          * @event render
37153          * Fires when this tab is created
37154          * @param {Roo.ContentPanel} this
37155          */
37156         "render" : true
37157         
37158         
37159         
37160     });
37161     
37162
37163     
37164     
37165     if(this.autoScroll){
37166         this.resizeEl.setStyle("overflow", "auto");
37167     } else {
37168         // fix randome scrolling
37169         //this.el.on('scroll', function() {
37170         //    Roo.log('fix random scolling');
37171         //    this.scrollTo('top',0); 
37172         //});
37173     }
37174     content = content || this.content;
37175     if(content){
37176         this.setContent(content);
37177     }
37178     if(config && config.url){
37179         this.setUrl(this.url, this.params, this.loadOnce);
37180     }
37181     
37182     
37183     
37184     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37185     
37186     if (this.view && typeof(this.view.xtype) != 'undefined') {
37187         this.view.el = this.el.appendChild(document.createElement("div"));
37188         this.view = Roo.factory(this.view); 
37189         this.view.render  &&  this.view.render(false, '');  
37190     }
37191     
37192     
37193     this.fireEvent('render', this);
37194 };
37195
37196 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37197     
37198     tabTip : '',
37199     
37200     setRegion : function(region){
37201         this.region = region;
37202         this.setActiveClass(region && !this.background);
37203     },
37204     
37205     
37206     setActiveClass: function(state)
37207     {
37208         if(state){
37209            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37210            this.el.setStyle('position','relative');
37211         }else{
37212            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37213            this.el.setStyle('position', 'absolute');
37214         } 
37215     },
37216     
37217     /**
37218      * Returns the toolbar for this Panel if one was configured. 
37219      * @return {Roo.Toolbar} 
37220      */
37221     getToolbar : function(){
37222         return this.toolbar;
37223     },
37224     
37225     setActiveState : function(active)
37226     {
37227         this.active = active;
37228         this.setActiveClass(active);
37229         if(!active){
37230             if(this.fireEvent("deactivate", this) === false){
37231                 return false;
37232             }
37233             return true;
37234         }
37235         this.fireEvent("activate", this);
37236         return true;
37237     },
37238     /**
37239      * Updates this panel's element
37240      * @param {String} content The new content
37241      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37242     */
37243     setContent : function(content, loadScripts){
37244         this.el.update(content, loadScripts);
37245     },
37246
37247     ignoreResize : function(w, h){
37248         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37249             return true;
37250         }else{
37251             this.lastSize = {width: w, height: h};
37252             return false;
37253         }
37254     },
37255     /**
37256      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37257      * @return {Roo.UpdateManager} The UpdateManager
37258      */
37259     getUpdateManager : function(){
37260         return this.el.getUpdateManager();
37261     },
37262      /**
37263      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37264      * @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:
37265 <pre><code>
37266 panel.load({
37267     url: "your-url.php",
37268     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37269     callback: yourFunction,
37270     scope: yourObject, //(optional scope)
37271     discardUrl: false,
37272     nocache: false,
37273     text: "Loading...",
37274     timeout: 30,
37275     scripts: false
37276 });
37277 </code></pre>
37278      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37279      * 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.
37280      * @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}
37281      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37282      * @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.
37283      * @return {Roo.ContentPanel} this
37284      */
37285     load : function(){
37286         var um = this.el.getUpdateManager();
37287         um.update.apply(um, arguments);
37288         return this;
37289     },
37290
37291
37292     /**
37293      * 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.
37294      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37295      * @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)
37296      * @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)
37297      * @return {Roo.UpdateManager} The UpdateManager
37298      */
37299     setUrl : function(url, params, loadOnce){
37300         if(this.refreshDelegate){
37301             this.removeListener("activate", this.refreshDelegate);
37302         }
37303         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37304         this.on("activate", this.refreshDelegate);
37305         return this.el.getUpdateManager();
37306     },
37307     
37308     _handleRefresh : function(url, params, loadOnce){
37309         if(!loadOnce || !this.loaded){
37310             var updater = this.el.getUpdateManager();
37311             updater.update(url, params, this._setLoaded.createDelegate(this));
37312         }
37313     },
37314     
37315     _setLoaded : function(){
37316         this.loaded = true;
37317     }, 
37318     
37319     /**
37320      * Returns this panel's id
37321      * @return {String} 
37322      */
37323     getId : function(){
37324         return this.el.id;
37325     },
37326     
37327     /** 
37328      * Returns this panel's element - used by regiosn to add.
37329      * @return {Roo.Element} 
37330      */
37331     getEl : function(){
37332         return this.wrapEl || this.el;
37333     },
37334     
37335    
37336     
37337     adjustForComponents : function(width, height)
37338     {
37339         //Roo.log('adjustForComponents ');
37340         if(this.resizeEl != this.el){
37341             width -= this.el.getFrameWidth('lr');
37342             height -= this.el.getFrameWidth('tb');
37343         }
37344         if(this.toolbar){
37345             var te = this.toolbar.getEl();
37346             te.setWidth(width);
37347             height -= te.getHeight();
37348         }
37349         if(this.footer){
37350             var te = this.footer.getEl();
37351             te.setWidth(width);
37352             height -= te.getHeight();
37353         }
37354         
37355         
37356         if(this.adjustments){
37357             width += this.adjustments[0];
37358             height += this.adjustments[1];
37359         }
37360         return {"width": width, "height": height};
37361     },
37362     
37363     setSize : function(width, height){
37364         if(this.fitToFrame && !this.ignoreResize(width, height)){
37365             if(this.fitContainer && this.resizeEl != this.el){
37366                 this.el.setSize(width, height);
37367             }
37368             var size = this.adjustForComponents(width, height);
37369             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37370             this.fireEvent('resize', this, size.width, size.height);
37371         }
37372     },
37373     
37374     /**
37375      * Returns this panel's title
37376      * @return {String} 
37377      */
37378     getTitle : function(){
37379         
37380         if (typeof(this.title) != 'object') {
37381             return this.title;
37382         }
37383         
37384         var t = '';
37385         for (var k in this.title) {
37386             if (!this.title.hasOwnProperty(k)) {
37387                 continue;
37388             }
37389             
37390             if (k.indexOf('-') >= 0) {
37391                 var s = k.split('-');
37392                 for (var i = 0; i<s.length; i++) {
37393                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37394                 }
37395             } else {
37396                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37397             }
37398         }
37399         return t;
37400     },
37401     
37402     /**
37403      * Set this panel's title
37404      * @param {String} title
37405      */
37406     setTitle : function(title){
37407         this.title = title;
37408         if(this.region){
37409             this.region.updatePanelTitle(this, title);
37410         }
37411     },
37412     
37413     /**
37414      * Returns true is this panel was configured to be closable
37415      * @return {Boolean} 
37416      */
37417     isClosable : function(){
37418         return this.closable;
37419     },
37420     
37421     beforeSlide : function(){
37422         this.el.clip();
37423         this.resizeEl.clip();
37424     },
37425     
37426     afterSlide : function(){
37427         this.el.unclip();
37428         this.resizeEl.unclip();
37429     },
37430     
37431     /**
37432      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37433      *   Will fail silently if the {@link #setUrl} method has not been called.
37434      *   This does not activate the panel, just updates its content.
37435      */
37436     refresh : function(){
37437         if(this.refreshDelegate){
37438            this.loaded = false;
37439            this.refreshDelegate();
37440         }
37441     },
37442     
37443     /**
37444      * Destroys this panel
37445      */
37446     destroy : function(){
37447         this.el.removeAllListeners();
37448         var tempEl = document.createElement("span");
37449         tempEl.appendChild(this.el.dom);
37450         tempEl.innerHTML = "";
37451         this.el.remove();
37452         this.el = null;
37453     },
37454     
37455     /**
37456      * form - if the content panel contains a form - this is a reference to it.
37457      * @type {Roo.form.Form}
37458      */
37459     form : false,
37460     /**
37461      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37462      *    This contains a reference to it.
37463      * @type {Roo.View}
37464      */
37465     view : false,
37466     
37467       /**
37468      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37469      * <pre><code>
37470
37471 layout.addxtype({
37472        xtype : 'Form',
37473        items: [ .... ]
37474    }
37475 );
37476
37477 </code></pre>
37478      * @param {Object} cfg Xtype definition of item to add.
37479      */
37480     
37481     
37482     getChildContainer: function () {
37483         return this.getEl();
37484     }
37485     
37486     
37487     /*
37488         var  ret = new Roo.factory(cfg);
37489         return ret;
37490         
37491         
37492         // add form..
37493         if (cfg.xtype.match(/^Form$/)) {
37494             
37495             var el;
37496             //if (this.footer) {
37497             //    el = this.footer.container.insertSibling(false, 'before');
37498             //} else {
37499                 el = this.el.createChild();
37500             //}
37501
37502             this.form = new  Roo.form.Form(cfg);
37503             
37504             
37505             if ( this.form.allItems.length) {
37506                 this.form.render(el.dom);
37507             }
37508             return this.form;
37509         }
37510         // should only have one of theses..
37511         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37512             // views.. should not be just added - used named prop 'view''
37513             
37514             cfg.el = this.el.appendChild(document.createElement("div"));
37515             // factory?
37516             
37517             var ret = new Roo.factory(cfg);
37518              
37519              ret.render && ret.render(false, ''); // render blank..
37520             this.view = ret;
37521             return ret;
37522         }
37523         return false;
37524     }
37525     \*/
37526 });
37527  
37528 /**
37529  * @class Roo.bootstrap.panel.Grid
37530  * @extends Roo.bootstrap.panel.Content
37531  * @constructor
37532  * Create a new GridPanel.
37533  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37534  * @param {Object} config A the config object
37535   
37536  */
37537
37538
37539
37540 Roo.bootstrap.panel.Grid = function(config)
37541 {
37542     
37543       
37544     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37545         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37546
37547     config.el = this.wrapper;
37548     //this.el = this.wrapper;
37549     
37550       if (config.container) {
37551         // ctor'ed from a Border/panel.grid
37552         
37553         
37554         this.wrapper.setStyle("overflow", "hidden");
37555         this.wrapper.addClass('roo-grid-container');
37556
37557     }
37558     
37559     
37560     if(config.toolbar){
37561         var tool_el = this.wrapper.createChild();    
37562         this.toolbar = Roo.factory(config.toolbar);
37563         var ti = [];
37564         if (config.toolbar.items) {
37565             ti = config.toolbar.items ;
37566             delete config.toolbar.items ;
37567         }
37568         
37569         var nitems = [];
37570         this.toolbar.render(tool_el);
37571         for(var i =0;i < ti.length;i++) {
37572           //  Roo.log(['add child', items[i]]);
37573             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37574         }
37575         this.toolbar.items = nitems;
37576         
37577         delete config.toolbar;
37578     }
37579     
37580     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37581     config.grid.scrollBody = true;;
37582     config.grid.monitorWindowResize = false; // turn off autosizing
37583     config.grid.autoHeight = false;
37584     config.grid.autoWidth = false;
37585     
37586     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37587     
37588     if (config.background) {
37589         // render grid on panel activation (if panel background)
37590         this.on('activate', function(gp) {
37591             if (!gp.grid.rendered) {
37592                 gp.grid.render(this.wrapper);
37593                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37594             }
37595         });
37596             
37597     } else {
37598         this.grid.render(this.wrapper);
37599         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37600
37601     }
37602     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37603     // ??? needed ??? config.el = this.wrapper;
37604     
37605     
37606     
37607   
37608     // xtype created footer. - not sure if will work as we normally have to render first..
37609     if (this.footer && !this.footer.el && this.footer.xtype) {
37610         
37611         var ctr = this.grid.getView().getFooterPanel(true);
37612         this.footer.dataSource = this.grid.dataSource;
37613         this.footer = Roo.factory(this.footer, Roo);
37614         this.footer.render(ctr);
37615         
37616     }
37617     
37618     
37619     
37620     
37621      
37622 };
37623
37624 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37625     getId : function(){
37626         return this.grid.id;
37627     },
37628     
37629     /**
37630      * Returns the grid for this panel
37631      * @return {Roo.bootstrap.Table} 
37632      */
37633     getGrid : function(){
37634         return this.grid;    
37635     },
37636     
37637     setSize : function(width, height){
37638         if(!this.ignoreResize(width, height)){
37639             var grid = this.grid;
37640             var size = this.adjustForComponents(width, height);
37641             var gridel = grid.getGridEl();
37642             gridel.setSize(size.width, size.height);
37643             /*
37644             var thd = grid.getGridEl().select('thead',true).first();
37645             var tbd = grid.getGridEl().select('tbody', true).first();
37646             if (tbd) {
37647                 tbd.setSize(width, height - thd.getHeight());
37648             }
37649             */
37650             grid.autoSize();
37651         }
37652     },
37653      
37654     
37655     
37656     beforeSlide : function(){
37657         this.grid.getView().scroller.clip();
37658     },
37659     
37660     afterSlide : function(){
37661         this.grid.getView().scroller.unclip();
37662     },
37663     
37664     destroy : function(){
37665         this.grid.destroy();
37666         delete this.grid;
37667         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37668     }
37669 });
37670
37671 /**
37672  * @class Roo.bootstrap.panel.Nest
37673  * @extends Roo.bootstrap.panel.Content
37674  * @constructor
37675  * Create a new Panel, that can contain a layout.Border.
37676  * 
37677  * 
37678  * @param {Roo.BorderLayout} layout The layout for this panel
37679  * @param {String/Object} config A string to set only the title or a config object
37680  */
37681 Roo.bootstrap.panel.Nest = function(config)
37682 {
37683     // construct with only one argument..
37684     /* FIXME - implement nicer consturctors
37685     if (layout.layout) {
37686         config = layout;
37687         layout = config.layout;
37688         delete config.layout;
37689     }
37690     if (layout.xtype && !layout.getEl) {
37691         // then layout needs constructing..
37692         layout = Roo.factory(layout, Roo);
37693     }
37694     */
37695     
37696     config.el =  config.layout.getEl();
37697     
37698     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37699     
37700     config.layout.monitorWindowResize = false; // turn off autosizing
37701     this.layout = config.layout;
37702     this.layout.getEl().addClass("roo-layout-nested-layout");
37703     
37704     
37705     
37706     
37707 };
37708
37709 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37710
37711     setSize : function(width, height){
37712         if(!this.ignoreResize(width, height)){
37713             var size = this.adjustForComponents(width, height);
37714             var el = this.layout.getEl();
37715             if (size.height < 1) {
37716                 el.setWidth(size.width);   
37717             } else {
37718                 el.setSize(size.width, size.height);
37719             }
37720             var touch = el.dom.offsetWidth;
37721             this.layout.layout();
37722             // ie requires a double layout on the first pass
37723             if(Roo.isIE && !this.initialized){
37724                 this.initialized = true;
37725                 this.layout.layout();
37726             }
37727         }
37728     },
37729     
37730     // activate all subpanels if not currently active..
37731     
37732     setActiveState : function(active){
37733         this.active = active;
37734         this.setActiveClass(active);
37735         
37736         if(!active){
37737             this.fireEvent("deactivate", this);
37738             return;
37739         }
37740         
37741         this.fireEvent("activate", this);
37742         // not sure if this should happen before or after..
37743         if (!this.layout) {
37744             return; // should not happen..
37745         }
37746         var reg = false;
37747         for (var r in this.layout.regions) {
37748             reg = this.layout.getRegion(r);
37749             if (reg.getActivePanel()) {
37750                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37751                 reg.setActivePanel(reg.getActivePanel());
37752                 continue;
37753             }
37754             if (!reg.panels.length) {
37755                 continue;
37756             }
37757             reg.showPanel(reg.getPanel(0));
37758         }
37759         
37760         
37761         
37762         
37763     },
37764     
37765     /**
37766      * Returns the nested BorderLayout for this panel
37767      * @return {Roo.BorderLayout} 
37768      */
37769     getLayout : function(){
37770         return this.layout;
37771     },
37772     
37773      /**
37774      * Adds a xtype elements to the layout of the nested panel
37775      * <pre><code>
37776
37777 panel.addxtype({
37778        xtype : 'ContentPanel',
37779        region: 'west',
37780        items: [ .... ]
37781    }
37782 );
37783
37784 panel.addxtype({
37785         xtype : 'NestedLayoutPanel',
37786         region: 'west',
37787         layout: {
37788            center: { },
37789            west: { }   
37790         },
37791         items : [ ... list of content panels or nested layout panels.. ]
37792    }
37793 );
37794 </code></pre>
37795      * @param {Object} cfg Xtype definition of item to add.
37796      */
37797     addxtype : function(cfg) {
37798         return this.layout.addxtype(cfg);
37799     
37800     }
37801 });        /*
37802  * Based on:
37803  * Ext JS Library 1.1.1
37804  * Copyright(c) 2006-2007, Ext JS, LLC.
37805  *
37806  * Originally Released Under LGPL - original licence link has changed is not relivant.
37807  *
37808  * Fork - LGPL
37809  * <script type="text/javascript">
37810  */
37811 /**
37812  * @class Roo.TabPanel
37813  * @extends Roo.util.Observable
37814  * A lightweight tab container.
37815  * <br><br>
37816  * Usage:
37817  * <pre><code>
37818 // basic tabs 1, built from existing content
37819 var tabs = new Roo.TabPanel("tabs1");
37820 tabs.addTab("script", "View Script");
37821 tabs.addTab("markup", "View Markup");
37822 tabs.activate("script");
37823
37824 // more advanced tabs, built from javascript
37825 var jtabs = new Roo.TabPanel("jtabs");
37826 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37827
37828 // set up the UpdateManager
37829 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37830 var updater = tab2.getUpdateManager();
37831 updater.setDefaultUrl("ajax1.htm");
37832 tab2.on('activate', updater.refresh, updater, true);
37833
37834 // Use setUrl for Ajax loading
37835 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37836 tab3.setUrl("ajax2.htm", null, true);
37837
37838 // Disabled tab
37839 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37840 tab4.disable();
37841
37842 jtabs.activate("jtabs-1");
37843  * </code></pre>
37844  * @constructor
37845  * Create a new TabPanel.
37846  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37847  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37848  */
37849 Roo.bootstrap.panel.Tabs = function(config){
37850     /**
37851     * The container element for this TabPanel.
37852     * @type Roo.Element
37853     */
37854     this.el = Roo.get(config.el);
37855     delete config.el;
37856     if(config){
37857         if(typeof config == "boolean"){
37858             this.tabPosition = config ? "bottom" : "top";
37859         }else{
37860             Roo.apply(this, config);
37861         }
37862     }
37863     
37864     if(this.tabPosition == "bottom"){
37865         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37866         this.el.addClass("roo-tabs-bottom");
37867     }
37868     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37869     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37870     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37871     if(Roo.isIE){
37872         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37873     }
37874     if(this.tabPosition != "bottom"){
37875         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37876          * @type Roo.Element
37877          */
37878         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37879         this.el.addClass("roo-tabs-top");
37880     }
37881     this.items = [];
37882
37883     this.bodyEl.setStyle("position", "relative");
37884
37885     this.active = null;
37886     this.activateDelegate = this.activate.createDelegate(this);
37887
37888     this.addEvents({
37889         /**
37890          * @event tabchange
37891          * Fires when the active tab changes
37892          * @param {Roo.TabPanel} this
37893          * @param {Roo.TabPanelItem} activePanel The new active tab
37894          */
37895         "tabchange": true,
37896         /**
37897          * @event beforetabchange
37898          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37899          * @param {Roo.TabPanel} this
37900          * @param {Object} e Set cancel to true on this object to cancel the tab change
37901          * @param {Roo.TabPanelItem} tab The tab being changed to
37902          */
37903         "beforetabchange" : true
37904     });
37905
37906     Roo.EventManager.onWindowResize(this.onResize, this);
37907     this.cpad = this.el.getPadding("lr");
37908     this.hiddenCount = 0;
37909
37910
37911     // toolbar on the tabbar support...
37912     if (this.toolbar) {
37913         alert("no toolbar support yet");
37914         this.toolbar  = false;
37915         /*
37916         var tcfg = this.toolbar;
37917         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37918         this.toolbar = new Roo.Toolbar(tcfg);
37919         if (Roo.isSafari) {
37920             var tbl = tcfg.container.child('table', true);
37921             tbl.setAttribute('width', '100%');
37922         }
37923         */
37924         
37925     }
37926    
37927
37928
37929     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37930 };
37931
37932 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37933     /*
37934      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37935      */
37936     tabPosition : "top",
37937     /*
37938      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37939      */
37940     currentTabWidth : 0,
37941     /*
37942      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37943      */
37944     minTabWidth : 40,
37945     /*
37946      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37947      */
37948     maxTabWidth : 250,
37949     /*
37950      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37951      */
37952     preferredTabWidth : 175,
37953     /*
37954      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37955      */
37956     resizeTabs : false,
37957     /*
37958      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37959      */
37960     monitorResize : true,
37961     /*
37962      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37963      */
37964     toolbar : false,
37965
37966     /**
37967      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37968      * @param {String} id The id of the div to use <b>or create</b>
37969      * @param {String} text The text for the tab
37970      * @param {String} content (optional) Content to put in the TabPanelItem body
37971      * @param {Boolean} closable (optional) True to create a close icon on the tab
37972      * @return {Roo.TabPanelItem} The created TabPanelItem
37973      */
37974     addTab : function(id, text, content, closable, tpl)
37975     {
37976         var item = new Roo.bootstrap.panel.TabItem({
37977             panel: this,
37978             id : id,
37979             text : text,
37980             closable : closable,
37981             tpl : tpl
37982         });
37983         this.addTabItem(item);
37984         if(content){
37985             item.setContent(content);
37986         }
37987         return item;
37988     },
37989
37990     /**
37991      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37992      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37993      * @return {Roo.TabPanelItem}
37994      */
37995     getTab : function(id){
37996         return this.items[id];
37997     },
37998
37999     /**
38000      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38001      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38002      */
38003     hideTab : function(id){
38004         var t = this.items[id];
38005         if(!t.isHidden()){
38006            t.setHidden(true);
38007            this.hiddenCount++;
38008            this.autoSizeTabs();
38009         }
38010     },
38011
38012     /**
38013      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38014      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38015      */
38016     unhideTab : function(id){
38017         var t = this.items[id];
38018         if(t.isHidden()){
38019            t.setHidden(false);
38020            this.hiddenCount--;
38021            this.autoSizeTabs();
38022         }
38023     },
38024
38025     /**
38026      * Adds an existing {@link Roo.TabPanelItem}.
38027      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38028      */
38029     addTabItem : function(item){
38030         this.items[item.id] = item;
38031         this.items.push(item);
38032       //  if(this.resizeTabs){
38033     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38034   //         this.autoSizeTabs();
38035 //        }else{
38036 //            item.autoSize();
38037        // }
38038     },
38039
38040     /**
38041      * Removes a {@link Roo.TabPanelItem}.
38042      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38043      */
38044     removeTab : function(id){
38045         var items = this.items;
38046         var tab = items[id];
38047         if(!tab) { return; }
38048         var index = items.indexOf(tab);
38049         if(this.active == tab && items.length > 1){
38050             var newTab = this.getNextAvailable(index);
38051             if(newTab) {
38052                 newTab.activate();
38053             }
38054         }
38055         this.stripEl.dom.removeChild(tab.pnode.dom);
38056         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38057             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38058         }
38059         items.splice(index, 1);
38060         delete this.items[tab.id];
38061         tab.fireEvent("close", tab);
38062         tab.purgeListeners();
38063         this.autoSizeTabs();
38064     },
38065
38066     getNextAvailable : function(start){
38067         var items = this.items;
38068         var index = start;
38069         // look for a next tab that will slide over to
38070         // replace the one being removed
38071         while(index < items.length){
38072             var item = items[++index];
38073             if(item && !item.isHidden()){
38074                 return item;
38075             }
38076         }
38077         // if one isn't found select the previous tab (on the left)
38078         index = start;
38079         while(index >= 0){
38080             var item = items[--index];
38081             if(item && !item.isHidden()){
38082                 return item;
38083             }
38084         }
38085         return null;
38086     },
38087
38088     /**
38089      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38090      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38091      */
38092     disableTab : function(id){
38093         var tab = this.items[id];
38094         if(tab && this.active != tab){
38095             tab.disable();
38096         }
38097     },
38098
38099     /**
38100      * Enables a {@link Roo.TabPanelItem} that is disabled.
38101      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38102      */
38103     enableTab : function(id){
38104         var tab = this.items[id];
38105         tab.enable();
38106     },
38107
38108     /**
38109      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38110      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38111      * @return {Roo.TabPanelItem} The TabPanelItem.
38112      */
38113     activate : function(id){
38114         var tab = this.items[id];
38115         if(!tab){
38116             return null;
38117         }
38118         if(tab == this.active || tab.disabled){
38119             return tab;
38120         }
38121         var e = {};
38122         this.fireEvent("beforetabchange", this, e, tab);
38123         if(e.cancel !== true && !tab.disabled){
38124             if(this.active){
38125                 this.active.hide();
38126             }
38127             this.active = this.items[id];
38128             this.active.show();
38129             this.fireEvent("tabchange", this, this.active);
38130         }
38131         return tab;
38132     },
38133
38134     /**
38135      * Gets the active {@link Roo.TabPanelItem}.
38136      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38137      */
38138     getActiveTab : function(){
38139         return this.active;
38140     },
38141
38142     /**
38143      * Updates the tab body element to fit the height of the container element
38144      * for overflow scrolling
38145      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38146      */
38147     syncHeight : function(targetHeight){
38148         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38149         var bm = this.bodyEl.getMargins();
38150         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38151         this.bodyEl.setHeight(newHeight);
38152         return newHeight;
38153     },
38154
38155     onResize : function(){
38156         if(this.monitorResize){
38157             this.autoSizeTabs();
38158         }
38159     },
38160
38161     /**
38162      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38163      */
38164     beginUpdate : function(){
38165         this.updating = true;
38166     },
38167
38168     /**
38169      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38170      */
38171     endUpdate : function(){
38172         this.updating = false;
38173         this.autoSizeTabs();
38174     },
38175
38176     /**
38177      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38178      */
38179     autoSizeTabs : function(){
38180         var count = this.items.length;
38181         var vcount = count - this.hiddenCount;
38182         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38183             return;
38184         }
38185         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38186         var availWidth = Math.floor(w / vcount);
38187         var b = this.stripBody;
38188         if(b.getWidth() > w){
38189             var tabs = this.items;
38190             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38191             if(availWidth < this.minTabWidth){
38192                 /*if(!this.sleft){    // incomplete scrolling code
38193                     this.createScrollButtons();
38194                 }
38195                 this.showScroll();
38196                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38197             }
38198         }else{
38199             if(this.currentTabWidth < this.preferredTabWidth){
38200                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38201             }
38202         }
38203     },
38204
38205     /**
38206      * Returns the number of tabs in this TabPanel.
38207      * @return {Number}
38208      */
38209      getCount : function(){
38210          return this.items.length;
38211      },
38212
38213     /**
38214      * Resizes all the tabs to the passed width
38215      * @param {Number} The new width
38216      */
38217     setTabWidth : function(width){
38218         this.currentTabWidth = width;
38219         for(var i = 0, len = this.items.length; i < len; i++) {
38220                 if(!this.items[i].isHidden()) {
38221                 this.items[i].setWidth(width);
38222             }
38223         }
38224     },
38225
38226     /**
38227      * Destroys this TabPanel
38228      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38229      */
38230     destroy : function(removeEl){
38231         Roo.EventManager.removeResizeListener(this.onResize, this);
38232         for(var i = 0, len = this.items.length; i < len; i++){
38233             this.items[i].purgeListeners();
38234         }
38235         if(removeEl === true){
38236             this.el.update("");
38237             this.el.remove();
38238         }
38239     },
38240     
38241     createStrip : function(container)
38242     {
38243         var strip = document.createElement("nav");
38244         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38245         container.appendChild(strip);
38246         return strip;
38247     },
38248     
38249     createStripList : function(strip)
38250     {
38251         // div wrapper for retard IE
38252         // returns the "tr" element.
38253         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38254         //'<div class="x-tabs-strip-wrap">'+
38255           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38256           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38257         return strip.firstChild; //.firstChild.firstChild.firstChild;
38258     },
38259     createBody : function(container)
38260     {
38261         var body = document.createElement("div");
38262         Roo.id(body, "tab-body");
38263         //Roo.fly(body).addClass("x-tabs-body");
38264         Roo.fly(body).addClass("tab-content");
38265         container.appendChild(body);
38266         return body;
38267     },
38268     createItemBody :function(bodyEl, id){
38269         var body = Roo.getDom(id);
38270         if(!body){
38271             body = document.createElement("div");
38272             body.id = id;
38273         }
38274         //Roo.fly(body).addClass("x-tabs-item-body");
38275         Roo.fly(body).addClass("tab-pane");
38276          bodyEl.insertBefore(body, bodyEl.firstChild);
38277         return body;
38278     },
38279     /** @private */
38280     createStripElements :  function(stripEl, text, closable, tpl)
38281     {
38282         var td = document.createElement("li"); // was td..
38283         
38284         
38285         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38286         
38287         
38288         stripEl.appendChild(td);
38289         /*if(closable){
38290             td.className = "x-tabs-closable";
38291             if(!this.closeTpl){
38292                 this.closeTpl = new Roo.Template(
38293                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38294                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38295                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38296                 );
38297             }
38298             var el = this.closeTpl.overwrite(td, {"text": text});
38299             var close = el.getElementsByTagName("div")[0];
38300             var inner = el.getElementsByTagName("em")[0];
38301             return {"el": el, "close": close, "inner": inner};
38302         } else {
38303         */
38304         // not sure what this is..
38305 //            if(!this.tabTpl){
38306                 //this.tabTpl = new Roo.Template(
38307                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38308                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38309                 //);
38310 //                this.tabTpl = new Roo.Template(
38311 //                   '<a href="#">' +
38312 //                   '<span unselectable="on"' +
38313 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38314 //                            ' >{text}</span></a>'
38315 //                );
38316 //                
38317 //            }
38318
38319
38320             var template = tpl || this.tabTpl || false;
38321             
38322             if(!template){
38323                 
38324                 template = new Roo.Template(
38325                    '<a href="#">' +
38326                    '<span unselectable="on"' +
38327                             (this.disableTooltips ? '' : ' title="{text}"') +
38328                             ' >{text}</span></a>'
38329                 );
38330             }
38331             
38332             switch (typeof(template)) {
38333                 case 'object' :
38334                     break;
38335                 case 'string' :
38336                     template = new Roo.Template(template);
38337                     break;
38338                 default :
38339                     break;
38340             }
38341             
38342             var el = template.overwrite(td, {"text": text});
38343             
38344             var inner = el.getElementsByTagName("span")[0];
38345             
38346             return {"el": el, "inner": inner};
38347             
38348     }
38349         
38350     
38351 });
38352
38353 /**
38354  * @class Roo.TabPanelItem
38355  * @extends Roo.util.Observable
38356  * Represents an individual item (tab plus body) in a TabPanel.
38357  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38358  * @param {String} id The id of this TabPanelItem
38359  * @param {String} text The text for the tab of this TabPanelItem
38360  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38361  */
38362 Roo.bootstrap.panel.TabItem = function(config){
38363     /**
38364      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38365      * @type Roo.TabPanel
38366      */
38367     this.tabPanel = config.panel;
38368     /**
38369      * The id for this TabPanelItem
38370      * @type String
38371      */
38372     this.id = config.id;
38373     /** @private */
38374     this.disabled = false;
38375     /** @private */
38376     this.text = config.text;
38377     /** @private */
38378     this.loaded = false;
38379     this.closable = config.closable;
38380
38381     /**
38382      * The body element for this TabPanelItem.
38383      * @type Roo.Element
38384      */
38385     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38386     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38387     this.bodyEl.setStyle("display", "block");
38388     this.bodyEl.setStyle("zoom", "1");
38389     //this.hideAction();
38390
38391     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38392     /** @private */
38393     this.el = Roo.get(els.el);
38394     this.inner = Roo.get(els.inner, true);
38395     this.textEl = Roo.get(this.el.dom.firstChild, true);
38396     this.pnode = Roo.get(els.el.parentNode, true);
38397 //    this.el.on("mousedown", this.onTabMouseDown, this);
38398     this.el.on("click", this.onTabClick, this);
38399     /** @private */
38400     if(config.closable){
38401         var c = Roo.get(els.close, true);
38402         c.dom.title = this.closeText;
38403         c.addClassOnOver("close-over");
38404         c.on("click", this.closeClick, this);
38405      }
38406
38407     this.addEvents({
38408          /**
38409          * @event activate
38410          * Fires when this tab becomes the active tab.
38411          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38412          * @param {Roo.TabPanelItem} this
38413          */
38414         "activate": true,
38415         /**
38416          * @event beforeclose
38417          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38418          * @param {Roo.TabPanelItem} this
38419          * @param {Object} e Set cancel to true on this object to cancel the close.
38420          */
38421         "beforeclose": true,
38422         /**
38423          * @event close
38424          * Fires when this tab is closed.
38425          * @param {Roo.TabPanelItem} this
38426          */
38427          "close": true,
38428         /**
38429          * @event deactivate
38430          * Fires when this tab is no longer the active tab.
38431          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38432          * @param {Roo.TabPanelItem} this
38433          */
38434          "deactivate" : true
38435     });
38436     this.hidden = false;
38437
38438     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38439 };
38440
38441 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38442            {
38443     purgeListeners : function(){
38444        Roo.util.Observable.prototype.purgeListeners.call(this);
38445        this.el.removeAllListeners();
38446     },
38447     /**
38448      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38449      */
38450     show : function(){
38451         this.pnode.addClass("active");
38452         this.showAction();
38453         if(Roo.isOpera){
38454             this.tabPanel.stripWrap.repaint();
38455         }
38456         this.fireEvent("activate", this.tabPanel, this);
38457     },
38458
38459     /**
38460      * Returns true if this tab is the active tab.
38461      * @return {Boolean}
38462      */
38463     isActive : function(){
38464         return this.tabPanel.getActiveTab() == this;
38465     },
38466
38467     /**
38468      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38469      */
38470     hide : function(){
38471         this.pnode.removeClass("active");
38472         this.hideAction();
38473         this.fireEvent("deactivate", this.tabPanel, this);
38474     },
38475
38476     hideAction : function(){
38477         this.bodyEl.hide();
38478         this.bodyEl.setStyle("position", "absolute");
38479         this.bodyEl.setLeft("-20000px");
38480         this.bodyEl.setTop("-20000px");
38481     },
38482
38483     showAction : function(){
38484         this.bodyEl.setStyle("position", "relative");
38485         this.bodyEl.setTop("");
38486         this.bodyEl.setLeft("");
38487         this.bodyEl.show();
38488     },
38489
38490     /**
38491      * Set the tooltip for the tab.
38492      * @param {String} tooltip The tab's tooltip
38493      */
38494     setTooltip : function(text){
38495         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38496             this.textEl.dom.qtip = text;
38497             this.textEl.dom.removeAttribute('title');
38498         }else{
38499             this.textEl.dom.title = text;
38500         }
38501     },
38502
38503     onTabClick : function(e){
38504         e.preventDefault();
38505         this.tabPanel.activate(this.id);
38506     },
38507
38508     onTabMouseDown : function(e){
38509         e.preventDefault();
38510         this.tabPanel.activate(this.id);
38511     },
38512 /*
38513     getWidth : function(){
38514         return this.inner.getWidth();
38515     },
38516
38517     setWidth : function(width){
38518         var iwidth = width - this.pnode.getPadding("lr");
38519         this.inner.setWidth(iwidth);
38520         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38521         this.pnode.setWidth(width);
38522     },
38523 */
38524     /**
38525      * Show or hide the tab
38526      * @param {Boolean} hidden True to hide or false to show.
38527      */
38528     setHidden : function(hidden){
38529         this.hidden = hidden;
38530         this.pnode.setStyle("display", hidden ? "none" : "");
38531     },
38532
38533     /**
38534      * Returns true if this tab is "hidden"
38535      * @return {Boolean}
38536      */
38537     isHidden : function(){
38538         return this.hidden;
38539     },
38540
38541     /**
38542      * Returns the text for this tab
38543      * @return {String}
38544      */
38545     getText : function(){
38546         return this.text;
38547     },
38548     /*
38549     autoSize : function(){
38550         //this.el.beginMeasure();
38551         this.textEl.setWidth(1);
38552         /*
38553          *  #2804 [new] Tabs in Roojs
38554          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38555          */
38556         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38557         //this.el.endMeasure();
38558     //},
38559
38560     /**
38561      * Sets the text for the tab (Note: this also sets the tooltip text)
38562      * @param {String} text The tab's text and tooltip
38563      */
38564     setText : function(text){
38565         this.text = text;
38566         this.textEl.update(text);
38567         this.setTooltip(text);
38568         //if(!this.tabPanel.resizeTabs){
38569         //    this.autoSize();
38570         //}
38571     },
38572     /**
38573      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38574      */
38575     activate : function(){
38576         this.tabPanel.activate(this.id);
38577     },
38578
38579     /**
38580      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38581      */
38582     disable : function(){
38583         if(this.tabPanel.active != this){
38584             this.disabled = true;
38585             this.pnode.addClass("disabled");
38586         }
38587     },
38588
38589     /**
38590      * Enables this TabPanelItem if it was previously disabled.
38591      */
38592     enable : function(){
38593         this.disabled = false;
38594         this.pnode.removeClass("disabled");
38595     },
38596
38597     /**
38598      * Sets the content for this TabPanelItem.
38599      * @param {String} content The content
38600      * @param {Boolean} loadScripts true to look for and load scripts
38601      */
38602     setContent : function(content, loadScripts){
38603         this.bodyEl.update(content, loadScripts);
38604     },
38605
38606     /**
38607      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38608      * @return {Roo.UpdateManager} The UpdateManager
38609      */
38610     getUpdateManager : function(){
38611         return this.bodyEl.getUpdateManager();
38612     },
38613
38614     /**
38615      * Set a URL to be used to load the content for this TabPanelItem.
38616      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38617      * @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)
38618      * @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)
38619      * @return {Roo.UpdateManager} The UpdateManager
38620      */
38621     setUrl : function(url, params, loadOnce){
38622         if(this.refreshDelegate){
38623             this.un('activate', this.refreshDelegate);
38624         }
38625         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38626         this.on("activate", this.refreshDelegate);
38627         return this.bodyEl.getUpdateManager();
38628     },
38629
38630     /** @private */
38631     _handleRefresh : function(url, params, loadOnce){
38632         if(!loadOnce || !this.loaded){
38633             var updater = this.bodyEl.getUpdateManager();
38634             updater.update(url, params, this._setLoaded.createDelegate(this));
38635         }
38636     },
38637
38638     /**
38639      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38640      *   Will fail silently if the setUrl method has not been called.
38641      *   This does not activate the panel, just updates its content.
38642      */
38643     refresh : function(){
38644         if(this.refreshDelegate){
38645            this.loaded = false;
38646            this.refreshDelegate();
38647         }
38648     },
38649
38650     /** @private */
38651     _setLoaded : function(){
38652         this.loaded = true;
38653     },
38654
38655     /** @private */
38656     closeClick : function(e){
38657         var o = {};
38658         e.stopEvent();
38659         this.fireEvent("beforeclose", this, o);
38660         if(o.cancel !== true){
38661             this.tabPanel.removeTab(this.id);
38662         }
38663     },
38664     /**
38665      * The text displayed in the tooltip for the close icon.
38666      * @type String
38667      */
38668     closeText : "Close this tab"
38669 });
38670 /**
38671 *    This script refer to:
38672 *    Title: International Telephone Input
38673 *    Author: Jack O'Connor
38674 *    Code version:  v12.1.12
38675 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38676 **/
38677
38678 Roo.bootstrap.PhoneInputData = function() {
38679     var d = [
38680       [
38681         "Afghanistan (‫افغانستان‬‎)",
38682         "af",
38683         "93"
38684       ],
38685       [
38686         "Albania (Shqipëri)",
38687         "al",
38688         "355"
38689       ],
38690       [
38691         "Algeria (‫الجزائر‬‎)",
38692         "dz",
38693         "213"
38694       ],
38695       [
38696         "American Samoa",
38697         "as",
38698         "1684"
38699       ],
38700       [
38701         "Andorra",
38702         "ad",
38703         "376"
38704       ],
38705       [
38706         "Angola",
38707         "ao",
38708         "244"
38709       ],
38710       [
38711         "Anguilla",
38712         "ai",
38713         "1264"
38714       ],
38715       [
38716         "Antigua and Barbuda",
38717         "ag",
38718         "1268"
38719       ],
38720       [
38721         "Argentina",
38722         "ar",
38723         "54"
38724       ],
38725       [
38726         "Armenia (Հայաստան)",
38727         "am",
38728         "374"
38729       ],
38730       [
38731         "Aruba",
38732         "aw",
38733         "297"
38734       ],
38735       [
38736         "Australia",
38737         "au",
38738         "61",
38739         0
38740       ],
38741       [
38742         "Austria (Österreich)",
38743         "at",
38744         "43"
38745       ],
38746       [
38747         "Azerbaijan (Azərbaycan)",
38748         "az",
38749         "994"
38750       ],
38751       [
38752         "Bahamas",
38753         "bs",
38754         "1242"
38755       ],
38756       [
38757         "Bahrain (‫البحرين‬‎)",
38758         "bh",
38759         "973"
38760       ],
38761       [
38762         "Bangladesh (বাংলাদেশ)",
38763         "bd",
38764         "880"
38765       ],
38766       [
38767         "Barbados",
38768         "bb",
38769         "1246"
38770       ],
38771       [
38772         "Belarus (Беларусь)",
38773         "by",
38774         "375"
38775       ],
38776       [
38777         "Belgium (België)",
38778         "be",
38779         "32"
38780       ],
38781       [
38782         "Belize",
38783         "bz",
38784         "501"
38785       ],
38786       [
38787         "Benin (Bénin)",
38788         "bj",
38789         "229"
38790       ],
38791       [
38792         "Bermuda",
38793         "bm",
38794         "1441"
38795       ],
38796       [
38797         "Bhutan (འབྲུག)",
38798         "bt",
38799         "975"
38800       ],
38801       [
38802         "Bolivia",
38803         "bo",
38804         "591"
38805       ],
38806       [
38807         "Bosnia and Herzegovina (Босна и Херцеговина)",
38808         "ba",
38809         "387"
38810       ],
38811       [
38812         "Botswana",
38813         "bw",
38814         "267"
38815       ],
38816       [
38817         "Brazil (Brasil)",
38818         "br",
38819         "55"
38820       ],
38821       [
38822         "British Indian Ocean Territory",
38823         "io",
38824         "246"
38825       ],
38826       [
38827         "British Virgin Islands",
38828         "vg",
38829         "1284"
38830       ],
38831       [
38832         "Brunei",
38833         "bn",
38834         "673"
38835       ],
38836       [
38837         "Bulgaria (България)",
38838         "bg",
38839         "359"
38840       ],
38841       [
38842         "Burkina Faso",
38843         "bf",
38844         "226"
38845       ],
38846       [
38847         "Burundi (Uburundi)",
38848         "bi",
38849         "257"
38850       ],
38851       [
38852         "Cambodia (កម្ពុជា)",
38853         "kh",
38854         "855"
38855       ],
38856       [
38857         "Cameroon (Cameroun)",
38858         "cm",
38859         "237"
38860       ],
38861       [
38862         "Canada",
38863         "ca",
38864         "1",
38865         1,
38866         ["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"]
38867       ],
38868       [
38869         "Cape Verde (Kabu Verdi)",
38870         "cv",
38871         "238"
38872       ],
38873       [
38874         "Caribbean Netherlands",
38875         "bq",
38876         "599",
38877         1
38878       ],
38879       [
38880         "Cayman Islands",
38881         "ky",
38882         "1345"
38883       ],
38884       [
38885         "Central African Republic (République centrafricaine)",
38886         "cf",
38887         "236"
38888       ],
38889       [
38890         "Chad (Tchad)",
38891         "td",
38892         "235"
38893       ],
38894       [
38895         "Chile",
38896         "cl",
38897         "56"
38898       ],
38899       [
38900         "China (中国)",
38901         "cn",
38902         "86"
38903       ],
38904       [
38905         "Christmas Island",
38906         "cx",
38907         "61",
38908         2
38909       ],
38910       [
38911         "Cocos (Keeling) Islands",
38912         "cc",
38913         "61",
38914         1
38915       ],
38916       [
38917         "Colombia",
38918         "co",
38919         "57"
38920       ],
38921       [
38922         "Comoros (‫جزر القمر‬‎)",
38923         "km",
38924         "269"
38925       ],
38926       [
38927         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38928         "cd",
38929         "243"
38930       ],
38931       [
38932         "Congo (Republic) (Congo-Brazzaville)",
38933         "cg",
38934         "242"
38935       ],
38936       [
38937         "Cook Islands",
38938         "ck",
38939         "682"
38940       ],
38941       [
38942         "Costa Rica",
38943         "cr",
38944         "506"
38945       ],
38946       [
38947         "Côte d’Ivoire",
38948         "ci",
38949         "225"
38950       ],
38951       [
38952         "Croatia (Hrvatska)",
38953         "hr",
38954         "385"
38955       ],
38956       [
38957         "Cuba",
38958         "cu",
38959         "53"
38960       ],
38961       [
38962         "Curaçao",
38963         "cw",
38964         "599",
38965         0
38966       ],
38967       [
38968         "Cyprus (Κύπρος)",
38969         "cy",
38970         "357"
38971       ],
38972       [
38973         "Czech Republic (Česká republika)",
38974         "cz",
38975         "420"
38976       ],
38977       [
38978         "Denmark (Danmark)",
38979         "dk",
38980         "45"
38981       ],
38982       [
38983         "Djibouti",
38984         "dj",
38985         "253"
38986       ],
38987       [
38988         "Dominica",
38989         "dm",
38990         "1767"
38991       ],
38992       [
38993         "Dominican Republic (República Dominicana)",
38994         "do",
38995         "1",
38996         2,
38997         ["809", "829", "849"]
38998       ],
38999       [
39000         "Ecuador",
39001         "ec",
39002         "593"
39003       ],
39004       [
39005         "Egypt (‫مصر‬‎)",
39006         "eg",
39007         "20"
39008       ],
39009       [
39010         "El Salvador",
39011         "sv",
39012         "503"
39013       ],
39014       [
39015         "Equatorial Guinea (Guinea Ecuatorial)",
39016         "gq",
39017         "240"
39018       ],
39019       [
39020         "Eritrea",
39021         "er",
39022         "291"
39023       ],
39024       [
39025         "Estonia (Eesti)",
39026         "ee",
39027         "372"
39028       ],
39029       [
39030         "Ethiopia",
39031         "et",
39032         "251"
39033       ],
39034       [
39035         "Falkland Islands (Islas Malvinas)",
39036         "fk",
39037         "500"
39038       ],
39039       [
39040         "Faroe Islands (Føroyar)",
39041         "fo",
39042         "298"
39043       ],
39044       [
39045         "Fiji",
39046         "fj",
39047         "679"
39048       ],
39049       [
39050         "Finland (Suomi)",
39051         "fi",
39052         "358",
39053         0
39054       ],
39055       [
39056         "France",
39057         "fr",
39058         "33"
39059       ],
39060       [
39061         "French Guiana (Guyane française)",
39062         "gf",
39063         "594"
39064       ],
39065       [
39066         "French Polynesia (Polynésie française)",
39067         "pf",
39068         "689"
39069       ],
39070       [
39071         "Gabon",
39072         "ga",
39073         "241"
39074       ],
39075       [
39076         "Gambia",
39077         "gm",
39078         "220"
39079       ],
39080       [
39081         "Georgia (საქართველო)",
39082         "ge",
39083         "995"
39084       ],
39085       [
39086         "Germany (Deutschland)",
39087         "de",
39088         "49"
39089       ],
39090       [
39091         "Ghana (Gaana)",
39092         "gh",
39093         "233"
39094       ],
39095       [
39096         "Gibraltar",
39097         "gi",
39098         "350"
39099       ],
39100       [
39101         "Greece (Ελλάδα)",
39102         "gr",
39103         "30"
39104       ],
39105       [
39106         "Greenland (Kalaallit Nunaat)",
39107         "gl",
39108         "299"
39109       ],
39110       [
39111         "Grenada",
39112         "gd",
39113         "1473"
39114       ],
39115       [
39116         "Guadeloupe",
39117         "gp",
39118         "590",
39119         0
39120       ],
39121       [
39122         "Guam",
39123         "gu",
39124         "1671"
39125       ],
39126       [
39127         "Guatemala",
39128         "gt",
39129         "502"
39130       ],
39131       [
39132         "Guernsey",
39133         "gg",
39134         "44",
39135         1
39136       ],
39137       [
39138         "Guinea (Guinée)",
39139         "gn",
39140         "224"
39141       ],
39142       [
39143         "Guinea-Bissau (Guiné Bissau)",
39144         "gw",
39145         "245"
39146       ],
39147       [
39148         "Guyana",
39149         "gy",
39150         "592"
39151       ],
39152       [
39153         "Haiti",
39154         "ht",
39155         "509"
39156       ],
39157       [
39158         "Honduras",
39159         "hn",
39160         "504"
39161       ],
39162       [
39163         "Hong Kong (香港)",
39164         "hk",
39165         "852"
39166       ],
39167       [
39168         "Hungary (Magyarország)",
39169         "hu",
39170         "36"
39171       ],
39172       [
39173         "Iceland (Ísland)",
39174         "is",
39175         "354"
39176       ],
39177       [
39178         "India (भारत)",
39179         "in",
39180         "91"
39181       ],
39182       [
39183         "Indonesia",
39184         "id",
39185         "62"
39186       ],
39187       [
39188         "Iran (‫ایران‬‎)",
39189         "ir",
39190         "98"
39191       ],
39192       [
39193         "Iraq (‫العراق‬‎)",
39194         "iq",
39195         "964"
39196       ],
39197       [
39198         "Ireland",
39199         "ie",
39200         "353"
39201       ],
39202       [
39203         "Isle of Man",
39204         "im",
39205         "44",
39206         2
39207       ],
39208       [
39209         "Israel (‫ישראל‬‎)",
39210         "il",
39211         "972"
39212       ],
39213       [
39214         "Italy (Italia)",
39215         "it",
39216         "39",
39217         0
39218       ],
39219       [
39220         "Jamaica",
39221         "jm",
39222         "1876"
39223       ],
39224       [
39225         "Japan (日本)",
39226         "jp",
39227         "81"
39228       ],
39229       [
39230         "Jersey",
39231         "je",
39232         "44",
39233         3
39234       ],
39235       [
39236         "Jordan (‫الأردن‬‎)",
39237         "jo",
39238         "962"
39239       ],
39240       [
39241         "Kazakhstan (Казахстан)",
39242         "kz",
39243         "7",
39244         1
39245       ],
39246       [
39247         "Kenya",
39248         "ke",
39249         "254"
39250       ],
39251       [
39252         "Kiribati",
39253         "ki",
39254         "686"
39255       ],
39256       [
39257         "Kosovo",
39258         "xk",
39259         "383"
39260       ],
39261       [
39262         "Kuwait (‫الكويت‬‎)",
39263         "kw",
39264         "965"
39265       ],
39266       [
39267         "Kyrgyzstan (Кыргызстан)",
39268         "kg",
39269         "996"
39270       ],
39271       [
39272         "Laos (ລາວ)",
39273         "la",
39274         "856"
39275       ],
39276       [
39277         "Latvia (Latvija)",
39278         "lv",
39279         "371"
39280       ],
39281       [
39282         "Lebanon (‫لبنان‬‎)",
39283         "lb",
39284         "961"
39285       ],
39286       [
39287         "Lesotho",
39288         "ls",
39289         "266"
39290       ],
39291       [
39292         "Liberia",
39293         "lr",
39294         "231"
39295       ],
39296       [
39297         "Libya (‫ليبيا‬‎)",
39298         "ly",
39299         "218"
39300       ],
39301       [
39302         "Liechtenstein",
39303         "li",
39304         "423"
39305       ],
39306       [
39307         "Lithuania (Lietuva)",
39308         "lt",
39309         "370"
39310       ],
39311       [
39312         "Luxembourg",
39313         "lu",
39314         "352"
39315       ],
39316       [
39317         "Macau (澳門)",
39318         "mo",
39319         "853"
39320       ],
39321       [
39322         "Macedonia (FYROM) (Македонија)",
39323         "mk",
39324         "389"
39325       ],
39326       [
39327         "Madagascar (Madagasikara)",
39328         "mg",
39329         "261"
39330       ],
39331       [
39332         "Malawi",
39333         "mw",
39334         "265"
39335       ],
39336       [
39337         "Malaysia",
39338         "my",
39339         "60"
39340       ],
39341       [
39342         "Maldives",
39343         "mv",
39344         "960"
39345       ],
39346       [
39347         "Mali",
39348         "ml",
39349         "223"
39350       ],
39351       [
39352         "Malta",
39353         "mt",
39354         "356"
39355       ],
39356       [
39357         "Marshall Islands",
39358         "mh",
39359         "692"
39360       ],
39361       [
39362         "Martinique",
39363         "mq",
39364         "596"
39365       ],
39366       [
39367         "Mauritania (‫موريتانيا‬‎)",
39368         "mr",
39369         "222"
39370       ],
39371       [
39372         "Mauritius (Moris)",
39373         "mu",
39374         "230"
39375       ],
39376       [
39377         "Mayotte",
39378         "yt",
39379         "262",
39380         1
39381       ],
39382       [
39383         "Mexico (México)",
39384         "mx",
39385         "52"
39386       ],
39387       [
39388         "Micronesia",
39389         "fm",
39390         "691"
39391       ],
39392       [
39393         "Moldova (Republica Moldova)",
39394         "md",
39395         "373"
39396       ],
39397       [
39398         "Monaco",
39399         "mc",
39400         "377"
39401       ],
39402       [
39403         "Mongolia (Монгол)",
39404         "mn",
39405         "976"
39406       ],
39407       [
39408         "Montenegro (Crna Gora)",
39409         "me",
39410         "382"
39411       ],
39412       [
39413         "Montserrat",
39414         "ms",
39415         "1664"
39416       ],
39417       [
39418         "Morocco (‫المغرب‬‎)",
39419         "ma",
39420         "212",
39421         0
39422       ],
39423       [
39424         "Mozambique (Moçambique)",
39425         "mz",
39426         "258"
39427       ],
39428       [
39429         "Myanmar (Burma) (မြန်မာ)",
39430         "mm",
39431         "95"
39432       ],
39433       [
39434         "Namibia (Namibië)",
39435         "na",
39436         "264"
39437       ],
39438       [
39439         "Nauru",
39440         "nr",
39441         "674"
39442       ],
39443       [
39444         "Nepal (नेपाल)",
39445         "np",
39446         "977"
39447       ],
39448       [
39449         "Netherlands (Nederland)",
39450         "nl",
39451         "31"
39452       ],
39453       [
39454         "New Caledonia (Nouvelle-Calédonie)",
39455         "nc",
39456         "687"
39457       ],
39458       [
39459         "New Zealand",
39460         "nz",
39461         "64"
39462       ],
39463       [
39464         "Nicaragua",
39465         "ni",
39466         "505"
39467       ],
39468       [
39469         "Niger (Nijar)",
39470         "ne",
39471         "227"
39472       ],
39473       [
39474         "Nigeria",
39475         "ng",
39476         "234"
39477       ],
39478       [
39479         "Niue",
39480         "nu",
39481         "683"
39482       ],
39483       [
39484         "Norfolk Island",
39485         "nf",
39486         "672"
39487       ],
39488       [
39489         "North Korea (조선 민주주의 인민 공화국)",
39490         "kp",
39491         "850"
39492       ],
39493       [
39494         "Northern Mariana Islands",
39495         "mp",
39496         "1670"
39497       ],
39498       [
39499         "Norway (Norge)",
39500         "no",
39501         "47",
39502         0
39503       ],
39504       [
39505         "Oman (‫عُمان‬‎)",
39506         "om",
39507         "968"
39508       ],
39509       [
39510         "Pakistan (‫پاکستان‬‎)",
39511         "pk",
39512         "92"
39513       ],
39514       [
39515         "Palau",
39516         "pw",
39517         "680"
39518       ],
39519       [
39520         "Palestine (‫فلسطين‬‎)",
39521         "ps",
39522         "970"
39523       ],
39524       [
39525         "Panama (Panamá)",
39526         "pa",
39527         "507"
39528       ],
39529       [
39530         "Papua New Guinea",
39531         "pg",
39532         "675"
39533       ],
39534       [
39535         "Paraguay",
39536         "py",
39537         "595"
39538       ],
39539       [
39540         "Peru (Perú)",
39541         "pe",
39542         "51"
39543       ],
39544       [
39545         "Philippines",
39546         "ph",
39547         "63"
39548       ],
39549       [
39550         "Poland (Polska)",
39551         "pl",
39552         "48"
39553       ],
39554       [
39555         "Portugal",
39556         "pt",
39557         "351"
39558       ],
39559       [
39560         "Puerto Rico",
39561         "pr",
39562         "1",
39563         3,
39564         ["787", "939"]
39565       ],
39566       [
39567         "Qatar (‫قطر‬‎)",
39568         "qa",
39569         "974"
39570       ],
39571       [
39572         "Réunion (La Réunion)",
39573         "re",
39574         "262",
39575         0
39576       ],
39577       [
39578         "Romania (România)",
39579         "ro",
39580         "40"
39581       ],
39582       [
39583         "Russia (Россия)",
39584         "ru",
39585         "7",
39586         0
39587       ],
39588       [
39589         "Rwanda",
39590         "rw",
39591         "250"
39592       ],
39593       [
39594         "Saint Barthélemy",
39595         "bl",
39596         "590",
39597         1
39598       ],
39599       [
39600         "Saint Helena",
39601         "sh",
39602         "290"
39603       ],
39604       [
39605         "Saint Kitts and Nevis",
39606         "kn",
39607         "1869"
39608       ],
39609       [
39610         "Saint Lucia",
39611         "lc",
39612         "1758"
39613       ],
39614       [
39615         "Saint Martin (Saint-Martin (partie française))",
39616         "mf",
39617         "590",
39618         2
39619       ],
39620       [
39621         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39622         "pm",
39623         "508"
39624       ],
39625       [
39626         "Saint Vincent and the Grenadines",
39627         "vc",
39628         "1784"
39629       ],
39630       [
39631         "Samoa",
39632         "ws",
39633         "685"
39634       ],
39635       [
39636         "San Marino",
39637         "sm",
39638         "378"
39639       ],
39640       [
39641         "São Tomé and Príncipe (São Tomé e Príncipe)",
39642         "st",
39643         "239"
39644       ],
39645       [
39646         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39647         "sa",
39648         "966"
39649       ],
39650       [
39651         "Senegal (Sénégal)",
39652         "sn",
39653         "221"
39654       ],
39655       [
39656         "Serbia (Србија)",
39657         "rs",
39658         "381"
39659       ],
39660       [
39661         "Seychelles",
39662         "sc",
39663         "248"
39664       ],
39665       [
39666         "Sierra Leone",
39667         "sl",
39668         "232"
39669       ],
39670       [
39671         "Singapore",
39672         "sg",
39673         "65"
39674       ],
39675       [
39676         "Sint Maarten",
39677         "sx",
39678         "1721"
39679       ],
39680       [
39681         "Slovakia (Slovensko)",
39682         "sk",
39683         "421"
39684       ],
39685       [
39686         "Slovenia (Slovenija)",
39687         "si",
39688         "386"
39689       ],
39690       [
39691         "Solomon Islands",
39692         "sb",
39693         "677"
39694       ],
39695       [
39696         "Somalia (Soomaaliya)",
39697         "so",
39698         "252"
39699       ],
39700       [
39701         "South Africa",
39702         "za",
39703         "27"
39704       ],
39705       [
39706         "South Korea (대한민국)",
39707         "kr",
39708         "82"
39709       ],
39710       [
39711         "South Sudan (‫جنوب السودان‬‎)",
39712         "ss",
39713         "211"
39714       ],
39715       [
39716         "Spain (España)",
39717         "es",
39718         "34"
39719       ],
39720       [
39721         "Sri Lanka (ශ්‍රී ලංකාව)",
39722         "lk",
39723         "94"
39724       ],
39725       [
39726         "Sudan (‫السودان‬‎)",
39727         "sd",
39728         "249"
39729       ],
39730       [
39731         "Suriname",
39732         "sr",
39733         "597"
39734       ],
39735       [
39736         "Svalbard and Jan Mayen",
39737         "sj",
39738         "47",
39739         1
39740       ],
39741       [
39742         "Swaziland",
39743         "sz",
39744         "268"
39745       ],
39746       [
39747         "Sweden (Sverige)",
39748         "se",
39749         "46"
39750       ],
39751       [
39752         "Switzerland (Schweiz)",
39753         "ch",
39754         "41"
39755       ],
39756       [
39757         "Syria (‫سوريا‬‎)",
39758         "sy",
39759         "963"
39760       ],
39761       [
39762         "Taiwan (台灣)",
39763         "tw",
39764         "886"
39765       ],
39766       [
39767         "Tajikistan",
39768         "tj",
39769         "992"
39770       ],
39771       [
39772         "Tanzania",
39773         "tz",
39774         "255"
39775       ],
39776       [
39777         "Thailand (ไทย)",
39778         "th",
39779         "66"
39780       ],
39781       [
39782         "Timor-Leste",
39783         "tl",
39784         "670"
39785       ],
39786       [
39787         "Togo",
39788         "tg",
39789         "228"
39790       ],
39791       [
39792         "Tokelau",
39793         "tk",
39794         "690"
39795       ],
39796       [
39797         "Tonga",
39798         "to",
39799         "676"
39800       ],
39801       [
39802         "Trinidad and Tobago",
39803         "tt",
39804         "1868"
39805       ],
39806       [
39807         "Tunisia (‫تونس‬‎)",
39808         "tn",
39809         "216"
39810       ],
39811       [
39812         "Turkey (Türkiye)",
39813         "tr",
39814         "90"
39815       ],
39816       [
39817         "Turkmenistan",
39818         "tm",
39819         "993"
39820       ],
39821       [
39822         "Turks and Caicos Islands",
39823         "tc",
39824         "1649"
39825       ],
39826       [
39827         "Tuvalu",
39828         "tv",
39829         "688"
39830       ],
39831       [
39832         "U.S. Virgin Islands",
39833         "vi",
39834         "1340"
39835       ],
39836       [
39837         "Uganda",
39838         "ug",
39839         "256"
39840       ],
39841       [
39842         "Ukraine (Україна)",
39843         "ua",
39844         "380"
39845       ],
39846       [
39847         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39848         "ae",
39849         "971"
39850       ],
39851       [
39852         "United Kingdom",
39853         "gb",
39854         "44",
39855         0
39856       ],
39857       [
39858         "United States",
39859         "us",
39860         "1",
39861         0
39862       ],
39863       [
39864         "Uruguay",
39865         "uy",
39866         "598"
39867       ],
39868       [
39869         "Uzbekistan (Oʻzbekiston)",
39870         "uz",
39871         "998"
39872       ],
39873       [
39874         "Vanuatu",
39875         "vu",
39876         "678"
39877       ],
39878       [
39879         "Vatican City (Città del Vaticano)",
39880         "va",
39881         "39",
39882         1
39883       ],
39884       [
39885         "Venezuela",
39886         "ve",
39887         "58"
39888       ],
39889       [
39890         "Vietnam (Việt Nam)",
39891         "vn",
39892         "84"
39893       ],
39894       [
39895         "Wallis and Futuna (Wallis-et-Futuna)",
39896         "wf",
39897         "681"
39898       ],
39899       [
39900         "Western Sahara (‫الصحراء الغربية‬‎)",
39901         "eh",
39902         "212",
39903         1
39904       ],
39905       [
39906         "Yemen (‫اليمن‬‎)",
39907         "ye",
39908         "967"
39909       ],
39910       [
39911         "Zambia",
39912         "zm",
39913         "260"
39914       ],
39915       [
39916         "Zimbabwe",
39917         "zw",
39918         "263"
39919       ],
39920       [
39921         "Åland Islands",
39922         "ax",
39923         "358",
39924         1
39925       ]
39926   ];
39927   
39928   return d;
39929 }/**
39930 *    This script refer to:
39931 *    Title: International Telephone Input
39932 *    Author: Jack O'Connor
39933 *    Code version:  v12.1.12
39934 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39935 **/
39936
39937 /**
39938  * @class Roo.bootstrap.PhoneInput
39939  * @extends Roo.bootstrap.TriggerField
39940  * An input with International dial-code selection
39941  
39942  * @cfg {String} defaultDialCode default '+852'
39943  * @cfg {Array} preferedCountries default []
39944   
39945  * @constructor
39946  * Create a new PhoneInput.
39947  * @param {Object} config Configuration options
39948  */
39949
39950 Roo.bootstrap.PhoneInput = function(config) {
39951     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39952 };
39953
39954 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39955         
39956         listWidth: undefined,
39957         
39958         selectedClass: 'active',
39959         
39960         invalidClass : "has-warning",
39961         
39962         validClass: 'has-success',
39963         
39964         allowed: '0123456789',
39965         
39966         max_length: 15,
39967         
39968         /**
39969          * @cfg {String} defaultDialCode The default dial code when initializing the input
39970          */
39971         defaultDialCode: '+852',
39972         
39973         /**
39974          * @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
39975          */
39976         preferedCountries: false,
39977         
39978         getAutoCreate : function()
39979         {
39980             var data = Roo.bootstrap.PhoneInputData();
39981             var align = this.labelAlign || this.parentLabelAlign();
39982             var id = Roo.id();
39983             
39984             this.allCountries = [];
39985             this.dialCodeMapping = [];
39986             
39987             for (var i = 0; i < data.length; i++) {
39988               var c = data[i];
39989               this.allCountries[i] = {
39990                 name: c[0],
39991                 iso2: c[1],
39992                 dialCode: c[2],
39993                 priority: c[3] || 0,
39994                 areaCodes: c[4] || null
39995               };
39996               this.dialCodeMapping[c[2]] = {
39997                   name: c[0],
39998                   iso2: c[1],
39999                   priority: c[3] || 0,
40000                   areaCodes: c[4] || null
40001               };
40002             }
40003             
40004             var cfg = {
40005                 cls: 'form-group',
40006                 cn: []
40007             };
40008             
40009             var input =  {
40010                 tag: 'input',
40011                 id : id,
40012                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40013                 maxlength: this.max_length,
40014                 cls : 'form-control tel-input',
40015                 autocomplete: 'new-password'
40016             };
40017             
40018             var hiddenInput = {
40019                 tag: 'input',
40020                 type: 'hidden',
40021                 cls: 'hidden-tel-input'
40022             };
40023             
40024             if (this.name) {
40025                 hiddenInput.name = this.name;
40026             }
40027             
40028             if (this.disabled) {
40029                 input.disabled = true;
40030             }
40031             
40032             var flag_container = {
40033                 tag: 'div',
40034                 cls: 'flag-box',
40035                 cn: [
40036                     {
40037                         tag: 'div',
40038                         cls: 'flag'
40039                     },
40040                     {
40041                         tag: 'div',
40042                         cls: 'caret'
40043                     }
40044                 ]
40045             };
40046             
40047             var box = {
40048                 tag: 'div',
40049                 cls: this.hasFeedback ? 'has-feedback' : '',
40050                 cn: [
40051                     hiddenInput,
40052                     input,
40053                     {
40054                         tag: 'input',
40055                         cls: 'dial-code-holder',
40056                         disabled: true
40057                     }
40058                 ]
40059             };
40060             
40061             var container = {
40062                 cls: 'roo-select2-container input-group',
40063                 cn: [
40064                     flag_container,
40065                     box
40066                 ]
40067             };
40068             
40069             if (this.fieldLabel.length) {
40070                 var indicator = {
40071                     tag: 'i',
40072                     tooltip: 'This field is required'
40073                 };
40074                 
40075                 var label = {
40076                     tag: 'label',
40077                     'for':  id,
40078                     cls: 'control-label',
40079                     cn: []
40080                 };
40081                 
40082                 var label_text = {
40083                     tag: 'span',
40084                     html: this.fieldLabel
40085                 };
40086                 
40087                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40088                 label.cn = [
40089                     indicator,
40090                     label_text
40091                 ];
40092                 
40093                 if(this.indicatorpos == 'right') {
40094                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40095                     label.cn = [
40096                         label_text,
40097                         indicator
40098                     ];
40099                 }
40100                 
40101                 if(align == 'left') {
40102                     container = {
40103                         tag: 'div',
40104                         cn: [
40105                             container
40106                         ]
40107                     };
40108                     
40109                     if(this.labelWidth > 12){
40110                         label.style = "width: " + this.labelWidth + 'px';
40111                     }
40112                     if(this.labelWidth < 13 && this.labelmd == 0){
40113                         this.labelmd = this.labelWidth;
40114                     }
40115                     if(this.labellg > 0){
40116                         label.cls += ' col-lg-' + this.labellg;
40117                         input.cls += ' col-lg-' + (12 - this.labellg);
40118                     }
40119                     if(this.labelmd > 0){
40120                         label.cls += ' col-md-' + this.labelmd;
40121                         container.cls += ' col-md-' + (12 - this.labelmd);
40122                     }
40123                     if(this.labelsm > 0){
40124                         label.cls += ' col-sm-' + this.labelsm;
40125                         container.cls += ' col-sm-' + (12 - this.labelsm);
40126                     }
40127                     if(this.labelxs > 0){
40128                         label.cls += ' col-xs-' + this.labelxs;
40129                         container.cls += ' col-xs-' + (12 - this.labelxs);
40130                     }
40131                 }
40132             }
40133             
40134             cfg.cn = [
40135                 label,
40136                 container
40137             ];
40138             
40139             var settings = this;
40140             
40141             ['xs','sm','md','lg'].map(function(size){
40142                 if (settings[size]) {
40143                     cfg.cls += ' col-' + size + '-' + settings[size];
40144                 }
40145             });
40146             
40147             this.store = new Roo.data.Store({
40148                 proxy : new Roo.data.MemoryProxy({}),
40149                 reader : new Roo.data.JsonReader({
40150                     fields : [
40151                         {
40152                             'name' : 'name',
40153                             'type' : 'string'
40154                         },
40155                         {
40156                             'name' : 'iso2',
40157                             'type' : 'string'
40158                         },
40159                         {
40160                             'name' : 'dialCode',
40161                             'type' : 'string'
40162                         },
40163                         {
40164                             'name' : 'priority',
40165                             'type' : 'string'
40166                         },
40167                         {
40168                             'name' : 'areaCodes',
40169                             'type' : 'string'
40170                         }
40171                     ]
40172                 })
40173             });
40174             
40175             if(!this.preferedCountries) {
40176                 this.preferedCountries = [
40177                     'hk',
40178                     'gb',
40179                     'us'
40180                 ];
40181             }
40182             
40183             var p = this.preferedCountries.reverse();
40184             
40185             if(p) {
40186                 for (var i = 0; i < p.length; i++) {
40187                     for (var j = 0; j < this.allCountries.length; j++) {
40188                         if(this.allCountries[j].iso2 == p[i]) {
40189                             var t = this.allCountries[j];
40190                             this.allCountries.splice(j,1);
40191                             this.allCountries.unshift(t);
40192                         }
40193                     } 
40194                 }
40195             }
40196             
40197             this.store.proxy.data = {
40198                 success: true,
40199                 data: this.allCountries
40200             };
40201             
40202             return cfg;
40203         },
40204         
40205         initEvents : function()
40206         {
40207             this.createList();
40208             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40209             
40210             this.indicator = this.indicatorEl();
40211             this.flag = this.flagEl();
40212             this.dialCodeHolder = this.dialCodeHolderEl();
40213             
40214             this.trigger = this.el.select('div.flag-box',true).first();
40215             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40216             
40217             var _this = this;
40218             
40219             (function(){
40220                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40221                 _this.list.setWidth(lw);
40222             }).defer(100);
40223             
40224             this.list.on('mouseover', this.onViewOver, this);
40225             this.list.on('mousemove', this.onViewMove, this);
40226             this.inputEl().on("keyup", this.onKeyUp, this);
40227             this.inputEl().on("keypress", this.onKeyPress, this);
40228             
40229             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40230
40231             this.view = new Roo.View(this.list, this.tpl, {
40232                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40233             });
40234             
40235             this.view.on('click', this.onViewClick, this);
40236             this.setValue(this.defaultDialCode);
40237         },
40238         
40239         onTriggerClick : function(e)
40240         {
40241             Roo.log('trigger click');
40242             if(this.disabled){
40243                 return;
40244             }
40245             
40246             if(this.isExpanded()){
40247                 this.collapse();
40248                 this.hasFocus = false;
40249             }else {
40250                 this.store.load({});
40251                 this.hasFocus = true;
40252                 this.expand();
40253             }
40254         },
40255         
40256         isExpanded : function()
40257         {
40258             return this.list.isVisible();
40259         },
40260         
40261         collapse : function()
40262         {
40263             if(!this.isExpanded()){
40264                 return;
40265             }
40266             this.list.hide();
40267             Roo.get(document).un('mousedown', this.collapseIf, this);
40268             Roo.get(document).un('mousewheel', this.collapseIf, this);
40269             this.fireEvent('collapse', this);
40270             this.validate();
40271         },
40272         
40273         expand : function()
40274         {
40275             Roo.log('expand');
40276
40277             if(this.isExpanded() || !this.hasFocus){
40278                 return;
40279             }
40280             
40281             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40282             this.list.setWidth(lw);
40283             
40284             this.list.show();
40285             this.restrictHeight();
40286             
40287             Roo.get(document).on('mousedown', this.collapseIf, this);
40288             Roo.get(document).on('mousewheel', this.collapseIf, this);
40289             
40290             this.fireEvent('expand', this);
40291         },
40292         
40293         restrictHeight : function()
40294         {
40295             this.list.alignTo(this.inputEl(), this.listAlign);
40296             this.list.alignTo(this.inputEl(), this.listAlign);
40297         },
40298         
40299         onViewOver : function(e, t)
40300         {
40301             if(this.inKeyMode){
40302                 return;
40303             }
40304             var item = this.view.findItemFromChild(t);
40305             
40306             if(item){
40307                 var index = this.view.indexOf(item);
40308                 this.select(index, false);
40309             }
40310         },
40311
40312         // private
40313         onViewClick : function(view, doFocus, el, e)
40314         {
40315             var index = this.view.getSelectedIndexes()[0];
40316             
40317             var r = this.store.getAt(index);
40318             
40319             if(r){
40320                 this.onSelect(r, index);
40321             }
40322             if(doFocus !== false && !this.blockFocus){
40323                 this.inputEl().focus();
40324             }
40325         },
40326         
40327         onViewMove : function(e, t)
40328         {
40329             this.inKeyMode = false;
40330         },
40331         
40332         select : function(index, scrollIntoView)
40333         {
40334             this.selectedIndex = index;
40335             this.view.select(index);
40336             if(scrollIntoView !== false){
40337                 var el = this.view.getNode(index);
40338                 if(el){
40339                     this.list.scrollChildIntoView(el, false);
40340                 }
40341             }
40342         },
40343         
40344         createList : function()
40345         {
40346             this.list = Roo.get(document.body).createChild({
40347                 tag: 'ul',
40348                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40349                 style: 'display:none'
40350             });
40351             
40352             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40353         },
40354         
40355         collapseIf : function(e)
40356         {
40357             var in_combo  = e.within(this.el);
40358             var in_list =  e.within(this.list);
40359             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40360             
40361             if (in_combo || in_list || is_list) {
40362                 return;
40363             }
40364             this.collapse();
40365         },
40366         
40367         onSelect : function(record, index)
40368         {
40369             if(this.fireEvent('beforeselect', this, record, index) !== false){
40370                 
40371                 this.setFlagClass(record.data.iso2);
40372                 this.setDialCode(record.data.dialCode);
40373                 this.hasFocus = false;
40374                 this.collapse();
40375                 this.fireEvent('select', this, record, index);
40376             }
40377         },
40378         
40379         flagEl : function()
40380         {
40381             var flag = this.el.select('div.flag',true).first();
40382             if(!flag){
40383                 return false;
40384             }
40385             return flag;
40386         },
40387         
40388         dialCodeHolderEl : function()
40389         {
40390             var d = this.el.select('input.dial-code-holder',true).first();
40391             if(!d){
40392                 return false;
40393             }
40394             return d;
40395         },
40396         
40397         setDialCode : function(v)
40398         {
40399             this.dialCodeHolder.dom.value = '+'+v;
40400         },
40401         
40402         setFlagClass : function(n)
40403         {
40404             this.flag.dom.className = 'flag '+n;
40405         },
40406         
40407         getValue : function()
40408         {
40409             var v = this.inputEl().getValue();
40410             if(this.dialCodeHolder) {
40411                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40412             }
40413             return v;
40414         },
40415         
40416         setValue : function(v)
40417         {
40418             var d = this.getDialCode(v);
40419             
40420             //invalid dial code
40421             if(v.length == 0 || !d || d.length == 0) {
40422                 if(this.rendered){
40423                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40424                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40425                 }
40426                 return;
40427             }
40428             
40429             //valid dial code
40430             this.setFlagClass(this.dialCodeMapping[d].iso2);
40431             this.setDialCode(d);
40432             this.inputEl().dom.value = v.replace('+'+d,'');
40433             this.hiddenEl().dom.value = this.getValue();
40434             
40435             this.validate();
40436         },
40437         
40438         getDialCode : function(v)
40439         {
40440             v = v ||  '';
40441             
40442             if (v.length == 0) {
40443                 return this.dialCodeHolder.dom.value;
40444             }
40445             
40446             var dialCode = "";
40447             if (v.charAt(0) != "+") {
40448                 return false;
40449             }
40450             var numericChars = "";
40451             for (var i = 1; i < v.length; i++) {
40452               var c = v.charAt(i);
40453               if (!isNaN(c)) {
40454                 numericChars += c;
40455                 if (this.dialCodeMapping[numericChars]) {
40456                   dialCode = v.substr(1, i);
40457                 }
40458                 if (numericChars.length == 4) {
40459                   break;
40460                 }
40461               }
40462             }
40463             return dialCode;
40464         },
40465         
40466         reset : function()
40467         {
40468             this.setValue(this.defaultDialCode);
40469             this.validate();
40470         },
40471         
40472         hiddenEl : function()
40473         {
40474             return this.el.select('input.hidden-tel-input',true).first();
40475         },
40476         
40477         // after setting val
40478         onKeyUp : function(e){
40479             this.setValue(this.getValue());
40480         },
40481         
40482         onKeyPress : function(e){
40483             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40484                 e.stopEvent();
40485             }
40486         }
40487         
40488 });
40489 /**
40490  * @class Roo.bootstrap.MoneyField
40491  * @extends Roo.bootstrap.ComboBox
40492  * Bootstrap MoneyField class
40493  * 
40494  * @constructor
40495  * Create a new MoneyField.
40496  * @param {Object} config Configuration options
40497  */
40498
40499 Roo.bootstrap.MoneyField = function(config) {
40500     
40501     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40502     
40503 };
40504
40505 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40506     
40507     /**
40508      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40509      */
40510     allowDecimals : true,
40511     /**
40512      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40513      */
40514     decimalSeparator : ".",
40515     /**
40516      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40517      */
40518     decimalPrecision : 0,
40519     /**
40520      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40521      */
40522     allowNegative : true,
40523     /**
40524      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40525      */
40526     allowZero: true,
40527     /**
40528      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40529      */
40530     minValue : Number.NEGATIVE_INFINITY,
40531     /**
40532      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40533      */
40534     maxValue : Number.MAX_VALUE,
40535     /**
40536      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40537      */
40538     minText : "The minimum value for this field is {0}",
40539     /**
40540      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40541      */
40542     maxText : "The maximum value for this field is {0}",
40543     /**
40544      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40545      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40546      */
40547     nanText : "{0} is not a valid number",
40548     /**
40549      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40550      */
40551     castInt : true,
40552     /**
40553      * @cfg {String} defaults currency of the MoneyField
40554      * value should be in lkey
40555      */
40556     defaultCurrency : false,
40557     /**
40558      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40559      */
40560     thousandsDelimiter : false,
40561     /**
40562      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40563      */
40564     max_length: false,
40565     
40566     inputlg : 9,
40567     inputmd : 9,
40568     inputsm : 9,
40569     inputxs : 6,
40570     
40571     store : false,
40572     
40573     getAutoCreate : function()
40574     {
40575         var align = this.labelAlign || this.parentLabelAlign();
40576         
40577         var id = Roo.id();
40578
40579         var cfg = {
40580             cls: 'form-group',
40581             cn: []
40582         };
40583
40584         var input =  {
40585             tag: 'input',
40586             id : id,
40587             cls : 'form-control roo-money-amount-input',
40588             autocomplete: 'new-password'
40589         };
40590         
40591         var hiddenInput = {
40592             tag: 'input',
40593             type: 'hidden',
40594             id: Roo.id(),
40595             cls: 'hidden-number-input'
40596         };
40597         
40598         if(this.max_length) {
40599             input.maxlength = this.max_length; 
40600         }
40601         
40602         if (this.name) {
40603             hiddenInput.name = this.name;
40604         }
40605
40606         if (this.disabled) {
40607             input.disabled = true;
40608         }
40609
40610         var clg = 12 - this.inputlg;
40611         var cmd = 12 - this.inputmd;
40612         var csm = 12 - this.inputsm;
40613         var cxs = 12 - this.inputxs;
40614         
40615         var container = {
40616             tag : 'div',
40617             cls : 'row roo-money-field',
40618             cn : [
40619                 {
40620                     tag : 'div',
40621                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40622                     cn : [
40623                         {
40624                             tag : 'div',
40625                             cls: 'roo-select2-container input-group',
40626                             cn: [
40627                                 {
40628                                     tag : 'input',
40629                                     cls : 'form-control roo-money-currency-input',
40630                                     autocomplete: 'new-password',
40631                                     readOnly : 1,
40632                                     name : this.currencyName
40633                                 },
40634                                 {
40635                                     tag :'span',
40636                                     cls : 'input-group-addon',
40637                                     cn : [
40638                                         {
40639                                             tag: 'span',
40640                                             cls: 'caret'
40641                                         }
40642                                     ]
40643                                 }
40644                             ]
40645                         }
40646                     ]
40647                 },
40648                 {
40649                     tag : 'div',
40650                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40651                     cn : [
40652                         {
40653                             tag: 'div',
40654                             cls: this.hasFeedback ? 'has-feedback' : '',
40655                             cn: [
40656                                 input
40657                             ]
40658                         }
40659                     ]
40660                 }
40661             ]
40662             
40663         };
40664         
40665         if (this.fieldLabel.length) {
40666             var indicator = {
40667                 tag: 'i',
40668                 tooltip: 'This field is required'
40669             };
40670
40671             var label = {
40672                 tag: 'label',
40673                 'for':  id,
40674                 cls: 'control-label',
40675                 cn: []
40676             };
40677
40678             var label_text = {
40679                 tag: 'span',
40680                 html: this.fieldLabel
40681             };
40682
40683             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40684             label.cn = [
40685                 indicator,
40686                 label_text
40687             ];
40688
40689             if(this.indicatorpos == 'right') {
40690                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40691                 label.cn = [
40692                     label_text,
40693                     indicator
40694                 ];
40695             }
40696
40697             if(align == 'left') {
40698                 container = {
40699                     tag: 'div',
40700                     cn: [
40701                         container
40702                     ]
40703                 };
40704
40705                 if(this.labelWidth > 12){
40706                     label.style = "width: " + this.labelWidth + 'px';
40707                 }
40708                 if(this.labelWidth < 13 && this.labelmd == 0){
40709                     this.labelmd = this.labelWidth;
40710                 }
40711                 if(this.labellg > 0){
40712                     label.cls += ' col-lg-' + this.labellg;
40713                     input.cls += ' col-lg-' + (12 - this.labellg);
40714                 }
40715                 if(this.labelmd > 0){
40716                     label.cls += ' col-md-' + this.labelmd;
40717                     container.cls += ' col-md-' + (12 - this.labelmd);
40718                 }
40719                 if(this.labelsm > 0){
40720                     label.cls += ' col-sm-' + this.labelsm;
40721                     container.cls += ' col-sm-' + (12 - this.labelsm);
40722                 }
40723                 if(this.labelxs > 0){
40724                     label.cls += ' col-xs-' + this.labelxs;
40725                     container.cls += ' col-xs-' + (12 - this.labelxs);
40726                 }
40727             }
40728         }
40729
40730         cfg.cn = [
40731             label,
40732             container,
40733             hiddenInput
40734         ];
40735         
40736         var settings = this;
40737
40738         ['xs','sm','md','lg'].map(function(size){
40739             if (settings[size]) {
40740                 cfg.cls += ' col-' + size + '-' + settings[size];
40741             }
40742         });
40743         
40744         return cfg;
40745     },
40746     
40747     initEvents : function()
40748     {
40749         this.indicator = this.indicatorEl();
40750         
40751         this.initCurrencyEvent();
40752         
40753         this.initNumberEvent();
40754     },
40755     
40756     initCurrencyEvent : function()
40757     {
40758         if (!this.store) {
40759             throw "can not find store for combo";
40760         }
40761         
40762         this.store = Roo.factory(this.store, Roo.data);
40763         this.store.parent = this;
40764         
40765         this.createList();
40766         
40767         this.triggerEl = this.el.select('.input-group-addon', true).first();
40768         
40769         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40770         
40771         var _this = this;
40772         
40773         (function(){
40774             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40775             _this.list.setWidth(lw);
40776         }).defer(100);
40777         
40778         this.list.on('mouseover', this.onViewOver, this);
40779         this.list.on('mousemove', this.onViewMove, this);
40780         this.list.on('scroll', this.onViewScroll, this);
40781         
40782         if(!this.tpl){
40783             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40784         }
40785         
40786         this.view = new Roo.View(this.list, this.tpl, {
40787             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40788         });
40789         
40790         this.view.on('click', this.onViewClick, this);
40791         
40792         this.store.on('beforeload', this.onBeforeLoad, this);
40793         this.store.on('load', this.onLoad, this);
40794         this.store.on('loadexception', this.onLoadException, this);
40795         
40796         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40797             "up" : function(e){
40798                 this.inKeyMode = true;
40799                 this.selectPrev();
40800             },
40801
40802             "down" : function(e){
40803                 if(!this.isExpanded()){
40804                     this.onTriggerClick();
40805                 }else{
40806                     this.inKeyMode = true;
40807                     this.selectNext();
40808                 }
40809             },
40810
40811             "enter" : function(e){
40812                 this.collapse();
40813                 
40814                 if(this.fireEvent("specialkey", this, e)){
40815                     this.onViewClick(false);
40816                 }
40817                 
40818                 return true;
40819             },
40820
40821             "esc" : function(e){
40822                 this.collapse();
40823             },
40824
40825             "tab" : function(e){
40826                 this.collapse();
40827                 
40828                 if(this.fireEvent("specialkey", this, e)){
40829                     this.onViewClick(false);
40830                 }
40831                 
40832                 return true;
40833             },
40834
40835             scope : this,
40836
40837             doRelay : function(foo, bar, hname){
40838                 if(hname == 'down' || this.scope.isExpanded()){
40839                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40840                 }
40841                 return true;
40842             },
40843
40844             forceKeyDown: true
40845         });
40846         
40847         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40848         
40849     },
40850     
40851     initNumberEvent : function(e)
40852     {
40853         this.inputEl().on("keydown" , this.fireKey,  this);
40854         this.inputEl().on("focus", this.onFocus,  this);
40855         this.inputEl().on("blur", this.onBlur,  this);
40856         
40857         this.inputEl().relayEvent('keyup', this);
40858         
40859         if(this.indicator){
40860             this.indicator.addClass('invisible');
40861         }
40862  
40863         this.originalValue = this.getValue();
40864         
40865         if(this.validationEvent == 'keyup'){
40866             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40867             this.inputEl().on('keyup', this.filterValidation, this);
40868         }
40869         else if(this.validationEvent !== false){
40870             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40871         }
40872         
40873         if(this.selectOnFocus){
40874             this.on("focus", this.preFocus, this);
40875             
40876         }
40877         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40878             this.inputEl().on("keypress", this.filterKeys, this);
40879         } else {
40880             this.inputEl().relayEvent('keypress', this);
40881         }
40882         
40883         var allowed = "0123456789";
40884         
40885         if(this.allowDecimals){
40886             allowed += this.decimalSeparator;
40887         }
40888         
40889         if(this.allowNegative){
40890             allowed += "-";
40891         }
40892         
40893         if(this.thousandsDelimiter) {
40894             allowed += ",";
40895         }
40896         
40897         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40898         
40899         var keyPress = function(e){
40900             
40901             var k = e.getKey();
40902             
40903             var c = e.getCharCode();
40904             
40905             if(
40906                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40907                     allowed.indexOf(String.fromCharCode(c)) === -1
40908             ){
40909                 e.stopEvent();
40910                 return;
40911             }
40912             
40913             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40914                 return;
40915             }
40916             
40917             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40918                 e.stopEvent();
40919             }
40920         };
40921         
40922         this.inputEl().on("keypress", keyPress, this);
40923         
40924     },
40925     
40926     onTriggerClick : function(e)
40927     {   
40928         if(this.disabled){
40929             return;
40930         }
40931         
40932         this.page = 0;
40933         this.loadNext = false;
40934         
40935         if(this.isExpanded()){
40936             this.collapse();
40937             return;
40938         }
40939         
40940         this.hasFocus = true;
40941         
40942         if(this.triggerAction == 'all') {
40943             this.doQuery(this.allQuery, true);
40944             return;
40945         }
40946         
40947         this.doQuery(this.getRawValue());
40948     },
40949     
40950     getCurrency : function()
40951     {   
40952         var v = this.currencyEl().getValue();
40953         
40954         return v;
40955     },
40956     
40957     restrictHeight : function()
40958     {
40959         this.list.alignTo(this.currencyEl(), this.listAlign);
40960         this.list.alignTo(this.currencyEl(), this.listAlign);
40961     },
40962     
40963     onViewClick : function(view, doFocus, el, e)
40964     {
40965         var index = this.view.getSelectedIndexes()[0];
40966         
40967         var r = this.store.getAt(index);
40968         
40969         if(r){
40970             this.onSelect(r, index);
40971         }
40972     },
40973     
40974     onSelect : function(record, index){
40975         
40976         if(this.fireEvent('beforeselect', this, record, index) !== false){
40977         
40978             this.setFromCurrencyData(index > -1 ? record.data : false);
40979             
40980             this.collapse();
40981             
40982             this.fireEvent('select', this, record, index);
40983         }
40984     },
40985     
40986     setFromCurrencyData : function(o)
40987     {
40988         var currency = '';
40989         
40990         this.lastCurrency = o;
40991         
40992         if (this.currencyField) {
40993             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40994         } else {
40995             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40996         }
40997         
40998         this.lastSelectionText = currency;
40999         
41000         //setting default currency
41001         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41002             this.setCurrency(this.defaultCurrency);
41003             return;
41004         }
41005         
41006         this.setCurrency(currency);
41007     },
41008     
41009     setFromData : function(o)
41010     {
41011         var c = {};
41012         
41013         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41014         
41015         this.setFromCurrencyData(c);
41016         
41017         var value = '';
41018         
41019         if (this.name) {
41020             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41021         } else {
41022             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41023         }
41024         
41025         this.setValue(value);
41026         
41027     },
41028     
41029     setCurrency : function(v)
41030     {   
41031         this.currencyValue = v;
41032         
41033         if(this.rendered){
41034             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41035             this.validate();
41036         }
41037     },
41038     
41039     setValue : function(v)
41040     {
41041         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41042         
41043         this.value = v;
41044         
41045         if(this.rendered){
41046             
41047             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41048             
41049             this.inputEl().dom.value = (v == '') ? '' :
41050                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41051             
41052             if(!this.allowZero && v === '0') {
41053                 this.hiddenEl().dom.value = '';
41054                 this.inputEl().dom.value = '';
41055             }
41056             
41057             this.validate();
41058         }
41059     },
41060     
41061     getRawValue : function()
41062     {
41063         var v = this.inputEl().getValue();
41064         
41065         return v;
41066     },
41067     
41068     getValue : function()
41069     {
41070         return this.fixPrecision(this.parseValue(this.getRawValue()));
41071     },
41072     
41073     parseValue : function(value)
41074     {
41075         if(this.thousandsDelimiter) {
41076             value += "";
41077             r = new RegExp(",", "g");
41078             value = value.replace(r, "");
41079         }
41080         
41081         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41082         return isNaN(value) ? '' : value;
41083         
41084     },
41085     
41086     fixPrecision : function(value)
41087     {
41088         if(this.thousandsDelimiter) {
41089             value += "";
41090             r = new RegExp(",", "g");
41091             value = value.replace(r, "");
41092         }
41093         
41094         var nan = isNaN(value);
41095         
41096         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41097             return nan ? '' : value;
41098         }
41099         return parseFloat(value).toFixed(this.decimalPrecision);
41100     },
41101     
41102     decimalPrecisionFcn : function(v)
41103     {
41104         return Math.floor(v);
41105     },
41106     
41107     validateValue : function(value)
41108     {
41109         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41110             return false;
41111         }
41112         
41113         var num = this.parseValue(value);
41114         
41115         if(isNaN(num)){
41116             this.markInvalid(String.format(this.nanText, value));
41117             return false;
41118         }
41119         
41120         if(num < this.minValue){
41121             this.markInvalid(String.format(this.minText, this.minValue));
41122             return false;
41123         }
41124         
41125         if(num > this.maxValue){
41126             this.markInvalid(String.format(this.maxText, this.maxValue));
41127             return false;
41128         }
41129         
41130         return true;
41131     },
41132     
41133     validate : function()
41134     {
41135         if(this.disabled || this.allowBlank){
41136             this.markValid();
41137             return true;
41138         }
41139         
41140         var currency = this.getCurrency();
41141         
41142         if(this.validateValue(this.getRawValue()) && currency.length){
41143             this.markValid();
41144             return true;
41145         }
41146         
41147         this.markInvalid();
41148         return false;
41149     },
41150     
41151     getName: function()
41152     {
41153         return this.name;
41154     },
41155     
41156     beforeBlur : function()
41157     {
41158         if(!this.castInt){
41159             return;
41160         }
41161         
41162         var v = this.parseValue(this.getRawValue());
41163         
41164         if(v || v == 0){
41165             this.setValue(v);
41166         }
41167     },
41168     
41169     onBlur : function()
41170     {
41171         this.beforeBlur();
41172         
41173         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41174             //this.el.removeClass(this.focusClass);
41175         }
41176         
41177         this.hasFocus = false;
41178         
41179         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41180             this.validate();
41181         }
41182         
41183         var v = this.getValue();
41184         
41185         if(String(v) !== String(this.startValue)){
41186             this.fireEvent('change', this, v, this.startValue);
41187         }
41188         
41189         this.fireEvent("blur", this);
41190     },
41191     
41192     inputEl : function()
41193     {
41194         return this.el.select('.roo-money-amount-input', true).first();
41195     },
41196     
41197     currencyEl : function()
41198     {
41199         return this.el.select('.roo-money-currency-input', true).first();
41200     },
41201     
41202     hiddenEl : function()
41203     {
41204         return this.el.select('input.hidden-number-input',true).first();
41205     }
41206     
41207 });