Roo/bootstrap/ComboBox.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 (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
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     badge: '',
653     theme: 'default',
654     inverse: false,
655     
656     toggle: false,
657     ontext: 'ON',
658     offtext: 'OFF',
659     defaulton: true,
660     preventDefault: true,
661     removeClass: false,
662     name: false,
663     target: false,
664      
665     pressed : null,
666      
667     
668     getAutoCreate : function(){
669         
670         var cfg = {
671             tag : 'button',
672             cls : 'roo-button',
673             html: ''
674         };
675         
676         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
677             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
678             this.tag = 'button';
679         } else {
680             cfg.tag = this.tag;
681         }
682         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
683         
684         if (this.toggle == true) {
685             cfg={
686                 tag: 'div',
687                 cls: 'slider-frame roo-button',
688                 cn: [
689                     {
690                         tag: 'span',
691                         'data-on-text':'ON',
692                         'data-off-text':'OFF',
693                         cls: 'slider-button',
694                         html: this.offtext
695                     }
696                 ]
697             };
698             
699             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
700                 cfg.cls += ' '+this.weight;
701             }
702             
703             return cfg;
704         }
705         
706         if (this.isClose) {
707             cfg.cls += ' close';
708             
709             cfg["aria-hidden"] = true;
710             
711             cfg.html = "&times;";
712             
713             return cfg;
714         }
715         
716          
717         if (this.theme==='default') {
718             cfg.cls = 'btn roo-button';
719             
720             //if (this.parentType != 'Navbar') {
721             this.weight = this.weight.length ?  this.weight : 'default';
722             //}
723             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
724                 
725                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
726                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
727                 cfg.cls += ' btn-' + outline + weight;
728                 if (this.weight == 'default') {
729                     // BC
730                     cfg.cls += ' btn-' + this.weight;
731                 }
732             }
733         } else if (this.theme==='glow') {
734             
735             cfg.tag = 'a';
736             cfg.cls = 'btn-glow roo-button';
737             
738             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
739                 
740                 cfg.cls += ' ' + this.weight;
741             }
742         }
743    
744         
745         if (this.inverse) {
746             this.cls += ' inverse';
747         }
748         
749         
750         if (this.active || this.pressed === true) {
751             cfg.cls += ' active';
752         }
753         
754         if (this.disabled) {
755             cfg.disabled = 'disabled';
756         }
757         
758         if (this.items) {
759             Roo.log('changing to ul' );
760             cfg.tag = 'ul';
761             this.glyphicon = 'caret';
762         }
763         
764         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
765          
766         //gsRoo.log(this.parentType);
767         if (this.parentType === 'Navbar' && !this.parent().bar) {
768             Roo.log('changing to li?');
769             
770             cfg.tag = 'li';
771             
772             cfg.cls = '';
773             cfg.cn =  [{
774                 tag : 'a',
775                 cls : 'roo-button',
776                 html : this.html,
777                 href : this.href || '#'
778             }];
779             if (this.menu) {
780                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
781                 cfg.cls += ' dropdown';
782             }   
783             
784             delete cfg.html;
785             
786         }
787         
788        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
789         
790         if (this.glyphicon) {
791             cfg.html = ' ' + cfg.html;
792             
793             cfg.cn = [
794                 {
795                     tag: 'span',
796                     cls: 'glyphicon glyphicon-' + this.glyphicon
797                 }
798             ];
799         }
800         
801         if (this.badge) {
802             cfg.html += ' ';
803             
804             cfg.tag = 'a';
805             
806 //            cfg.cls='btn roo-button';
807             
808             cfg.href=this.href;
809             
810             var value = cfg.html;
811             
812             if(this.glyphicon){
813                 value = {
814                             tag: 'span',
815                             cls: 'glyphicon glyphicon-' + this.glyphicon,
816                             html: this.html
817                         };
818                 
819             }
820             var bw = this.badge_weight.length ? this.badge_weight :
821                 (this.weight.length ? this.weight : 'secondary');
822             bw = bw == 'default' ? 'secondary' : bw;
823             
824             cfg.cn = [
825                 value,
826                 {
827                     tag: 'span',
828                     cls: 'badge badge-' + bw,
829                     html: this.badge
830                 }
831             ];
832             
833             cfg.html='';
834         }
835         
836         if (this.menu) {
837             cfg.cls += ' dropdown';
838             cfg.html = typeof(cfg.html) != 'undefined' ?
839                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
840         }
841         
842         if (cfg.tag !== 'a' && this.href !== '') {
843             throw "Tag must be a to set href.";
844         } else if (this.href.length > 0) {
845             cfg.href = this.href;
846         }
847         
848         if(this.removeClass){
849             cfg.cls = '';
850         }
851         
852         if(this.target){
853             cfg.target = this.target;
854         }
855         
856         return cfg;
857     },
858     initEvents: function() {
859        // Roo.log('init events?');
860 //        Roo.log(this.el.dom);
861         // add the menu...
862         
863         if (typeof (this.menu) != 'undefined') {
864             this.menu.parentType = this.xtype;
865             this.menu.triggerEl = this.el;
866             this.addxtype(Roo.apply({}, this.menu));
867         }
868
869
870        if (this.el.hasClass('roo-button')) {
871             this.el.on('click', this.onClick, this);
872        } else {
873             this.el.select('.roo-button').on('click', this.onClick, this);
874        }
875        
876        if(this.removeClass){
877            this.el.on('click', this.onClick, this);
878        }
879        
880        this.el.enableDisplayMode();
881         
882     },
883     onClick : function(e)
884     {
885         if (this.disabled) {
886             return;
887         }
888         
889         Roo.log('button on click ');
890         if(this.preventDefault){
891             e.preventDefault();
892         }
893         
894         if (this.pressed === true || this.pressed === false) {
895             this.toggleActive(e);
896         }
897         
898         
899         this.fireEvent('click', this, e);
900     },
901     
902     /**
903      * Enables this button
904      */
905     enable : function()
906     {
907         this.disabled = false;
908         this.el.removeClass('disabled');
909     },
910     
911     /**
912      * Disable this button
913      */
914     disable : function()
915     {
916         this.disabled = true;
917         this.el.addClass('disabled');
918     },
919      /**
920      * sets the active state on/off, 
921      * @param {Boolean} state (optional) Force a particular state
922      */
923     setActive : function(v) {
924         
925         this.el[v ? 'addClass' : 'removeClass']('active');
926         this.pressed = v;
927     },
928      /**
929      * toggles the current active state 
930      */
931     toggleActive : function(e)
932     {
933         this.setActive(!this.pressed);
934         this.fireEvent('toggle', this, e, !this.pressed);
935     },
936      /**
937      * get the current active state
938      * @return {boolean} true if it's active
939      */
940     isActive : function()
941     {
942         return this.el.hasClass('active');
943     },
944     /**
945      * set the text of the first selected button
946      */
947     setText : function(str)
948     {
949         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
950     },
951     /**
952      * get the text of the first selected button
953      */
954     getText : function()
955     {
956         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
957     },
958     
959     setWeight : function(str)
960     {
961         this.el.removeClass(this.weightClass);
962         this.weight = str;
963         var outline = this.outline ? 'outline-' : '';
964         if (str == 'default') {
965             this.el.addClass('btn-default btn-outline-secondary');        
966             return;
967         }
968         this.el.addClass('btn-' + outline + str);        
969     }
970     
971     
972 });
973
974  /*
975  * - LGPL
976  *
977  * column
978  * 
979  */
980
981 /**
982  * @class Roo.bootstrap.Column
983  * @extends Roo.bootstrap.Component
984  * Bootstrap Column class
985  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
986  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
987  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
988  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
989  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
990  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
991  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
992  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
993  *
994  * 
995  * @cfg {Boolean} hidden (true|false) hide the element
996  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
997  * @cfg {String} fa (ban|check|...) font awesome icon
998  * @cfg {Number} fasize (1|2|....) font awsome size
999
1000  * @cfg {String} icon (info-sign|check|...) glyphicon name
1001
1002  * @cfg {String} html content of column.
1003  * 
1004  * @constructor
1005  * Create a new Column
1006  * @param {Object} config The config object
1007  */
1008
1009 Roo.bootstrap.Column = function(config){
1010     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1011 };
1012
1013 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1014     
1015     xs: false,
1016     sm: false,
1017     md: false,
1018     lg: false,
1019     xsoff: false,
1020     smoff: false,
1021     mdoff: false,
1022     lgoff: false,
1023     html: '',
1024     offset: 0,
1025     alert: false,
1026     fa: false,
1027     icon : false,
1028     hidden : false,
1029     fasize : 1,
1030     
1031     getAutoCreate : function(){
1032         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1033         
1034         cfg = {
1035             tag: 'div',
1036             cls: 'column'
1037         };
1038         
1039         var settings=this;
1040         ['xs','sm','md','lg'].map(function(size){
1041             //Roo.log( size + ':' + settings[size]);
1042             
1043             if (settings[size+'off'] !== false) {
1044                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1045             }
1046             
1047             if (settings[size] === false) {
1048                 return;
1049             }
1050             
1051             if (!settings[size]) { // 0 = hidden
1052                 cfg.cls += ' hidden-' + size;
1053                 return;
1054             }
1055             cfg.cls += ' col-' + size + '-' + settings[size];
1056             
1057         });
1058         
1059         if (this.hidden) {
1060             cfg.cls += ' hidden';
1061         }
1062         
1063         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1064             cfg.cls +=' alert alert-' + this.alert;
1065         }
1066         
1067         
1068         if (this.html.length) {
1069             cfg.html = this.html;
1070         }
1071         if (this.fa) {
1072             var fasize = '';
1073             if (this.fasize > 1) {
1074                 fasize = ' fa-' + this.fasize + 'x';
1075             }
1076             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1077             
1078             
1079         }
1080         if (this.icon) {
1081             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1082         }
1083         
1084         return cfg;
1085     }
1086    
1087 });
1088
1089  
1090
1091  /*
1092  * - LGPL
1093  *
1094  * page container.
1095  * 
1096  */
1097
1098
1099 /**
1100  * @class Roo.bootstrap.Container
1101  * @extends Roo.bootstrap.Component
1102  * Bootstrap Container class
1103  * @cfg {Boolean} jumbotron is it a jumbotron element
1104  * @cfg {String} html content of element
1105  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1106  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1107  * @cfg {String} header content of header (for panel)
1108  * @cfg {String} footer content of footer (for panel)
1109  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1110  * @cfg {String} tag (header|aside|section) type of HTML tag.
1111  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1112  * @cfg {String} fa font awesome icon
1113  * @cfg {String} icon (info-sign|check|...) glyphicon name
1114  * @cfg {Boolean} hidden (true|false) hide the element
1115  * @cfg {Boolean} expandable (true|false) default false
1116  * @cfg {Boolean} expanded (true|false) default true
1117  * @cfg {String} rheader contet on the right of header
1118  * @cfg {Boolean} clickable (true|false) default false
1119
1120  *     
1121  * @constructor
1122  * Create a new Container
1123  * @param {Object} config The config object
1124  */
1125
1126 Roo.bootstrap.Container = function(config){
1127     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1128     
1129     this.addEvents({
1130         // raw events
1131          /**
1132          * @event expand
1133          * After the panel has been expand
1134          * 
1135          * @param {Roo.bootstrap.Container} this
1136          */
1137         "expand" : true,
1138         /**
1139          * @event collapse
1140          * After the panel has been collapsed
1141          * 
1142          * @param {Roo.bootstrap.Container} this
1143          */
1144         "collapse" : true,
1145         /**
1146          * @event click
1147          * When a element is chick
1148          * @param {Roo.bootstrap.Container} this
1149          * @param {Roo.EventObject} e
1150          */
1151         "click" : true
1152     });
1153 };
1154
1155 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1156     
1157     jumbotron : false,
1158     well: '',
1159     panel : '',
1160     header: '',
1161     footer : '',
1162     sticky: '',
1163     tag : false,
1164     alert : false,
1165     fa: false,
1166     icon : false,
1167     expandable : false,
1168     rheader : '',
1169     expanded : true,
1170     clickable: false,
1171   
1172      
1173     getChildContainer : function() {
1174         
1175         if(!this.el){
1176             return false;
1177         }
1178         
1179         if (this.panel.length) {
1180             return this.el.select('.panel-body',true).first();
1181         }
1182         
1183         return this.el;
1184     },
1185     
1186     
1187     getAutoCreate : function(){
1188         
1189         var cfg = {
1190             tag : this.tag || 'div',
1191             html : '',
1192             cls : ''
1193         };
1194         if (this.jumbotron) {
1195             cfg.cls = 'jumbotron';
1196         }
1197         
1198         
1199         
1200         // - this is applied by the parent..
1201         //if (this.cls) {
1202         //    cfg.cls = this.cls + '';
1203         //}
1204         
1205         if (this.sticky.length) {
1206             
1207             var bd = Roo.get(document.body);
1208             if (!bd.hasClass('bootstrap-sticky')) {
1209                 bd.addClass('bootstrap-sticky');
1210                 Roo.select('html',true).setStyle('height', '100%');
1211             }
1212              
1213             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1214         }
1215         
1216         
1217         if (this.well.length) {
1218             switch (this.well) {
1219                 case 'lg':
1220                 case 'sm':
1221                     cfg.cls +=' well well-' +this.well;
1222                     break;
1223                 default:
1224                     cfg.cls +=' well';
1225                     break;
1226             }
1227         }
1228         
1229         if (this.hidden) {
1230             cfg.cls += ' hidden';
1231         }
1232         
1233         
1234         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1235             cfg.cls +=' alert alert-' + this.alert;
1236         }
1237         
1238         var body = cfg;
1239         
1240         if (this.panel.length) {
1241             cfg.cls += ' panel panel-' + this.panel;
1242             cfg.cn = [];
1243             if (this.header.length) {
1244                 
1245                 var h = [];
1246                 
1247                 if(this.expandable){
1248                     
1249                     cfg.cls = cfg.cls + ' expandable';
1250                     
1251                     h.push({
1252                         tag: 'i',
1253                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1254                     });
1255                     
1256                 }
1257                 
1258                 h.push(
1259                     {
1260                         tag: 'span',
1261                         cls : 'panel-title',
1262                         html : (this.expandable ? '&nbsp;' : '') + this.header
1263                     },
1264                     {
1265                         tag: 'span',
1266                         cls: 'panel-header-right',
1267                         html: this.rheader
1268                     }
1269                 );
1270                 
1271                 cfg.cn.push({
1272                     cls : 'panel-heading',
1273                     style : this.expandable ? 'cursor: pointer' : '',
1274                     cn : h
1275                 });
1276                 
1277             }
1278             
1279             body = false;
1280             cfg.cn.push({
1281                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1282                 html : this.html
1283             });
1284             
1285             
1286             if (this.footer.length) {
1287                 cfg.cn.push({
1288                     cls : 'panel-footer',
1289                     html : this.footer
1290                     
1291                 });
1292             }
1293             
1294         }
1295         
1296         if (body) {
1297             body.html = this.html || cfg.html;
1298             // prefix with the icons..
1299             if (this.fa) {
1300                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1301             }
1302             if (this.icon) {
1303                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1304             }
1305             
1306             
1307         }
1308         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1309             cfg.cls =  'container';
1310         }
1311         
1312         return cfg;
1313     },
1314     
1315     initEvents: function() 
1316     {
1317         if(this.expandable){
1318             var headerEl = this.headerEl();
1319         
1320             if(headerEl){
1321                 headerEl.on('click', this.onToggleClick, this);
1322             }
1323         }
1324         
1325         if(this.clickable){
1326             this.el.on('click', this.onClick, this);
1327         }
1328         
1329     },
1330     
1331     onToggleClick : function()
1332     {
1333         var headerEl = this.headerEl();
1334         
1335         if(!headerEl){
1336             return;
1337         }
1338         
1339         if(this.expanded){
1340             this.collapse();
1341             return;
1342         }
1343         
1344         this.expand();
1345     },
1346     
1347     expand : function()
1348     {
1349         if(this.fireEvent('expand', this)) {
1350             
1351             this.expanded = true;
1352             
1353             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1354             
1355             this.el.select('.panel-body',true).first().removeClass('hide');
1356             
1357             var toggleEl = this.toggleEl();
1358
1359             if(!toggleEl){
1360                 return;
1361             }
1362
1363             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1364         }
1365         
1366     },
1367     
1368     collapse : function()
1369     {
1370         if(this.fireEvent('collapse', this)) {
1371             
1372             this.expanded = false;
1373             
1374             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1375             this.el.select('.panel-body',true).first().addClass('hide');
1376         
1377             var toggleEl = this.toggleEl();
1378
1379             if(!toggleEl){
1380                 return;
1381             }
1382
1383             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1384         }
1385     },
1386     
1387     toggleEl : function()
1388     {
1389         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1390             return;
1391         }
1392         
1393         return this.el.select('.panel-heading .fa',true).first();
1394     },
1395     
1396     headerEl : function()
1397     {
1398         if(!this.el || !this.panel.length || !this.header.length){
1399             return;
1400         }
1401         
1402         return this.el.select('.panel-heading',true).first()
1403     },
1404     
1405     bodyEl : function()
1406     {
1407         if(!this.el || !this.panel.length){
1408             return;
1409         }
1410         
1411         return this.el.select('.panel-body',true).first()
1412     },
1413     
1414     titleEl : function()
1415     {
1416         if(!this.el || !this.panel.length || !this.header.length){
1417             return;
1418         }
1419         
1420         return this.el.select('.panel-title',true).first();
1421     },
1422     
1423     setTitle : function(v)
1424     {
1425         var titleEl = this.titleEl();
1426         
1427         if(!titleEl){
1428             return;
1429         }
1430         
1431         titleEl.dom.innerHTML = v;
1432     },
1433     
1434     getTitle : function()
1435     {
1436         
1437         var titleEl = this.titleEl();
1438         
1439         if(!titleEl){
1440             return '';
1441         }
1442         
1443         return titleEl.dom.innerHTML;
1444     },
1445     
1446     setRightTitle : function(v)
1447     {
1448         var t = this.el.select('.panel-header-right',true).first();
1449         
1450         if(!t){
1451             return;
1452         }
1453         
1454         t.dom.innerHTML = v;
1455     },
1456     
1457     onClick : function(e)
1458     {
1459         e.preventDefault();
1460         
1461         this.fireEvent('click', this, e);
1462     }
1463 });
1464
1465  /*
1466  * - LGPL
1467  *
1468  * image
1469  * 
1470  */
1471
1472
1473 /**
1474  * @class Roo.bootstrap.Img
1475  * @extends Roo.bootstrap.Component
1476  * Bootstrap Img class
1477  * @cfg {Boolean} imgResponsive false | true
1478  * @cfg {String} border rounded | circle | thumbnail
1479  * @cfg {String} src image source
1480  * @cfg {String} alt image alternative text
1481  * @cfg {String} href a tag href
1482  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1483  * @cfg {String} xsUrl xs image source
1484  * @cfg {String} smUrl sm image source
1485  * @cfg {String} mdUrl md image source
1486  * @cfg {String} lgUrl lg image source
1487  * 
1488  * @constructor
1489  * Create a new Input
1490  * @param {Object} config The config object
1491  */
1492
1493 Roo.bootstrap.Img = function(config){
1494     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1495     
1496     this.addEvents({
1497         // img events
1498         /**
1499          * @event click
1500          * The img click event for the img.
1501          * @param {Roo.EventObject} e
1502          */
1503         "click" : true
1504     });
1505 };
1506
1507 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1508     
1509     imgResponsive: true,
1510     border: '',
1511     src: 'about:blank',
1512     href: false,
1513     target: false,
1514     xsUrl: '',
1515     smUrl: '',
1516     mdUrl: '',
1517     lgUrl: '',
1518
1519     getAutoCreate : function()
1520     {   
1521         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1522             return this.createSingleImg();
1523         }
1524         
1525         var cfg = {
1526             tag: 'div',
1527             cls: 'roo-image-responsive-group',
1528             cn: []
1529         };
1530         var _this = this;
1531         
1532         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1533             
1534             if(!_this[size + 'Url']){
1535                 return;
1536             }
1537             
1538             var img = {
1539                 tag: 'img',
1540                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1541                 html: _this.html || cfg.html,
1542                 src: _this[size + 'Url']
1543             };
1544             
1545             img.cls += ' roo-image-responsive-' + size;
1546             
1547             var s = ['xs', 'sm', 'md', 'lg'];
1548             
1549             s.splice(s.indexOf(size), 1);
1550             
1551             Roo.each(s, function(ss){
1552                 img.cls += ' hidden-' + ss;
1553             });
1554             
1555             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1556                 cfg.cls += ' img-' + _this.border;
1557             }
1558             
1559             if(_this.alt){
1560                 cfg.alt = _this.alt;
1561             }
1562             
1563             if(_this.href){
1564                 var a = {
1565                     tag: 'a',
1566                     href: _this.href,
1567                     cn: [
1568                         img
1569                     ]
1570                 };
1571
1572                 if(this.target){
1573                     a.target = _this.target;
1574                 }
1575             }
1576             
1577             cfg.cn.push((_this.href) ? a : img);
1578             
1579         });
1580         
1581         return cfg;
1582     },
1583     
1584     createSingleImg : function()
1585     {
1586         var cfg = {
1587             tag: 'img',
1588             cls: (this.imgResponsive) ? 'img-responsive' : '',
1589             html : null,
1590             src : 'about:blank'  // just incase src get's set to undefined?!?
1591         };
1592         
1593         cfg.html = this.html || cfg.html;
1594         
1595         cfg.src = this.src || cfg.src;
1596         
1597         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1598             cfg.cls += ' img-' + this.border;
1599         }
1600         
1601         if(this.alt){
1602             cfg.alt = this.alt;
1603         }
1604         
1605         if(this.href){
1606             var a = {
1607                 tag: 'a',
1608                 href: this.href,
1609                 cn: [
1610                     cfg
1611                 ]
1612             };
1613             
1614             if(this.target){
1615                 a.target = this.target;
1616             }
1617             
1618         }
1619         
1620         return (this.href) ? a : cfg;
1621     },
1622     
1623     initEvents: function() 
1624     {
1625         if(!this.href){
1626             this.el.on('click', this.onClick, this);
1627         }
1628         
1629     },
1630     
1631     onClick : function(e)
1632     {
1633         Roo.log('img onclick');
1634         this.fireEvent('click', this, e);
1635     },
1636     /**
1637      * Sets the url of the image - used to update it
1638      * @param {String} url the url of the image
1639      */
1640     
1641     setSrc : function(url)
1642     {
1643         this.src =  url;
1644         
1645         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1646             this.el.dom.src =  url;
1647             return;
1648         }
1649         
1650         this.el.select('img', true).first().dom.src =  url;
1651     }
1652     
1653     
1654    
1655 });
1656
1657  /*
1658  * - LGPL
1659  *
1660  * image
1661  * 
1662  */
1663
1664
1665 /**
1666  * @class Roo.bootstrap.Link
1667  * @extends Roo.bootstrap.Component
1668  * Bootstrap Link Class
1669  * @cfg {String} alt image alternative text
1670  * @cfg {String} href a tag href
1671  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1672  * @cfg {String} html the content of the link.
1673  * @cfg {String} anchor name for the anchor link
1674  * @cfg {String} fa - favicon
1675
1676  * @cfg {Boolean} preventDefault (true | false) default false
1677
1678  * 
1679  * @constructor
1680  * Create a new Input
1681  * @param {Object} config The config object
1682  */
1683
1684 Roo.bootstrap.Link = function(config){
1685     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1686     
1687     this.addEvents({
1688         // img events
1689         /**
1690          * @event click
1691          * The img click event for the img.
1692          * @param {Roo.EventObject} e
1693          */
1694         "click" : true
1695     });
1696 };
1697
1698 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1699     
1700     href: false,
1701     target: false,
1702     preventDefault: false,
1703     anchor : false,
1704     alt : false,
1705     fa: false,
1706
1707
1708     getAutoCreate : function()
1709     {
1710         var html = this.html || '';
1711         
1712         if (this.fa !== false) {
1713             html = '<i class="fa fa-' + this.fa + '"></i>';
1714         }
1715         var cfg = {
1716             tag: 'a'
1717         };
1718         // anchor's do not require html/href...
1719         if (this.anchor === false) {
1720             cfg.html = html;
1721             cfg.href = this.href || '#';
1722         } else {
1723             cfg.name = this.anchor;
1724             if (this.html !== false || this.fa !== false) {
1725                 cfg.html = html;
1726             }
1727             if (this.href !== false) {
1728                 cfg.href = this.href;
1729             }
1730         }
1731         
1732         if(this.alt !== false){
1733             cfg.alt = this.alt;
1734         }
1735         
1736         
1737         if(this.target !== false) {
1738             cfg.target = this.target;
1739         }
1740         
1741         return cfg;
1742     },
1743     
1744     initEvents: function() {
1745         
1746         if(!this.href || this.preventDefault){
1747             this.el.on('click', this.onClick, this);
1748         }
1749     },
1750     
1751     onClick : function(e)
1752     {
1753         if(this.preventDefault){
1754             e.preventDefault();
1755         }
1756         //Roo.log('img onclick');
1757         this.fireEvent('click', this, e);
1758     }
1759    
1760 });
1761
1762  /*
1763  * - LGPL
1764  *
1765  * header
1766  * 
1767  */
1768
1769 /**
1770  * @class Roo.bootstrap.Header
1771  * @extends Roo.bootstrap.Component
1772  * Bootstrap Header class
1773  * @cfg {String} html content of header
1774  * @cfg {Number} level (1|2|3|4|5|6) default 1
1775  * 
1776  * @constructor
1777  * Create a new Header
1778  * @param {Object} config The config object
1779  */
1780
1781
1782 Roo.bootstrap.Header  = function(config){
1783     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1784 };
1785
1786 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1787     
1788     //href : false,
1789     html : false,
1790     level : 1,
1791     
1792     
1793     
1794     getAutoCreate : function(){
1795         
1796         
1797         
1798         var cfg = {
1799             tag: 'h' + (1 *this.level),
1800             html: this.html || ''
1801         } ;
1802         
1803         return cfg;
1804     }
1805    
1806 });
1807
1808  
1809
1810  /*
1811  * Based on:
1812  * Ext JS Library 1.1.1
1813  * Copyright(c) 2006-2007, Ext JS, LLC.
1814  *
1815  * Originally Released Under LGPL - original licence link has changed is not relivant.
1816  *
1817  * Fork - LGPL
1818  * <script type="text/javascript">
1819  */
1820  
1821 /**
1822  * @class Roo.bootstrap.MenuMgr
1823  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1824  * @singleton
1825  */
1826 Roo.bootstrap.MenuMgr = function(){
1827    var menus, active, groups = {}, attached = false, lastShow = new Date();
1828
1829    // private - called when first menu is created
1830    function init(){
1831        menus = {};
1832        active = new Roo.util.MixedCollection();
1833        Roo.get(document).addKeyListener(27, function(){
1834            if(active.length > 0){
1835                hideAll();
1836            }
1837        });
1838    }
1839
1840    // private
1841    function hideAll(){
1842        if(active && active.length > 0){
1843            var c = active.clone();
1844            c.each(function(m){
1845                m.hide();
1846            });
1847        }
1848    }
1849
1850    // private
1851    function onHide(m){
1852        active.remove(m);
1853        if(active.length < 1){
1854            Roo.get(document).un("mouseup", onMouseDown);
1855             
1856            attached = false;
1857        }
1858    }
1859
1860    // private
1861    function onShow(m){
1862        var last = active.last();
1863        lastShow = new Date();
1864        active.add(m);
1865        if(!attached){
1866           Roo.get(document).on("mouseup", onMouseDown);
1867            
1868            attached = true;
1869        }
1870        if(m.parentMenu){
1871           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1872           m.parentMenu.activeChild = m;
1873        }else if(last && last.isVisible()){
1874           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1875        }
1876    }
1877
1878    // private
1879    function onBeforeHide(m){
1880        if(m.activeChild){
1881            m.activeChild.hide();
1882        }
1883        if(m.autoHideTimer){
1884            clearTimeout(m.autoHideTimer);
1885            delete m.autoHideTimer;
1886        }
1887    }
1888
1889    // private
1890    function onBeforeShow(m){
1891        var pm = m.parentMenu;
1892        if(!pm && !m.allowOtherMenus){
1893            hideAll();
1894        }else if(pm && pm.activeChild && active != m){
1895            pm.activeChild.hide();
1896        }
1897    }
1898
1899    // private this should really trigger on mouseup..
1900    function onMouseDown(e){
1901         Roo.log("on Mouse Up");
1902         
1903         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1904             Roo.log("MenuManager hideAll");
1905             hideAll();
1906             e.stopEvent();
1907         }
1908         
1909         
1910    }
1911
1912    // private
1913    function onBeforeCheck(mi, state){
1914        if(state){
1915            var g = groups[mi.group];
1916            for(var i = 0, l = g.length; i < l; i++){
1917                if(g[i] != mi){
1918                    g[i].setChecked(false);
1919                }
1920            }
1921        }
1922    }
1923
1924    return {
1925
1926        /**
1927         * Hides all menus that are currently visible
1928         */
1929        hideAll : function(){
1930             hideAll();  
1931        },
1932
1933        // private
1934        register : function(menu){
1935            if(!menus){
1936                init();
1937            }
1938            menus[menu.id] = menu;
1939            menu.on("beforehide", onBeforeHide);
1940            menu.on("hide", onHide);
1941            menu.on("beforeshow", onBeforeShow);
1942            menu.on("show", onShow);
1943            var g = menu.group;
1944            if(g && menu.events["checkchange"]){
1945                if(!groups[g]){
1946                    groups[g] = [];
1947                }
1948                groups[g].push(menu);
1949                menu.on("checkchange", onCheck);
1950            }
1951        },
1952
1953         /**
1954          * Returns a {@link Roo.menu.Menu} object
1955          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1956          * be used to generate and return a new Menu instance.
1957          */
1958        get : function(menu){
1959            if(typeof menu == "string"){ // menu id
1960                return menus[menu];
1961            }else if(menu.events){  // menu instance
1962                return menu;
1963            }
1964            /*else if(typeof menu.length == 'number'){ // array of menu items?
1965                return new Roo.bootstrap.Menu({items:menu});
1966            }else{ // otherwise, must be a config
1967                return new Roo.bootstrap.Menu(menu);
1968            }
1969            */
1970            return false;
1971        },
1972
1973        // private
1974        unregister : function(menu){
1975            delete menus[menu.id];
1976            menu.un("beforehide", onBeforeHide);
1977            menu.un("hide", onHide);
1978            menu.un("beforeshow", onBeforeShow);
1979            menu.un("show", onShow);
1980            var g = menu.group;
1981            if(g && menu.events["checkchange"]){
1982                groups[g].remove(menu);
1983                menu.un("checkchange", onCheck);
1984            }
1985        },
1986
1987        // private
1988        registerCheckable : function(menuItem){
1989            var g = menuItem.group;
1990            if(g){
1991                if(!groups[g]){
1992                    groups[g] = [];
1993                }
1994                groups[g].push(menuItem);
1995                menuItem.on("beforecheckchange", onBeforeCheck);
1996            }
1997        },
1998
1999        // private
2000        unregisterCheckable : function(menuItem){
2001            var g = menuItem.group;
2002            if(g){
2003                groups[g].remove(menuItem);
2004                menuItem.un("beforecheckchange", onBeforeCheck);
2005            }
2006        }
2007    };
2008 }();/*
2009  * - LGPL
2010  *
2011  * menu
2012  * 
2013  */
2014
2015 /**
2016  * @class Roo.bootstrap.Menu
2017  * @extends Roo.bootstrap.Component
2018  * Bootstrap Menu class - container for MenuItems
2019  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2020  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2021  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2022  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2023  * 
2024  * @constructor
2025  * Create a new Menu
2026  * @param {Object} config The config object
2027  */
2028
2029
2030 Roo.bootstrap.Menu = function(config){
2031     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2032     if (this.registerMenu && this.type != 'treeview')  {
2033         Roo.bootstrap.MenuMgr.register(this);
2034     }
2035     
2036     
2037     this.addEvents({
2038         /**
2039          * @event beforeshow
2040          * Fires before this menu is displayed
2041          * @param {Roo.menu.Menu} this
2042          */
2043         beforeshow : true,
2044         /**
2045          * @event beforehide
2046          * Fires before this menu is hidden
2047          * @param {Roo.menu.Menu} this
2048          */
2049         beforehide : true,
2050         /**
2051          * @event show
2052          * Fires after this menu is displayed
2053          * @param {Roo.menu.Menu} this
2054          */
2055         show : true,
2056         /**
2057          * @event hide
2058          * Fires after this menu is hidden
2059          * @param {Roo.menu.Menu} this
2060          */
2061         hide : true,
2062         /**
2063          * @event click
2064          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2065          * @param {Roo.menu.Menu} this
2066          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2067          * @param {Roo.EventObject} e
2068          */
2069         click : true,
2070         /**
2071          * @event mouseover
2072          * Fires when the mouse is hovering over this menu
2073          * @param {Roo.menu.Menu} this
2074          * @param {Roo.EventObject} e
2075          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2076          */
2077         mouseover : true,
2078         /**
2079          * @event mouseout
2080          * Fires when the mouse exits this menu
2081          * @param {Roo.menu.Menu} this
2082          * @param {Roo.EventObject} e
2083          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2084          */
2085         mouseout : true,
2086         /**
2087          * @event itemclick
2088          * Fires when a menu item contained in this menu is clicked
2089          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2090          * @param {Roo.EventObject} e
2091          */
2092         itemclick: true
2093     });
2094     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2095 };
2096
2097 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2098     
2099    /// html : false,
2100     //align : '',
2101     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2102     type: false,
2103     /**
2104      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2105      */
2106     registerMenu : true,
2107     
2108     menuItems :false, // stores the menu items..
2109     
2110     hidden:true,
2111         
2112     parentMenu : false,
2113     
2114     stopEvent : true,
2115     
2116     isLink : false,
2117     
2118     getChildContainer : function() {
2119         return this.el;  
2120     },
2121     
2122     getAutoCreate : function(){
2123          
2124         //if (['right'].indexOf(this.align)!==-1) {
2125         //    cfg.cn[1].cls += ' pull-right'
2126         //}
2127         
2128         
2129         var cfg = {
2130             tag : 'ul',
2131             cls : 'dropdown-menu' ,
2132             style : 'z-index:1000'
2133             
2134         };
2135         
2136         if (this.type === 'submenu') {
2137             cfg.cls = 'submenu active';
2138         }
2139         if (this.type === 'treeview') {
2140             cfg.cls = 'treeview-menu';
2141         }
2142         
2143         return cfg;
2144     },
2145     initEvents : function() {
2146         
2147        // Roo.log("ADD event");
2148        // Roo.log(this.triggerEl.dom);
2149         
2150         this.triggerEl.on('click', this.onTriggerClick, this);
2151         
2152         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2153         
2154         
2155         if (this.triggerEl.hasClass('nav-item')) {
2156             // dropdown toggle on the 'a' in BS4?
2157             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2158         } else {
2159             this.triggerEl.addClass('dropdown-toggle');
2160         }
2161         if (Roo.isTouch) {
2162             this.el.on('touchstart'  , this.onTouch, this);
2163         }
2164         this.el.on('click' , this.onClick, this);
2165
2166         this.el.on("mouseover", this.onMouseOver, this);
2167         this.el.on("mouseout", this.onMouseOut, this);
2168         
2169     },
2170     
2171     findTargetItem : function(e)
2172     {
2173         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2174         if(!t){
2175             return false;
2176         }
2177         //Roo.log(t);         Roo.log(t.id);
2178         if(t && t.id){
2179             //Roo.log(this.menuitems);
2180             return this.menuitems.get(t.id);
2181             
2182             //return this.items.get(t.menuItemId);
2183         }
2184         
2185         return false;
2186     },
2187     
2188     onTouch : function(e) 
2189     {
2190         Roo.log("menu.onTouch");
2191         //e.stopEvent(); this make the user popdown broken
2192         this.onClick(e);
2193     },
2194     
2195     onClick : function(e)
2196     {
2197         Roo.log("menu.onClick");
2198         
2199         var t = this.findTargetItem(e);
2200         if(!t || t.isContainer){
2201             return;
2202         }
2203         Roo.log(e);
2204         /*
2205         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2206             if(t == this.activeItem && t.shouldDeactivate(e)){
2207                 this.activeItem.deactivate();
2208                 delete this.activeItem;
2209                 return;
2210             }
2211             if(t.canActivate){
2212                 this.setActiveItem(t, true);
2213             }
2214             return;
2215             
2216             
2217         }
2218         */
2219        
2220         Roo.log('pass click event');
2221         
2222         t.onClick(e);
2223         
2224         this.fireEvent("click", this, t, e);
2225         
2226         var _this = this;
2227         
2228         if(!t.href.length || t.href == '#'){
2229             (function() { _this.hide(); }).defer(100);
2230         }
2231         
2232     },
2233     
2234     onMouseOver : function(e){
2235         var t  = this.findTargetItem(e);
2236         //Roo.log(t);
2237         //if(t){
2238         //    if(t.canActivate && !t.disabled){
2239         //        this.setActiveItem(t, true);
2240         //    }
2241         //}
2242         
2243         this.fireEvent("mouseover", this, e, t);
2244     },
2245     isVisible : function(){
2246         return !this.hidden;
2247     },
2248      onMouseOut : function(e){
2249         var t  = this.findTargetItem(e);
2250         
2251         //if(t ){
2252         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2253         //        this.activeItem.deactivate();
2254         //        delete this.activeItem;
2255         //    }
2256         //}
2257         this.fireEvent("mouseout", this, e, t);
2258     },
2259     
2260     
2261     /**
2262      * Displays this menu relative to another element
2263      * @param {String/HTMLElement/Roo.Element} element The element to align to
2264      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2265      * the element (defaults to this.defaultAlign)
2266      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2267      */
2268     show : function(el, pos, parentMenu){
2269         this.parentMenu = parentMenu;
2270         if(!this.el){
2271             this.render();
2272         }
2273         this.fireEvent("beforeshow", this);
2274         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2275     },
2276      /**
2277      * Displays this menu at a specific xy position
2278      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2279      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2280      */
2281     showAt : function(xy, parentMenu, /* private: */_e){
2282         this.parentMenu = parentMenu;
2283         if(!this.el){
2284             this.render();
2285         }
2286         if(_e !== false){
2287             this.fireEvent("beforeshow", this);
2288             //xy = this.el.adjustForConstraints(xy);
2289         }
2290         
2291         //this.el.show();
2292         this.hideMenuItems();
2293         this.hidden = false;
2294         this.triggerEl.addClass('open');
2295         this.el.addClass('show');
2296         
2297         // reassign x when hitting right
2298         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2299             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2300         }
2301         
2302         // reassign y when hitting bottom
2303         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2304             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2305         }
2306         
2307         // but the list may align on trigger left or trigger top... should it be a properity?
2308         
2309         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2310             this.el.setXY(xy);
2311         }
2312         
2313         this.focus();
2314         this.fireEvent("show", this);
2315     },
2316     
2317     focus : function(){
2318         return;
2319         if(!this.hidden){
2320             this.doFocus.defer(50, this);
2321         }
2322     },
2323
2324     doFocus : function(){
2325         if(!this.hidden){
2326             this.focusEl.focus();
2327         }
2328     },
2329
2330     /**
2331      * Hides this menu and optionally all parent menus
2332      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2333      */
2334     hide : function(deep)
2335     {
2336         
2337         this.hideMenuItems();
2338         if(this.el && this.isVisible()){
2339             this.fireEvent("beforehide", this);
2340             if(this.activeItem){
2341                 this.activeItem.deactivate();
2342                 this.activeItem = null;
2343             }
2344             this.triggerEl.removeClass('open');;
2345             this.el.removeClass('show');
2346             this.hidden = true;
2347             this.fireEvent("hide", this);
2348         }
2349         if(deep === true && this.parentMenu){
2350             this.parentMenu.hide(true);
2351         }
2352     },
2353     
2354     onTriggerClick : function(e)
2355     {
2356         Roo.log('trigger click');
2357         
2358         var target = e.getTarget();
2359         
2360         Roo.log(target.nodeName.toLowerCase());
2361         
2362         if(target.nodeName.toLowerCase() === 'i'){
2363             e.preventDefault();
2364         }
2365         
2366     },
2367     
2368     onTriggerPress  : function(e)
2369     {
2370         Roo.log('trigger press');
2371         //Roo.log(e.getTarget());
2372        // Roo.log(this.triggerEl.dom);
2373        
2374         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2375         var pel = Roo.get(e.getTarget());
2376         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2377             Roo.log('is treeview or dropdown?');
2378             return;
2379         }
2380         
2381         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2382             return;
2383         }
2384         
2385         if (this.isVisible()) {
2386             Roo.log('hide');
2387             this.hide();
2388         } else {
2389             Roo.log('show');
2390             this.show(this.triggerEl, false, false);
2391         }
2392         
2393         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2394             e.stopEvent();
2395         }
2396         
2397     },
2398        
2399     
2400     hideMenuItems : function()
2401     {
2402         Roo.log("hide Menu Items");
2403         if (!this.el) { 
2404             return;
2405         }
2406         //$(backdrop).remove()
2407         this.el.select('.open',true).each(function(aa) {
2408             
2409             aa.removeClass('open');
2410           //var parent = getParent($(this))
2411           //var relatedTarget = { relatedTarget: this }
2412           
2413            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2414           //if (e.isDefaultPrevented()) return
2415            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2416         });
2417     },
2418     addxtypeChild : function (tree, cntr) {
2419         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2420           
2421         this.menuitems.add(comp);
2422         return comp;
2423
2424     },
2425     getEl : function()
2426     {
2427         Roo.log(this.el);
2428         return this.el;
2429     },
2430     
2431     clear : function()
2432     {
2433         this.getEl().dom.innerHTML = '';
2434         this.menuitems.clear();
2435     }
2436 });
2437
2438  
2439  /*
2440  * - LGPL
2441  *
2442  * menu item
2443  * 
2444  */
2445
2446
2447 /**
2448  * @class Roo.bootstrap.MenuItem
2449  * @extends Roo.bootstrap.Component
2450  * Bootstrap MenuItem class
2451  * @cfg {String} html the menu label
2452  * @cfg {String} href the link
2453  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2454  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2455  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2456  * @cfg {String} fa favicon to show on left of menu item.
2457  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2458  * 
2459  * 
2460  * @constructor
2461  * Create a new MenuItem
2462  * @param {Object} config The config object
2463  */
2464
2465
2466 Roo.bootstrap.MenuItem = function(config){
2467     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2468     this.addEvents({
2469         // raw events
2470         /**
2471          * @event click
2472          * The raw click event for the entire grid.
2473          * @param {Roo.bootstrap.MenuItem} this
2474          * @param {Roo.EventObject} e
2475          */
2476         "click" : true
2477     });
2478 };
2479
2480 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2481     
2482     href : false,
2483     html : false,
2484     preventDefault: false,
2485     isContainer : false,
2486     active : false,
2487     fa: false,
2488     
2489     getAutoCreate : function(){
2490         
2491         if(this.isContainer){
2492             return {
2493                 tag: 'li',
2494                 cls: 'dropdown-menu-item dropdown-item'
2495             };
2496         }
2497         var ctag = {
2498             tag: 'span',
2499             html: 'Link'
2500         };
2501         
2502         var anc = {
2503             tag : 'a',
2504             href : '#',
2505             cn : [  ]
2506         };
2507         
2508         if (this.fa !== false) {
2509             anc.cn.push({
2510                 tag : 'i',
2511                 cls : 'fa fa-' + this.fa
2512             });
2513         }
2514         
2515         anc.cn.push(ctag);
2516         
2517         
2518         var cfg= {
2519             tag: 'li',
2520             cls: 'dropdown-menu-item dropdown-item',
2521             cn: [ anc ]
2522         };
2523         if (this.parent().type == 'treeview') {
2524             cfg.cls = 'treeview-menu';
2525         }
2526         if (this.active) {
2527             cfg.cls += ' active';
2528         }
2529         
2530         
2531         
2532         anc.href = this.href || cfg.cn[0].href ;
2533         ctag.html = this.html || cfg.cn[0].html ;
2534         return cfg;
2535     },
2536     
2537     initEvents: function()
2538     {
2539         if (this.parent().type == 'treeview') {
2540             this.el.select('a').on('click', this.onClick, this);
2541         }
2542         
2543         if (this.menu) {
2544             this.menu.parentType = this.xtype;
2545             this.menu.triggerEl = this.el;
2546             this.menu = this.addxtype(Roo.apply({}, this.menu));
2547         }
2548         
2549     },
2550     onClick : function(e)
2551     {
2552         Roo.log('item on click ');
2553         
2554         if(this.preventDefault){
2555             e.preventDefault();
2556         }
2557         //this.parent().hideMenuItems();
2558         
2559         this.fireEvent('click', this, e);
2560     },
2561     getEl : function()
2562     {
2563         return this.el;
2564     } 
2565 });
2566
2567  
2568
2569  /*
2570  * - LGPL
2571  *
2572  * menu separator
2573  * 
2574  */
2575
2576
2577 /**
2578  * @class Roo.bootstrap.MenuSeparator
2579  * @extends Roo.bootstrap.Component
2580  * Bootstrap MenuSeparator class
2581  * 
2582  * @constructor
2583  * Create a new MenuItem
2584  * @param {Object} config The config object
2585  */
2586
2587
2588 Roo.bootstrap.MenuSeparator = function(config){
2589     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2590 };
2591
2592 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2593     
2594     getAutoCreate : function(){
2595         var cfg = {
2596             cls: 'divider',
2597             tag : 'li'
2598         };
2599         
2600         return cfg;
2601     }
2602    
2603 });
2604
2605  
2606
2607  
2608 /*
2609 * Licence: LGPL
2610 */
2611
2612 /**
2613  * @class Roo.bootstrap.Modal
2614  * @extends Roo.bootstrap.Component
2615  * Bootstrap Modal class
2616  * @cfg {String} title Title of dialog
2617  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2618  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2619  * @cfg {Boolean} specificTitle default false
2620  * @cfg {Array} buttons Array of buttons or standard button set..
2621  * @cfg {String} buttonPosition (left|right|center) default right
2622  * @cfg {Boolean} animate default true
2623  * @cfg {Boolean} allow_close default true
2624  * @cfg {Boolean} fitwindow default false
2625  * @cfg {String} size (sm|lg) default empty
2626  * @cfg {Number} max_width set the max width of modal
2627  *
2628  *
2629  * @constructor
2630  * Create a new Modal Dialog
2631  * @param {Object} config The config object
2632  */
2633
2634 Roo.bootstrap.Modal = function(config){
2635     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2636     this.addEvents({
2637         // raw events
2638         /**
2639          * @event btnclick
2640          * The raw btnclick event for the button
2641          * @param {Roo.EventObject} e
2642          */
2643         "btnclick" : true,
2644         /**
2645          * @event resize
2646          * Fire when dialog resize
2647          * @param {Roo.bootstrap.Modal} this
2648          * @param {Roo.EventObject} e
2649          */
2650         "resize" : true
2651     });
2652     this.buttons = this.buttons || [];
2653
2654     if (this.tmpl) {
2655         this.tmpl = Roo.factory(this.tmpl);
2656     }
2657
2658 };
2659
2660 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2661
2662     title : 'test dialog',
2663
2664     buttons : false,
2665
2666     // set on load...
2667
2668     html: false,
2669
2670     tmp: false,
2671
2672     specificTitle: false,
2673
2674     buttonPosition: 'right',
2675
2676     allow_close : true,
2677
2678     animate : true,
2679
2680     fitwindow: false,
2681     
2682      // private
2683     dialogEl: false,
2684     bodyEl:  false,
2685     footerEl:  false,
2686     titleEl:  false,
2687     closeEl:  false,
2688
2689     size: '',
2690     
2691     max_width: 0,
2692     
2693     max_height: 0,
2694     
2695     fit_content: false,
2696
2697     onRender : function(ct, position)
2698     {
2699         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2700
2701         if(!this.el){
2702             var cfg = Roo.apply({},  this.getAutoCreate());
2703             cfg.id = Roo.id();
2704             //if(!cfg.name){
2705             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2706             //}
2707             //if (!cfg.name.length) {
2708             //    delete cfg.name;
2709            // }
2710             if (this.cls) {
2711                 cfg.cls += ' ' + this.cls;
2712             }
2713             if (this.style) {
2714                 cfg.style = this.style;
2715             }
2716             this.el = Roo.get(document.body).createChild(cfg, position);
2717         }
2718         //var type = this.el.dom.type;
2719
2720
2721         if(this.tabIndex !== undefined){
2722             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2723         }
2724
2725         this.dialogEl = this.el.select('.modal-dialog',true).first();
2726         this.bodyEl = this.el.select('.modal-body',true).first();
2727         this.closeEl = this.el.select('.modal-header .close', true).first();
2728         this.headerEl = this.el.select('.modal-header',true).first();
2729         this.titleEl = this.el.select('.modal-title',true).first();
2730         this.footerEl = this.el.select('.modal-footer',true).first();
2731
2732         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2733         
2734         //this.el.addClass("x-dlg-modal");
2735
2736         if (this.buttons.length) {
2737             Roo.each(this.buttons, function(bb) {
2738                 var b = Roo.apply({}, bb);
2739                 b.xns = b.xns || Roo.bootstrap;
2740                 b.xtype = b.xtype || 'Button';
2741                 if (typeof(b.listeners) == 'undefined') {
2742                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2743                 }
2744
2745                 var btn = Roo.factory(b);
2746
2747                 btn.render(this.el.select('.modal-footer div').first());
2748
2749             },this);
2750         }
2751         // render the children.
2752         var nitems = [];
2753
2754         if(typeof(this.items) != 'undefined'){
2755             var items = this.items;
2756             delete this.items;
2757
2758             for(var i =0;i < items.length;i++) {
2759                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2760             }
2761         }
2762
2763         this.items = nitems;
2764
2765         // where are these used - they used to be body/close/footer
2766
2767
2768         this.initEvents();
2769         //this.el.addClass([this.fieldClass, this.cls]);
2770
2771     },
2772
2773     getAutoCreate : function()
2774     {
2775         var bdy = {
2776                 cls : 'modal-body',
2777                 html : this.html || ''
2778         };
2779
2780         var title = {
2781             tag: 'h4',
2782             cls : 'modal-title',
2783             html : this.title
2784         };
2785
2786         if(this.specificTitle){
2787             title = this.title;
2788
2789         };
2790
2791         var header = [];
2792         if (this.allow_close && Roo.bootstrap.version == 3) {
2793             header.push({
2794                 tag: 'button',
2795                 cls : 'close',
2796                 html : '&times'
2797             });
2798         }
2799
2800         header.push(title);
2801
2802         if (this.allow_close && Roo.bootstrap.version == 4) {
2803             header.push({
2804                 tag: 'button',
2805                 cls : 'close',
2806                 html : '&times'
2807             });
2808         }
2809         
2810         var size = '';
2811
2812         if(this.size.length){
2813             size = 'modal-' + this.size;
2814         }
2815
2816         var modal = {
2817             cls: "modal",
2818              cn : [
2819                 {
2820                     cls: "modal-dialog " + size,
2821                     cn : [
2822                         {
2823                             cls : "modal-content",
2824                             cn : [
2825                                 {
2826                                     cls : 'modal-header',
2827                                     cn : header
2828                                 },
2829                                 bdy,
2830                                 {
2831                                     cls : 'modal-footer',
2832                                     cn : [
2833                                         {
2834                                             tag: 'div',
2835                                             cls: 'btn-' + this.buttonPosition
2836                                         }
2837                                     ]
2838
2839                                 }
2840
2841
2842                             ]
2843
2844                         }
2845                     ]
2846
2847                 }
2848             ]
2849         };
2850
2851         if(this.animate){
2852             modal.cls += ' fade';
2853         }
2854
2855         return modal;
2856
2857     },
2858     getChildContainer : function() {
2859
2860          return this.bodyEl;
2861
2862     },
2863     getButtonContainer : function() {
2864          return this.el.select('.modal-footer div',true).first();
2865
2866     },
2867     initEvents : function()
2868     {
2869         if (this.allow_close) {
2870             this.closeEl.on('click', this.hide, this);
2871         }
2872         Roo.EventManager.onWindowResize(this.resize, this, true);
2873
2874
2875     },
2876
2877     resize : function()
2878     {
2879         this.maskEl.setSize(
2880             Roo.lib.Dom.getViewWidth(true),
2881             Roo.lib.Dom.getViewHeight(true)
2882         );
2883         
2884         if (this.fitwindow) {
2885             this.setSize(
2886                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2887                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2888             );
2889             return;
2890         }
2891         
2892         if(this.max_width !== 0) {
2893             
2894             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2895             
2896             if(this.height) {
2897                 this.setSize(w, this.height);
2898                 return;
2899             }
2900             
2901             if(this.max_height) {
2902                 this.setSize(w,Math.min(
2903                     this.max_height,
2904                     Roo.lib.Dom.getViewportHeight(true) - 60
2905                 ));
2906                 
2907                 return;
2908             }
2909             
2910             if(!this.fit_content) {
2911                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2912                 return;
2913             }
2914             
2915             this.setSize(w, Math.min(
2916                 60 +
2917                 this.headerEl.getHeight() + 
2918                 this.footerEl.getHeight() + 
2919                 this.getChildHeight(this.bodyEl.dom.childNodes),
2920                 Roo.lib.Dom.getViewportHeight(true) - 60)
2921             );
2922         }
2923         
2924     },
2925
2926     setSize : function(w,h)
2927     {
2928         if (!w && !h) {
2929             return;
2930         }
2931         
2932         this.resizeTo(w,h);
2933     },
2934
2935     show : function() {
2936
2937         if (!this.rendered) {
2938             this.render();
2939         }
2940
2941         //this.el.setStyle('display', 'block');
2942         this.el.removeClass('hideing');
2943         this.el.dom.style.display='block';
2944         
2945         Roo.get(document.body).addClass('modal-open');
2946  
2947         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2948             var _this = this;
2949             (function(){
2950                 this.el.addClass('show');
2951                 this.el.addClass('in');
2952             }).defer(50, this);
2953         }else{
2954             this.el.addClass('show');
2955             this.el.addClass('in');
2956         }
2957
2958         // not sure how we can show data in here..
2959         //if (this.tmpl) {
2960         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2961         //}
2962
2963         Roo.get(document.body).addClass("x-body-masked");
2964         
2965         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2966         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2967         this.maskEl.dom.style.display = 'block';
2968         this.maskEl.addClass('show');
2969         
2970         
2971         this.resize();
2972         
2973         this.fireEvent('show', this);
2974
2975         // set zindex here - otherwise it appears to be ignored...
2976         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2977
2978         (function () {
2979             this.items.forEach( function(e) {
2980                 e.layout ? e.layout() : false;
2981
2982             });
2983         }).defer(100,this);
2984
2985     },
2986     hide : function()
2987     {
2988         if(this.fireEvent("beforehide", this) !== false){
2989             
2990             this.maskEl.removeClass('show');
2991             
2992             this.maskEl.dom.style.display = '';
2993             Roo.get(document.body).removeClass("x-body-masked");
2994             this.el.removeClass('in');
2995             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2996
2997             if(this.animate){ // why
2998                 this.el.addClass('hideing');
2999                 this.el.removeClass('show');
3000                 (function(){
3001                     if (!this.el.hasClass('hideing')) {
3002                         return; // it's been shown again...
3003                     }
3004                     
3005                     this.el.dom.style.display='';
3006
3007                     Roo.get(document.body).removeClass('modal-open');
3008                     this.el.removeClass('hideing');
3009                 }).defer(150,this);
3010                 
3011             }else{
3012                 this.el.removeClass('show');
3013                 this.el.dom.style.display='';
3014                 Roo.get(document.body).removeClass('modal-open');
3015
3016             }
3017             this.fireEvent('hide', this);
3018         }
3019     },
3020     isVisible : function()
3021     {
3022         
3023         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3024         
3025     },
3026
3027     addButton : function(str, cb)
3028     {
3029
3030
3031         var b = Roo.apply({}, { html : str } );
3032         b.xns = b.xns || Roo.bootstrap;
3033         b.xtype = b.xtype || 'Button';
3034         if (typeof(b.listeners) == 'undefined') {
3035             b.listeners = { click : cb.createDelegate(this)  };
3036         }
3037
3038         var btn = Roo.factory(b);
3039
3040         btn.render(this.el.select('.modal-footer div').first());
3041
3042         return btn;
3043
3044     },
3045
3046     setDefaultButton : function(btn)
3047     {
3048         //this.el.select('.modal-footer').()
3049     },
3050     diff : false,
3051
3052     resizeTo: function(w,h)
3053     {
3054         // skip.. ?? why??
3055
3056         this.dialogEl.setWidth(w);
3057         if (this.diff === false) {
3058             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3059         }
3060
3061         this.bodyEl.setHeight(h - this.diff);
3062
3063         this.fireEvent('resize', this);
3064
3065     },
3066     setContentSize  : function(w, h)
3067     {
3068
3069     },
3070     onButtonClick: function(btn,e)
3071     {
3072         //Roo.log([a,b,c]);
3073         this.fireEvent('btnclick', btn.name, e);
3074     },
3075      /**
3076      * Set the title of the Dialog
3077      * @param {String} str new Title
3078      */
3079     setTitle: function(str) {
3080         this.titleEl.dom.innerHTML = str;
3081     },
3082     /**
3083      * Set the body of the Dialog
3084      * @param {String} str new Title
3085      */
3086     setBody: function(str) {
3087         this.bodyEl.dom.innerHTML = str;
3088     },
3089     /**
3090      * Set the body of the Dialog using the template
3091      * @param {Obj} data - apply this data to the template and replace the body contents.
3092      */
3093     applyBody: function(obj)
3094     {
3095         if (!this.tmpl) {
3096             Roo.log("Error - using apply Body without a template");
3097             //code
3098         }
3099         this.tmpl.overwrite(this.bodyEl, obj);
3100     },
3101     
3102     getChildHeight : function(child_nodes)
3103     {
3104         if(
3105             !child_nodes ||
3106             child_nodes.length == 0
3107         ) {
3108             return;
3109         }
3110         
3111         var child_height = 0;
3112         
3113         for(var i = 0; i < child_nodes.length; i++) {
3114             
3115             /*
3116             * for modal with tabs...
3117             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3118                 
3119                 var layout_childs = child_nodes[i].childNodes;
3120                 
3121                 for(var j = 0; j < layout_childs.length; j++) {
3122                     
3123                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3124                         
3125                         var layout_body_childs = layout_childs[j].childNodes;
3126                         
3127                         for(var k = 0; k < layout_body_childs.length; k++) {
3128                             
3129                             if(layout_body_childs[k].classList.contains('navbar')) {
3130                                 child_height += layout_body_childs[k].offsetHeight;
3131                                 continue;
3132                             }
3133                             
3134                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3135                                 
3136                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3137                                 
3138                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3139                                     
3140                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3141                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3142                                         continue;
3143                                     }
3144                                     
3145                                 }
3146                                 
3147                             }
3148                             
3149                         }
3150                     }
3151                 }
3152                 continue;
3153             }
3154             */
3155             
3156             child_height += child_nodes[i].offsetHeight;
3157             // Roo.log(child_nodes[i].offsetHeight);
3158         }
3159         
3160         return child_height;
3161     }
3162
3163 });
3164
3165
3166 Roo.apply(Roo.bootstrap.Modal,  {
3167     /**
3168          * Button config that displays a single OK button
3169          * @type Object
3170          */
3171         OK :  [{
3172             name : 'ok',
3173             weight : 'primary',
3174             html : 'OK'
3175         }],
3176         /**
3177          * Button config that displays Yes and No buttons
3178          * @type Object
3179          */
3180         YESNO : [
3181             {
3182                 name  : 'no',
3183                 html : 'No'
3184             },
3185             {
3186                 name  :'yes',
3187                 weight : 'primary',
3188                 html : 'Yes'
3189             }
3190         ],
3191
3192         /**
3193          * Button config that displays OK and Cancel buttons
3194          * @type Object
3195          */
3196         OKCANCEL : [
3197             {
3198                name : 'cancel',
3199                 html : 'Cancel'
3200             },
3201             {
3202                 name : 'ok',
3203                 weight : 'primary',
3204                 html : 'OK'
3205             }
3206         ],
3207         /**
3208          * Button config that displays Yes, No and Cancel buttons
3209          * @type Object
3210          */
3211         YESNOCANCEL : [
3212             {
3213                 name : 'yes',
3214                 weight : 'primary',
3215                 html : 'Yes'
3216             },
3217             {
3218                 name : 'no',
3219                 html : 'No'
3220             },
3221             {
3222                 name : 'cancel',
3223                 html : 'Cancel'
3224             }
3225         ],
3226         
3227         zIndex : 10001
3228 });
3229 /*
3230  * - LGPL
3231  *
3232  * messagebox - can be used as a replace
3233  * 
3234  */
3235 /**
3236  * @class Roo.MessageBox
3237  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3238  * Example usage:
3239  *<pre><code>
3240 // Basic alert:
3241 Roo.Msg.alert('Status', 'Changes saved successfully.');
3242
3243 // Prompt for user data:
3244 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3245     if (btn == 'ok'){
3246         // process text value...
3247     }
3248 });
3249
3250 // Show a dialog using config options:
3251 Roo.Msg.show({
3252    title:'Save Changes?',
3253    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3254    buttons: Roo.Msg.YESNOCANCEL,
3255    fn: processResult,
3256    animEl: 'elId'
3257 });
3258 </code></pre>
3259  * @singleton
3260  */
3261 Roo.bootstrap.MessageBox = function(){
3262     var dlg, opt, mask, waitTimer;
3263     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3264     var buttons, activeTextEl, bwidth;
3265
3266     
3267     // private
3268     var handleButton = function(button){
3269         dlg.hide();
3270         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3271     };
3272
3273     // private
3274     var handleHide = function(){
3275         if(opt && opt.cls){
3276             dlg.el.removeClass(opt.cls);
3277         }
3278         //if(waitTimer){
3279         //    Roo.TaskMgr.stop(waitTimer);
3280         //    waitTimer = null;
3281         //}
3282     };
3283
3284     // private
3285     var updateButtons = function(b){
3286         var width = 0;
3287         if(!b){
3288             buttons["ok"].hide();
3289             buttons["cancel"].hide();
3290             buttons["yes"].hide();
3291             buttons["no"].hide();
3292             //dlg.footer.dom.style.display = 'none';
3293             return width;
3294         }
3295         dlg.footerEl.dom.style.display = '';
3296         for(var k in buttons){
3297             if(typeof buttons[k] != "function"){
3298                 if(b[k]){
3299                     buttons[k].show();
3300                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3301                     width += buttons[k].el.getWidth()+15;
3302                 }else{
3303                     buttons[k].hide();
3304                 }
3305             }
3306         }
3307         return width;
3308     };
3309
3310     // private
3311     var handleEsc = function(d, k, e){
3312         if(opt && opt.closable !== false){
3313             dlg.hide();
3314         }
3315         if(e){
3316             e.stopEvent();
3317         }
3318     };
3319
3320     return {
3321         /**
3322          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3323          * @return {Roo.BasicDialog} The BasicDialog element
3324          */
3325         getDialog : function(){
3326            if(!dlg){
3327                 dlg = new Roo.bootstrap.Modal( {
3328                     //draggable: true,
3329                     //resizable:false,
3330                     //constraintoviewport:false,
3331                     //fixedcenter:true,
3332                     //collapsible : false,
3333                     //shim:true,
3334                     //modal: true,
3335                 //    width: 'auto',
3336                   //  height:100,
3337                     //buttonAlign:"center",
3338                     closeClick : function(){
3339                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3340                             handleButton("no");
3341                         }else{
3342                             handleButton("cancel");
3343                         }
3344                     }
3345                 });
3346                 dlg.render();
3347                 dlg.on("hide", handleHide);
3348                 mask = dlg.mask;
3349                 //dlg.addKeyListener(27, handleEsc);
3350                 buttons = {};
3351                 this.buttons = buttons;
3352                 var bt = this.buttonText;
3353                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3354                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3355                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3356                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3357                 //Roo.log(buttons);
3358                 bodyEl = dlg.bodyEl.createChild({
3359
3360                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3361                         '<textarea class="roo-mb-textarea"></textarea>' +
3362                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3363                 });
3364                 msgEl = bodyEl.dom.firstChild;
3365                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3366                 textboxEl.enableDisplayMode();
3367                 textboxEl.addKeyListener([10,13], function(){
3368                     if(dlg.isVisible() && opt && opt.buttons){
3369                         if(opt.buttons.ok){
3370                             handleButton("ok");
3371                         }else if(opt.buttons.yes){
3372                             handleButton("yes");
3373                         }
3374                     }
3375                 });
3376                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3377                 textareaEl.enableDisplayMode();
3378                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3379                 progressEl.enableDisplayMode();
3380                 
3381                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3382                 var pf = progressEl.dom.firstChild;
3383                 if (pf) {
3384                     pp = Roo.get(pf.firstChild);
3385                     pp.setHeight(pf.offsetHeight);
3386                 }
3387                 
3388             }
3389             return dlg;
3390         },
3391
3392         /**
3393          * Updates the message box body text
3394          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3395          * the XHTML-compliant non-breaking space character '&amp;#160;')
3396          * @return {Roo.MessageBox} This message box
3397          */
3398         updateText : function(text)
3399         {
3400             if(!dlg.isVisible() && !opt.width){
3401                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3402                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3403             }
3404             msgEl.innerHTML = text || '&#160;';
3405       
3406             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3407             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3408             var w = Math.max(
3409                     Math.min(opt.width || cw , this.maxWidth), 
3410                     Math.max(opt.minWidth || this.minWidth, bwidth)
3411             );
3412             if(opt.prompt){
3413                 activeTextEl.setWidth(w);
3414             }
3415             if(dlg.isVisible()){
3416                 dlg.fixedcenter = false;
3417             }
3418             // to big, make it scroll. = But as usual stupid IE does not support
3419             // !important..
3420             
3421             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3422                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3423                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3424             } else {
3425                 bodyEl.dom.style.height = '';
3426                 bodyEl.dom.style.overflowY = '';
3427             }
3428             if (cw > w) {
3429                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3430             } else {
3431                 bodyEl.dom.style.overflowX = '';
3432             }
3433             
3434             dlg.setContentSize(w, bodyEl.getHeight());
3435             if(dlg.isVisible()){
3436                 dlg.fixedcenter = true;
3437             }
3438             return this;
3439         },
3440
3441         /**
3442          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3443          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3444          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3445          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3446          * @return {Roo.MessageBox} This message box
3447          */
3448         updateProgress : function(value, text){
3449             if(text){
3450                 this.updateText(text);
3451             }
3452             
3453             if (pp) { // weird bug on my firefox - for some reason this is not defined
3454                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3455                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3456             }
3457             return this;
3458         },        
3459
3460         /**
3461          * Returns true if the message box is currently displayed
3462          * @return {Boolean} True if the message box is visible, else false
3463          */
3464         isVisible : function(){
3465             return dlg && dlg.isVisible();  
3466         },
3467
3468         /**
3469          * Hides the message box if it is displayed
3470          */
3471         hide : function(){
3472             if(this.isVisible()){
3473                 dlg.hide();
3474             }  
3475         },
3476
3477         /**
3478          * Displays a new message box, or reinitializes an existing message box, based on the config options
3479          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3480          * The following config object properties are supported:
3481          * <pre>
3482 Property    Type             Description
3483 ----------  ---------------  ------------------------------------------------------------------------------------
3484 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3485                                    closes (defaults to undefined)
3486 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3487                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3488 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3489                                    progress and wait dialogs will ignore this property and always hide the
3490                                    close button as they can only be closed programmatically.
3491 cls               String           A custom CSS class to apply to the message box element
3492 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3493                                    displayed (defaults to 75)
3494 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3495                                    function will be btn (the name of the button that was clicked, if applicable,
3496                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3497                                    Progress and wait dialogs will ignore this option since they do not respond to
3498                                    user actions and can only be closed programmatically, so any required function
3499                                    should be called by the same code after it closes the dialog.
3500 icon              String           A CSS class that provides a background image to be used as an icon for
3501                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3502 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3503 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3504 modal             Boolean          False to allow user interaction with the page while the message box is
3505                                    displayed (defaults to true)
3506 msg               String           A string that will replace the existing message box body text (defaults
3507                                    to the XHTML-compliant non-breaking space character '&#160;')
3508 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3509 progress          Boolean          True to display a progress bar (defaults to false)
3510 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3511 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3512 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3513 title             String           The title text
3514 value             String           The string value to set into the active textbox element if displayed
3515 wait              Boolean          True to display a progress bar (defaults to false)
3516 width             Number           The width of the dialog in pixels
3517 </pre>
3518          *
3519          * Example usage:
3520          * <pre><code>
3521 Roo.Msg.show({
3522    title: 'Address',
3523    msg: 'Please enter your address:',
3524    width: 300,
3525    buttons: Roo.MessageBox.OKCANCEL,
3526    multiline: true,
3527    fn: saveAddress,
3528    animEl: 'addAddressBtn'
3529 });
3530 </code></pre>
3531          * @param {Object} config Configuration options
3532          * @return {Roo.MessageBox} This message box
3533          */
3534         show : function(options)
3535         {
3536             
3537             // this causes nightmares if you show one dialog after another
3538             // especially on callbacks..
3539              
3540             if(this.isVisible()){
3541                 
3542                 this.hide();
3543                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3544                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3545                 Roo.log("New Dialog Message:" +  options.msg )
3546                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3547                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3548                 
3549             }
3550             var d = this.getDialog();
3551             opt = options;
3552             d.setTitle(opt.title || "&#160;");
3553             d.closeEl.setDisplayed(opt.closable !== false);
3554             activeTextEl = textboxEl;
3555             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3556             if(opt.prompt){
3557                 if(opt.multiline){
3558                     textboxEl.hide();
3559                     textareaEl.show();
3560                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3561                         opt.multiline : this.defaultTextHeight);
3562                     activeTextEl = textareaEl;
3563                 }else{
3564                     textboxEl.show();
3565                     textareaEl.hide();
3566                 }
3567             }else{
3568                 textboxEl.hide();
3569                 textareaEl.hide();
3570             }
3571             progressEl.setDisplayed(opt.progress === true);
3572             this.updateProgress(0);
3573             activeTextEl.dom.value = opt.value || "";
3574             if(opt.prompt){
3575                 dlg.setDefaultButton(activeTextEl);
3576             }else{
3577                 var bs = opt.buttons;
3578                 var db = null;
3579                 if(bs && bs.ok){
3580                     db = buttons["ok"];
3581                 }else if(bs && bs.yes){
3582                     db = buttons["yes"];
3583                 }
3584                 dlg.setDefaultButton(db);
3585             }
3586             bwidth = updateButtons(opt.buttons);
3587             this.updateText(opt.msg);
3588             if(opt.cls){
3589                 d.el.addClass(opt.cls);
3590             }
3591             d.proxyDrag = opt.proxyDrag === true;
3592             d.modal = opt.modal !== false;
3593             d.mask = opt.modal !== false ? mask : false;
3594             if(!d.isVisible()){
3595                 // force it to the end of the z-index stack so it gets a cursor in FF
3596                 document.body.appendChild(dlg.el.dom);
3597                 d.animateTarget = null;
3598                 d.show(options.animEl);
3599             }
3600             return this;
3601         },
3602
3603         /**
3604          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3605          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3606          * and closing the message box when the process is complete.
3607          * @param {String} title The title bar text
3608          * @param {String} msg The message box body text
3609          * @return {Roo.MessageBox} This message box
3610          */
3611         progress : function(title, msg){
3612             this.show({
3613                 title : title,
3614                 msg : msg,
3615                 buttons: false,
3616                 progress:true,
3617                 closable:false,
3618                 minWidth: this.minProgressWidth,
3619                 modal : true
3620             });
3621             return this;
3622         },
3623
3624         /**
3625          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3626          * If a callback function is passed it will be called after the user clicks the button, and the
3627          * id of the button that was clicked will be passed as the only parameter to the callback
3628          * (could also be the top-right close button).
3629          * @param {String} title The title bar text
3630          * @param {String} msg The message box body text
3631          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3632          * @param {Object} scope (optional) The scope of the callback function
3633          * @return {Roo.MessageBox} This message box
3634          */
3635         alert : function(title, msg, fn, scope)
3636         {
3637             this.show({
3638                 title : title,
3639                 msg : msg,
3640                 buttons: this.OK,
3641                 fn: fn,
3642                 closable : false,
3643                 scope : scope,
3644                 modal : true
3645             });
3646             return this;
3647         },
3648
3649         /**
3650          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3651          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3652          * You are responsible for closing the message box when the process is complete.
3653          * @param {String} msg The message box body text
3654          * @param {String} title (optional) The title bar text
3655          * @return {Roo.MessageBox} This message box
3656          */
3657         wait : function(msg, title){
3658             this.show({
3659                 title : title,
3660                 msg : msg,
3661                 buttons: false,
3662                 closable:false,
3663                 progress:true,
3664                 modal:true,
3665                 width:300,
3666                 wait:true
3667             });
3668             waitTimer = Roo.TaskMgr.start({
3669                 run: function(i){
3670                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3671                 },
3672                 interval: 1000
3673             });
3674             return this;
3675         },
3676
3677         /**
3678          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3679          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3680          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3681          * @param {String} title The title bar text
3682          * @param {String} msg The message box body text
3683          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3684          * @param {Object} scope (optional) The scope of the callback function
3685          * @return {Roo.MessageBox} This message box
3686          */
3687         confirm : function(title, msg, fn, scope){
3688             this.show({
3689                 title : title,
3690                 msg : msg,
3691                 buttons: this.YESNO,
3692                 fn: fn,
3693                 scope : scope,
3694                 modal : true
3695             });
3696             return this;
3697         },
3698
3699         /**
3700          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3701          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3702          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3703          * (could also be the top-right close button) and the text that was entered will be passed as the two
3704          * parameters to the callback.
3705          * @param {String} title The title bar text
3706          * @param {String} msg The message box body text
3707          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3708          * @param {Object} scope (optional) The scope of the callback function
3709          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3710          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3711          * @return {Roo.MessageBox} This message box
3712          */
3713         prompt : function(title, msg, fn, scope, multiline){
3714             this.show({
3715                 title : title,
3716                 msg : msg,
3717                 buttons: this.OKCANCEL,
3718                 fn: fn,
3719                 minWidth:250,
3720                 scope : scope,
3721                 prompt:true,
3722                 multiline: multiline,
3723                 modal : true
3724             });
3725             return this;
3726         },
3727
3728         /**
3729          * Button config that displays a single OK button
3730          * @type Object
3731          */
3732         OK : {ok:true},
3733         /**
3734          * Button config that displays Yes and No buttons
3735          * @type Object
3736          */
3737         YESNO : {yes:true, no:true},
3738         /**
3739          * Button config that displays OK and Cancel buttons
3740          * @type Object
3741          */
3742         OKCANCEL : {ok:true, cancel:true},
3743         /**
3744          * Button config that displays Yes, No and Cancel buttons
3745          * @type Object
3746          */
3747         YESNOCANCEL : {yes:true, no:true, cancel:true},
3748
3749         /**
3750          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3751          * @type Number
3752          */
3753         defaultTextHeight : 75,
3754         /**
3755          * The maximum width in pixels of the message box (defaults to 600)
3756          * @type Number
3757          */
3758         maxWidth : 600,
3759         /**
3760          * The minimum width in pixels of the message box (defaults to 100)
3761          * @type Number
3762          */
3763         minWidth : 100,
3764         /**
3765          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3766          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3767          * @type Number
3768          */
3769         minProgressWidth : 250,
3770         /**
3771          * An object containing the default button text strings that can be overriden for localized language support.
3772          * Supported properties are: ok, cancel, yes and no.
3773          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3774          * @type Object
3775          */
3776         buttonText : {
3777             ok : "OK",
3778             cancel : "Cancel",
3779             yes : "Yes",
3780             no : "No"
3781         }
3782     };
3783 }();
3784
3785 /**
3786  * Shorthand for {@link Roo.MessageBox}
3787  */
3788 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3789 Roo.Msg = Roo.Msg || Roo.MessageBox;
3790 /*
3791  * - LGPL
3792  *
3793  * navbar
3794  * 
3795  */
3796
3797 /**
3798  * @class Roo.bootstrap.Navbar
3799  * @extends Roo.bootstrap.Component
3800  * Bootstrap Navbar class
3801
3802  * @constructor
3803  * Create a new Navbar
3804  * @param {Object} config The config object
3805  */
3806
3807
3808 Roo.bootstrap.Navbar = function(config){
3809     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3810     this.addEvents({
3811         // raw events
3812         /**
3813          * @event beforetoggle
3814          * Fire before toggle the menu
3815          * @param {Roo.EventObject} e
3816          */
3817         "beforetoggle" : true
3818     });
3819 };
3820
3821 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3822     
3823     
3824    
3825     // private
3826     navItems : false,
3827     loadMask : false,
3828     
3829     
3830     getAutoCreate : function(){
3831         
3832         
3833         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3834         
3835     },
3836     
3837     initEvents :function ()
3838     {
3839         //Roo.log(this.el.select('.navbar-toggle',true));
3840         this.el.select('.navbar-toggle',true).on('click', function() {
3841             if(this.fireEvent('beforetoggle', this) !== false){
3842                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3843             }
3844             
3845         }, this);
3846         
3847         var mark = {
3848             tag: "div",
3849             cls:"x-dlg-mask"
3850         };
3851         
3852         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3853         
3854         var size = this.el.getSize();
3855         this.maskEl.setSize(size.width, size.height);
3856         this.maskEl.enableDisplayMode("block");
3857         this.maskEl.hide();
3858         
3859         if(this.loadMask){
3860             this.maskEl.show();
3861         }
3862     },
3863     
3864     
3865     getChildContainer : function()
3866     {
3867         if (this.el.select('.collapse').getCount()) {
3868             return this.el.select('.collapse',true).first();
3869         }
3870         
3871         return this.el;
3872     },
3873     
3874     mask : function()
3875     {
3876         this.maskEl.show();
3877     },
3878     
3879     unmask : function()
3880     {
3881         this.maskEl.hide();
3882     } 
3883     
3884     
3885     
3886     
3887 });
3888
3889
3890
3891  
3892
3893  /*
3894  * - LGPL
3895  *
3896  * navbar
3897  * 
3898  */
3899
3900 /**
3901  * @class Roo.bootstrap.NavSimplebar
3902  * @extends Roo.bootstrap.Navbar
3903  * Bootstrap Sidebar class
3904  *
3905  * @cfg {Boolean} inverse is inverted color
3906  * 
3907  * @cfg {String} type (nav | pills | tabs)
3908  * @cfg {Boolean} arrangement stacked | justified
3909  * @cfg {String} align (left | right) alignment
3910  * 
3911  * @cfg {Boolean} main (true|false) main nav bar? default false
3912  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3913  * 
3914  * @cfg {String} tag (header|footer|nav|div) default is nav 
3915
3916  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3917  * 
3918  * 
3919  * @constructor
3920  * Create a new Sidebar
3921  * @param {Object} config The config object
3922  */
3923
3924
3925 Roo.bootstrap.NavSimplebar = function(config){
3926     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3927 };
3928
3929 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3930     
3931     inverse: false,
3932     
3933     type: false,
3934     arrangement: '',
3935     align : false,
3936     
3937     weight : 'light',
3938     
3939     main : false,
3940     
3941     
3942     tag : false,
3943     
3944     
3945     getAutoCreate : function(){
3946         
3947         
3948         var cfg = {
3949             tag : this.tag || 'div',
3950             cls : 'navbar navbar-expand-lg'
3951         };
3952         if (['light','white'].indexOf(this.weight) > -1) {
3953             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
3954         }
3955         cfg.cls += ' bg-' + this.weight;
3956         
3957           
3958         
3959         cfg.cn = [
3960             {
3961                 cls: 'nav',
3962                 tag : 'ul'
3963             }
3964         ];
3965         
3966          
3967         this.type = this.type || 'nav';
3968         if (['tabs','pills'].indexOf(this.type)!==-1) {
3969             cfg.cn[0].cls += ' nav-' + this.type
3970         
3971         
3972         } else {
3973             if (this.type!=='nav') {
3974                 Roo.log('nav type must be nav/tabs/pills')
3975             }
3976             cfg.cn[0].cls += ' navbar-nav'
3977         }
3978         
3979         
3980         
3981         
3982         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3983             cfg.cn[0].cls += ' nav-' + this.arrangement;
3984         }
3985         
3986         
3987         if (this.align === 'right') {
3988             cfg.cn[0].cls += ' navbar-right';
3989         }
3990         
3991         if (this.inverse) {
3992             cfg.cls += ' navbar-inverse';
3993             
3994         }
3995         
3996         
3997         return cfg;
3998     
3999         
4000     }
4001     
4002     
4003     
4004 });
4005
4006
4007
4008  
4009
4010  
4011        /*
4012  * - LGPL
4013  *
4014  * navbar
4015  * navbar-fixed-top
4016  * navbar-expand-md  fixed-top 
4017  */
4018
4019 /**
4020  * @class Roo.bootstrap.NavHeaderbar
4021  * @extends Roo.bootstrap.NavSimplebar
4022  * Bootstrap Sidebar class
4023  *
4024  * @cfg {String} brand what is brand
4025  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4026  * @cfg {String} brand_href href of the brand
4027  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4028  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4029  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4030  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4031  * 
4032  * @constructor
4033  * Create a new Sidebar
4034  * @param {Object} config The config object
4035  */
4036
4037
4038 Roo.bootstrap.NavHeaderbar = function(config){
4039     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4040       
4041 };
4042
4043 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4044     
4045     position: '',
4046     brand: '',
4047     brand_href: false,
4048     srButton : true,
4049     autohide : false,
4050     desktopCenter : false,
4051    
4052     
4053     getAutoCreate : function(){
4054         
4055         var   cfg = {
4056             tag: this.nav || 'nav',
4057             cls: 'navbar navbar-expand-md',
4058             role: 'navigation',
4059             cn: []
4060         };
4061         
4062         var cn = cfg.cn;
4063         if (this.desktopCenter) {
4064             cn.push({cls : 'container', cn : []});
4065             cn = cn[0].cn;
4066         }
4067         
4068         if(this.srButton){
4069             cn.push({
4070                 tag: 'div',
4071                 cls: 'navbar-header',
4072                 cn: [
4073                     {
4074                         tag: 'button',
4075                         type: 'button',
4076                         cls: 'navbar-toggle navbar-toggler',
4077                         'data-toggle': 'collapse',
4078                         cn: [
4079                             {
4080                                 tag: 'span',
4081                                 cls: 'sr-only',
4082                                 html: 'Toggle navigation'
4083                             },
4084                             {
4085                                 tag: 'span',
4086                                 cls: 'icon-bar navbar-toggler-icon'
4087                             },
4088                             {
4089                                 tag: 'span',
4090                                 cls: 'icon-bar'
4091                             },
4092                             {
4093                                 tag: 'span',
4094                                 cls: 'icon-bar'
4095                             }
4096                         ]
4097                     }
4098                 ]
4099             });
4100         }
4101         
4102         cn.push({
4103             tag: 'div',
4104             cls: 'collapse navbar-collapse',
4105             cn : []
4106         });
4107         
4108         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4109         
4110         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4111             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4112             
4113             // tag can override this..
4114             
4115             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4116         }
4117         
4118         if (this.brand !== '') {
4119             cn[0].cn.push({
4120                 tag: 'a',
4121                 href: this.brand_href ? this.brand_href : '#',
4122                 cls: 'navbar-brand',
4123                 cn: [
4124                 this.brand
4125                 ]
4126             });
4127         }
4128         
4129         if(this.main){
4130             cfg.cls += ' main-nav';
4131         }
4132         
4133         
4134         return cfg;
4135
4136         
4137     },
4138     getHeaderChildContainer : function()
4139     {
4140         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4141             return this.el.select('.navbar-header',true).first();
4142         }
4143         
4144         return this.getChildContainer();
4145     },
4146     
4147     
4148     initEvents : function()
4149     {
4150         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4151         
4152         if (this.autohide) {
4153             
4154             var prevScroll = 0;
4155             var ft = this.el;
4156             
4157             Roo.get(document).on('scroll',function(e) {
4158                 var ns = Roo.get(document).getScroll().top;
4159                 var os = prevScroll;
4160                 prevScroll = ns;
4161                 
4162                 if(ns > os){
4163                     ft.removeClass('slideDown');
4164                     ft.addClass('slideUp');
4165                     return;
4166                 }
4167                 ft.removeClass('slideUp');
4168                 ft.addClass('slideDown');
4169                  
4170               
4171           },this);
4172         }
4173     }    
4174     
4175 });
4176
4177
4178
4179  
4180
4181  /*
4182  * - LGPL
4183  *
4184  * navbar
4185  * 
4186  */
4187
4188 /**
4189  * @class Roo.bootstrap.NavSidebar
4190  * @extends Roo.bootstrap.Navbar
4191  * Bootstrap Sidebar class
4192  * 
4193  * @constructor
4194  * Create a new Sidebar
4195  * @param {Object} config The config object
4196  */
4197
4198
4199 Roo.bootstrap.NavSidebar = function(config){
4200     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4201 };
4202
4203 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4204     
4205     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4206     
4207     getAutoCreate : function(){
4208         
4209         
4210         return  {
4211             tag: 'div',
4212             cls: 'sidebar sidebar-nav'
4213         };
4214     
4215         
4216     }
4217     
4218     
4219     
4220 });
4221
4222
4223
4224  
4225
4226  /*
4227  * - LGPL
4228  *
4229  * nav group
4230  * 
4231  */
4232
4233 /**
4234  * @class Roo.bootstrap.NavGroup
4235  * @extends Roo.bootstrap.Component
4236  * Bootstrap NavGroup class
4237  * @cfg {String} align (left|right)
4238  * @cfg {Boolean} inverse
4239  * @cfg {String} type (nav|pills|tab) default nav
4240  * @cfg {String} navId - reference Id for navbar.
4241
4242  * 
4243  * @constructor
4244  * Create a new nav group
4245  * @param {Object} config The config object
4246  */
4247
4248 Roo.bootstrap.NavGroup = function(config){
4249     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4250     this.navItems = [];
4251    
4252     Roo.bootstrap.NavGroup.register(this);
4253      this.addEvents({
4254         /**
4255              * @event changed
4256              * Fires when the active item changes
4257              * @param {Roo.bootstrap.NavGroup} this
4258              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4259              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4260          */
4261         'changed': true
4262      });
4263     
4264 };
4265
4266 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4267     
4268     align: '',
4269     inverse: false,
4270     form: false,
4271     type: 'nav',
4272     navId : '',
4273     // private
4274     
4275     navItems : false, 
4276     
4277     getAutoCreate : function()
4278     {
4279         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4280         
4281         cfg = {
4282             tag : 'ul',
4283             cls: 'nav' 
4284         };
4285         
4286         if (['tabs','pills'].indexOf(this.type)!==-1) {
4287             cfg.cls += ' nav-' + this.type
4288         } else {
4289             if (this.type!=='nav') {
4290                 Roo.log('nav type must be nav/tabs/pills')
4291             }
4292             cfg.cls += ' navbar-nav'
4293         }
4294         
4295         if (this.parent() && this.parent().sidebar) {
4296             cfg = {
4297                 tag: 'ul',
4298                 cls: 'dashboard-menu sidebar-menu'
4299             };
4300             
4301             return cfg;
4302         }
4303         
4304         if (this.form === true) {
4305             cfg = {
4306                 tag: 'form',
4307                 cls: 'navbar-form'
4308             };
4309             
4310             if (this.align === 'right') {
4311                 cfg.cls += ' navbar-right ml-md-auto';
4312             } else {
4313                 cfg.cls += ' navbar-left';
4314             }
4315         }
4316         
4317         if (this.align === 'right') {
4318             cfg.cls += ' navbar-right ml-md-auto';
4319         } else {
4320             cfg.cls += ' mr-auto';
4321         }
4322         
4323         if (this.inverse) {
4324             cfg.cls += ' navbar-inverse';
4325             
4326         }
4327         
4328         
4329         return cfg;
4330     },
4331     /**
4332     * sets the active Navigation item
4333     * @param {Roo.bootstrap.NavItem} the new current navitem
4334     */
4335     setActiveItem : function(item)
4336     {
4337         var prev = false;
4338         Roo.each(this.navItems, function(v){
4339             if (v == item) {
4340                 return ;
4341             }
4342             if (v.isActive()) {
4343                 v.setActive(false, true);
4344                 prev = v;
4345                 
4346             }
4347             
4348         });
4349
4350         item.setActive(true, true);
4351         this.fireEvent('changed', this, item, prev);
4352         
4353         
4354     },
4355     /**
4356     * gets the active Navigation item
4357     * @return {Roo.bootstrap.NavItem} the current navitem
4358     */
4359     getActive : function()
4360     {
4361         
4362         var prev = false;
4363         Roo.each(this.navItems, function(v){
4364             
4365             if (v.isActive()) {
4366                 prev = v;
4367                 
4368             }
4369             
4370         });
4371         return prev;
4372     },
4373     
4374     indexOfNav : function()
4375     {
4376         
4377         var prev = false;
4378         Roo.each(this.navItems, function(v,i){
4379             
4380             if (v.isActive()) {
4381                 prev = i;
4382                 
4383             }
4384             
4385         });
4386         return prev;
4387     },
4388     /**
4389     * adds a Navigation item
4390     * @param {Roo.bootstrap.NavItem} the navitem to add
4391     */
4392     addItem : function(cfg)
4393     {
4394         var cn = new Roo.bootstrap.NavItem(cfg);
4395         this.register(cn);
4396         cn.parentId = this.id;
4397         cn.onRender(this.el, null);
4398         return cn;
4399     },
4400     /**
4401     * register a Navigation item
4402     * @param {Roo.bootstrap.NavItem} the navitem to add
4403     */
4404     register : function(item)
4405     {
4406         this.navItems.push( item);
4407         item.navId = this.navId;
4408     
4409     },
4410     
4411     /**
4412     * clear all the Navigation item
4413     */
4414    
4415     clearAll : function()
4416     {
4417         this.navItems = [];
4418         this.el.dom.innerHTML = '';
4419     },
4420     
4421     getNavItem: function(tabId)
4422     {
4423         var ret = false;
4424         Roo.each(this.navItems, function(e) {
4425             if (e.tabId == tabId) {
4426                ret =  e;
4427                return false;
4428             }
4429             return true;
4430             
4431         });
4432         return ret;
4433     },
4434     
4435     setActiveNext : function()
4436     {
4437         var i = this.indexOfNav(this.getActive());
4438         if (i > this.navItems.length) {
4439             return;
4440         }
4441         this.setActiveItem(this.navItems[i+1]);
4442     },
4443     setActivePrev : function()
4444     {
4445         var i = this.indexOfNav(this.getActive());
4446         if (i  < 1) {
4447             return;
4448         }
4449         this.setActiveItem(this.navItems[i-1]);
4450     },
4451     clearWasActive : function(except) {
4452         Roo.each(this.navItems, function(e) {
4453             if (e.tabId != except.tabId && e.was_active) {
4454                e.was_active = false;
4455                return false;
4456             }
4457             return true;
4458             
4459         });
4460     },
4461     getWasActive : function ()
4462     {
4463         var r = false;
4464         Roo.each(this.navItems, function(e) {
4465             if (e.was_active) {
4466                r = e;
4467                return false;
4468             }
4469             return true;
4470             
4471         });
4472         return r;
4473     }
4474     
4475     
4476 });
4477
4478  
4479 Roo.apply(Roo.bootstrap.NavGroup, {
4480     
4481     groups: {},
4482      /**
4483     * register a Navigation Group
4484     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4485     */
4486     register : function(navgrp)
4487     {
4488         this.groups[navgrp.navId] = navgrp;
4489         
4490     },
4491     /**
4492     * fetch a Navigation Group based on the navigation ID
4493     * @param {string} the navgroup to add
4494     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4495     */
4496     get: function(navId) {
4497         if (typeof(this.groups[navId]) == 'undefined') {
4498             return false;
4499             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4500         }
4501         return this.groups[navId] ;
4502     }
4503     
4504     
4505     
4506 });
4507
4508  /*
4509  * - LGPL
4510  *
4511  * row
4512  * 
4513  */
4514
4515 /**
4516  * @class Roo.bootstrap.NavItem
4517  * @extends Roo.bootstrap.Component
4518  * Bootstrap Navbar.NavItem class
4519  * @cfg {String} href  link to
4520  * @cfg {String} html content of button
4521  * @cfg {String} badge text inside badge
4522  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4523  * @cfg {String} glyphicon name of glyphicon
4524  * @cfg {String} icon name of font awesome icon
4525  * @cfg {Boolean} active Is item active
4526  * @cfg {Boolean} disabled Is item disabled
4527  
4528  * @cfg {Boolean} preventDefault (true | false) default false
4529  * @cfg {String} tabId the tab that this item activates.
4530  * @cfg {String} tagtype (a|span) render as a href or span?
4531  * @cfg {Boolean} animateRef (true|false) link to element default false  
4532   
4533  * @constructor
4534  * Create a new Navbar Item
4535  * @param {Object} config The config object
4536  */
4537 Roo.bootstrap.NavItem = function(config){
4538     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4539     this.addEvents({
4540         // raw events
4541         /**
4542          * @event click
4543          * The raw click event for the entire grid.
4544          * @param {Roo.EventObject} e
4545          */
4546         "click" : true,
4547          /**
4548             * @event changed
4549             * Fires when the active item active state changes
4550             * @param {Roo.bootstrap.NavItem} this
4551             * @param {boolean} state the new state
4552              
4553          */
4554         'changed': true,
4555         /**
4556             * @event scrollto
4557             * Fires when scroll to element
4558             * @param {Roo.bootstrap.NavItem} this
4559             * @param {Object} options
4560             * @param {Roo.EventObject} e
4561              
4562          */
4563         'scrollto': true
4564     });
4565    
4566 };
4567
4568 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4569     
4570     href: false,
4571     html: '',
4572     badge: '',
4573     icon: false,
4574     glyphicon: false,
4575     active: false,
4576     preventDefault : false,
4577     tabId : false,
4578     tagtype : 'a',
4579     disabled : false,
4580     animateRef : false,
4581     was_active : false,
4582     
4583     getAutoCreate : function(){
4584          
4585         var cfg = {
4586             tag: 'li',
4587             cls: 'nav-item'
4588             
4589         };
4590         
4591         if (this.active) {
4592             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4593         }
4594         if (this.disabled) {
4595             cfg.cls += ' disabled';
4596         }
4597         
4598         if (this.href || this.html || this.glyphicon || this.icon) {
4599             cfg.cn = [
4600                 {
4601                     tag: this.tagtype,
4602                     href : this.href || "#",
4603                     html: this.html || ''
4604                 }
4605             ];
4606             if (this.tagtype == 'a') {
4607                 cfg.cn[0].cls = 'nav-link';
4608             }
4609             if (this.icon) {
4610                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4611             }
4612
4613             if(this.glyphicon) {
4614                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4615             }
4616             
4617             if (this.menu) {
4618                 
4619                 cfg.cn[0].html += " <span class='caret'></span>";
4620              
4621             }
4622             
4623             if (this.badge !== '') {
4624                  
4625                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4626             }
4627         }
4628         
4629         
4630         
4631         return cfg;
4632     },
4633     initEvents: function() 
4634     {
4635         if (typeof (this.menu) != 'undefined') {
4636             this.menu.parentType = this.xtype;
4637             this.menu.triggerEl = this.el;
4638             this.menu = this.addxtype(Roo.apply({}, this.menu));
4639         }
4640         
4641         this.el.select('a',true).on('click', this.onClick, this);
4642         
4643         if(this.tagtype == 'span'){
4644             this.el.select('span',true).on('click', this.onClick, this);
4645         }
4646        
4647         // at this point parent should be available..
4648         this.parent().register(this);
4649     },
4650     
4651     onClick : function(e)
4652     {
4653         if (e.getTarget('.dropdown-menu-item')) {
4654             // did you click on a menu itemm.... - then don't trigger onclick..
4655             return;
4656         }
4657         
4658         if(
4659                 this.preventDefault || 
4660                 this.href == '#' 
4661         ){
4662             Roo.log("NavItem - prevent Default?");
4663             e.preventDefault();
4664         }
4665         
4666         if (this.disabled) {
4667             return;
4668         }
4669         
4670         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4671         if (tg && tg.transition) {
4672             Roo.log("waiting for the transitionend");
4673             return;
4674         }
4675         
4676         
4677         
4678         //Roo.log("fire event clicked");
4679         if(this.fireEvent('click', this, e) === false){
4680             return;
4681         };
4682         
4683         if(this.tagtype == 'span'){
4684             return;
4685         }
4686         
4687         //Roo.log(this.href);
4688         var ael = this.el.select('a',true).first();
4689         //Roo.log(ael);
4690         
4691         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4692             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4693             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4694                 return; // ignore... - it's a 'hash' to another page.
4695             }
4696             Roo.log("NavItem - prevent Default?");
4697             e.preventDefault();
4698             this.scrollToElement(e);
4699         }
4700         
4701         
4702         var p =  this.parent();
4703    
4704         if (['tabs','pills'].indexOf(p.type)!==-1) {
4705             if (typeof(p.setActiveItem) !== 'undefined') {
4706                 p.setActiveItem(this);
4707             }
4708         }
4709         
4710         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4711         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4712             // remove the collapsed menu expand...
4713             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4714         }
4715     },
4716     
4717     isActive: function () {
4718         return this.active
4719     },
4720     setActive : function(state, fire, is_was_active)
4721     {
4722         if (this.active && !state && this.navId) {
4723             this.was_active = true;
4724             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4725             if (nv) {
4726                 nv.clearWasActive(this);
4727             }
4728             
4729         }
4730         this.active = state;
4731         
4732         if (!state ) {
4733             this.el.removeClass('active');
4734         } else if (!this.el.hasClass('active')) {
4735             this.el.addClass('active');
4736         }
4737         if (fire) {
4738             this.fireEvent('changed', this, state);
4739         }
4740         
4741         // show a panel if it's registered and related..
4742         
4743         if (!this.navId || !this.tabId || !state || is_was_active) {
4744             return;
4745         }
4746         
4747         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4748         if (!tg) {
4749             return;
4750         }
4751         var pan = tg.getPanelByName(this.tabId);
4752         if (!pan) {
4753             return;
4754         }
4755         // if we can not flip to new panel - go back to old nav highlight..
4756         if (false == tg.showPanel(pan)) {
4757             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4758             if (nv) {
4759                 var onav = nv.getWasActive();
4760                 if (onav) {
4761                     onav.setActive(true, false, true);
4762                 }
4763             }
4764             
4765         }
4766         
4767         
4768         
4769     },
4770      // this should not be here...
4771     setDisabled : function(state)
4772     {
4773         this.disabled = state;
4774         if (!state ) {
4775             this.el.removeClass('disabled');
4776         } else if (!this.el.hasClass('disabled')) {
4777             this.el.addClass('disabled');
4778         }
4779         
4780     },
4781     
4782     /**
4783      * Fetch the element to display the tooltip on.
4784      * @return {Roo.Element} defaults to this.el
4785      */
4786     tooltipEl : function()
4787     {
4788         return this.el.select('' + this.tagtype + '', true).first();
4789     },
4790     
4791     scrollToElement : function(e)
4792     {
4793         var c = document.body;
4794         
4795         /*
4796          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4797          */
4798         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4799             c = document.documentElement;
4800         }
4801         
4802         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4803         
4804         if(!target){
4805             return;
4806         }
4807
4808         var o = target.calcOffsetsTo(c);
4809         
4810         var options = {
4811             target : target,
4812             value : o[1]
4813         };
4814         
4815         this.fireEvent('scrollto', this, options, e);
4816         
4817         Roo.get(c).scrollTo('top', options.value, true);
4818         
4819         return;
4820     }
4821 });
4822  
4823
4824  /*
4825  * - LGPL
4826  *
4827  * sidebar item
4828  *
4829  *  li
4830  *    <span> icon </span>
4831  *    <span> text </span>
4832  *    <span>badge </span>
4833  */
4834
4835 /**
4836  * @class Roo.bootstrap.NavSidebarItem
4837  * @extends Roo.bootstrap.NavItem
4838  * Bootstrap Navbar.NavSidebarItem class
4839  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4840  * {Boolean} open is the menu open
4841  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4842  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4843  * {String} buttonSize (sm|md|lg)the extra classes for the button
4844  * {Boolean} showArrow show arrow next to the text (default true)
4845  * @constructor
4846  * Create a new Navbar Button
4847  * @param {Object} config The config object
4848  */
4849 Roo.bootstrap.NavSidebarItem = function(config){
4850     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4851     this.addEvents({
4852         // raw events
4853         /**
4854          * @event click
4855          * The raw click event for the entire grid.
4856          * @param {Roo.EventObject} e
4857          */
4858         "click" : true,
4859          /**
4860             * @event changed
4861             * Fires when the active item active state changes
4862             * @param {Roo.bootstrap.NavSidebarItem} this
4863             * @param {boolean} state the new state
4864              
4865          */
4866         'changed': true
4867     });
4868    
4869 };
4870
4871 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4872     
4873     badgeWeight : 'default',
4874     
4875     open: false,
4876     
4877     buttonView : false,
4878     
4879     buttonWeight : 'default',
4880     
4881     buttonSize : 'md',
4882     
4883     showArrow : true,
4884     
4885     getAutoCreate : function(){
4886         
4887         
4888         var a = {
4889                 tag: 'a',
4890                 href : this.href || '#',
4891                 cls: '',
4892                 html : '',
4893                 cn : []
4894         };
4895         
4896         if(this.buttonView){
4897             a = {
4898                 tag: 'button',
4899                 href : this.href || '#',
4900                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4901                 html : this.html,
4902                 cn : []
4903             };
4904         }
4905         
4906         var cfg = {
4907             tag: 'li',
4908             cls: '',
4909             cn: [ a ]
4910         };
4911         
4912         if (this.active) {
4913             cfg.cls += ' active';
4914         }
4915         
4916         if (this.disabled) {
4917             cfg.cls += ' disabled';
4918         }
4919         if (this.open) {
4920             cfg.cls += ' open x-open';
4921         }
4922         // left icon..
4923         if (this.glyphicon || this.icon) {
4924             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4925             a.cn.push({ tag : 'i', cls : c }) ;
4926         }
4927         
4928         if(!this.buttonView){
4929             var span = {
4930                 tag: 'span',
4931                 html : this.html || ''
4932             };
4933
4934             a.cn.push(span);
4935             
4936         }
4937         
4938         if (this.badge !== '') {
4939             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4940         }
4941         
4942         if (this.menu) {
4943             
4944             if(this.showArrow){
4945                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4946             }
4947             
4948             a.cls += ' dropdown-toggle treeview' ;
4949         }
4950         
4951         return cfg;
4952     },
4953     
4954     initEvents : function()
4955     { 
4956         if (typeof (this.menu) != 'undefined') {
4957             this.menu.parentType = this.xtype;
4958             this.menu.triggerEl = this.el;
4959             this.menu = this.addxtype(Roo.apply({}, this.menu));
4960         }
4961         
4962         this.el.on('click', this.onClick, this);
4963         
4964         if(this.badge !== ''){
4965             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4966         }
4967         
4968     },
4969     
4970     onClick : function(e)
4971     {
4972         if(this.disabled){
4973             e.preventDefault();
4974             return;
4975         }
4976         
4977         if(this.preventDefault){
4978             e.preventDefault();
4979         }
4980         
4981         this.fireEvent('click', this);
4982     },
4983     
4984     disable : function()
4985     {
4986         this.setDisabled(true);
4987     },
4988     
4989     enable : function()
4990     {
4991         this.setDisabled(false);
4992     },
4993     
4994     setDisabled : function(state)
4995     {
4996         if(this.disabled == state){
4997             return;
4998         }
4999         
5000         this.disabled = state;
5001         
5002         if (state) {
5003             this.el.addClass('disabled');
5004             return;
5005         }
5006         
5007         this.el.removeClass('disabled');
5008         
5009         return;
5010     },
5011     
5012     setActive : function(state)
5013     {
5014         if(this.active == state){
5015             return;
5016         }
5017         
5018         this.active = state;
5019         
5020         if (state) {
5021             this.el.addClass('active');
5022             return;
5023         }
5024         
5025         this.el.removeClass('active');
5026         
5027         return;
5028     },
5029     
5030     isActive: function () 
5031     {
5032         return this.active;
5033     },
5034     
5035     setBadge : function(str)
5036     {
5037         if(!this.badgeEl){
5038             return;
5039         }
5040         
5041         this.badgeEl.dom.innerHTML = str;
5042     }
5043     
5044    
5045      
5046  
5047 });
5048  
5049
5050  /*
5051  * - LGPL
5052  *
5053  * row
5054  * 
5055  */
5056
5057 /**
5058  * @class Roo.bootstrap.Row
5059  * @extends Roo.bootstrap.Component
5060  * Bootstrap Row class (contains columns...)
5061  * 
5062  * @constructor
5063  * Create a new Row
5064  * @param {Object} config The config object
5065  */
5066
5067 Roo.bootstrap.Row = function(config){
5068     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5069 };
5070
5071 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5072     
5073     getAutoCreate : function(){
5074        return {
5075             cls: 'row clearfix'
5076        };
5077     }
5078     
5079     
5080 });
5081
5082  
5083
5084  /*
5085  * - LGPL
5086  *
5087  * element
5088  * 
5089  */
5090
5091 /**
5092  * @class Roo.bootstrap.Element
5093  * @extends Roo.bootstrap.Component
5094  * Bootstrap Element class
5095  * @cfg {String} html contents of the element
5096  * @cfg {String} tag tag of the element
5097  * @cfg {String} cls class of the element
5098  * @cfg {Boolean} preventDefault (true|false) default false
5099  * @cfg {Boolean} clickable (true|false) default false
5100  * 
5101  * @constructor
5102  * Create a new Element
5103  * @param {Object} config The config object
5104  */
5105
5106 Roo.bootstrap.Element = function(config){
5107     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5108     
5109     this.addEvents({
5110         // raw events
5111         /**
5112          * @event click
5113          * When a element is chick
5114          * @param {Roo.bootstrap.Element} this
5115          * @param {Roo.EventObject} e
5116          */
5117         "click" : true
5118     });
5119 };
5120
5121 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5122     
5123     tag: 'div',
5124     cls: '',
5125     html: '',
5126     preventDefault: false, 
5127     clickable: false,
5128     
5129     getAutoCreate : function(){
5130         
5131         var cfg = {
5132             tag: this.tag,
5133             // cls: this.cls, double assign in parent class Component.js :: onRender
5134             html: this.html
5135         };
5136         
5137         return cfg;
5138     },
5139     
5140     initEvents: function() 
5141     {
5142         Roo.bootstrap.Element.superclass.initEvents.call(this);
5143         
5144         if(this.clickable){
5145             this.el.on('click', this.onClick, this);
5146         }
5147         
5148     },
5149     
5150     onClick : function(e)
5151     {
5152         if(this.preventDefault){
5153             e.preventDefault();
5154         }
5155         
5156         this.fireEvent('click', this, e);
5157     },
5158     
5159     getValue : function()
5160     {
5161         return this.el.dom.innerHTML;
5162     },
5163     
5164     setValue : function(value)
5165     {
5166         this.el.dom.innerHTML = value;
5167     }
5168    
5169 });
5170
5171  
5172
5173  /*
5174  * - LGPL
5175  *
5176  * pagination
5177  * 
5178  */
5179
5180 /**
5181  * @class Roo.bootstrap.Pagination
5182  * @extends Roo.bootstrap.Component
5183  * Bootstrap Pagination class
5184  * @cfg {String} size xs | sm | md | lg
5185  * @cfg {Boolean} inverse false | true
5186  * 
5187  * @constructor
5188  * Create a new Pagination
5189  * @param {Object} config The config object
5190  */
5191
5192 Roo.bootstrap.Pagination = function(config){
5193     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5194 };
5195
5196 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5197     
5198     cls: false,
5199     size: false,
5200     inverse: false,
5201     
5202     getAutoCreate : function(){
5203         var cfg = {
5204             tag: 'ul',
5205                 cls: 'pagination'
5206         };
5207         if (this.inverse) {
5208             cfg.cls += ' inverse';
5209         }
5210         if (this.html) {
5211             cfg.html=this.html;
5212         }
5213         if (this.cls) {
5214             cfg.cls += " " + this.cls;
5215         }
5216         return cfg;
5217     }
5218    
5219 });
5220
5221  
5222
5223  /*
5224  * - LGPL
5225  *
5226  * Pagination item
5227  * 
5228  */
5229
5230
5231 /**
5232  * @class Roo.bootstrap.PaginationItem
5233  * @extends Roo.bootstrap.Component
5234  * Bootstrap PaginationItem class
5235  * @cfg {String} html text
5236  * @cfg {String} href the link
5237  * @cfg {Boolean} preventDefault (true | false) default true
5238  * @cfg {Boolean} active (true | false) default false
5239  * @cfg {Boolean} disabled default false
5240  * 
5241  * 
5242  * @constructor
5243  * Create a new PaginationItem
5244  * @param {Object} config The config object
5245  */
5246
5247
5248 Roo.bootstrap.PaginationItem = function(config){
5249     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5250     this.addEvents({
5251         // raw events
5252         /**
5253          * @event click
5254          * The raw click event for the entire grid.
5255          * @param {Roo.EventObject} e
5256          */
5257         "click" : true
5258     });
5259 };
5260
5261 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5262     
5263     href : false,
5264     html : false,
5265     preventDefault: true,
5266     active : false,
5267     cls : false,
5268     disabled: false,
5269     
5270     getAutoCreate : function(){
5271         var cfg= {
5272             tag: 'li',
5273             cn: [
5274                 {
5275                     tag : 'a',
5276                     href : this.href ? this.href : '#',
5277                     html : this.html ? this.html : ''
5278                 }
5279             ]
5280         };
5281         
5282         if(this.cls){
5283             cfg.cls = this.cls;
5284         }
5285         
5286         if(this.disabled){
5287             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5288         }
5289         
5290         if(this.active){
5291             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5292         }
5293         
5294         return cfg;
5295     },
5296     
5297     initEvents: function() {
5298         
5299         this.el.on('click', this.onClick, this);
5300         
5301     },
5302     onClick : function(e)
5303     {
5304         Roo.log('PaginationItem on click ');
5305         if(this.preventDefault){
5306             e.preventDefault();
5307         }
5308         
5309         if(this.disabled){
5310             return;
5311         }
5312         
5313         this.fireEvent('click', this, e);
5314     }
5315    
5316 });
5317
5318  
5319
5320  /*
5321  * - LGPL
5322  *
5323  * slider
5324  * 
5325  */
5326
5327
5328 /**
5329  * @class Roo.bootstrap.Slider
5330  * @extends Roo.bootstrap.Component
5331  * Bootstrap Slider class
5332  *    
5333  * @constructor
5334  * Create a new Slider
5335  * @param {Object} config The config object
5336  */
5337
5338 Roo.bootstrap.Slider = function(config){
5339     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5340 };
5341
5342 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5343     
5344     getAutoCreate : function(){
5345         
5346         var cfg = {
5347             tag: 'div',
5348             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5349             cn: [
5350                 {
5351                     tag: 'a',
5352                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5353                 }
5354             ]
5355         };
5356         
5357         return cfg;
5358     }
5359    
5360 });
5361
5362  /*
5363  * Based on:
5364  * Ext JS Library 1.1.1
5365  * Copyright(c) 2006-2007, Ext JS, LLC.
5366  *
5367  * Originally Released Under LGPL - original licence link has changed is not relivant.
5368  *
5369  * Fork - LGPL
5370  * <script type="text/javascript">
5371  */
5372  
5373
5374 /**
5375  * @class Roo.grid.ColumnModel
5376  * @extends Roo.util.Observable
5377  * This is the default implementation of a ColumnModel used by the Grid. It defines
5378  * the columns in the grid.
5379  * <br>Usage:<br>
5380  <pre><code>
5381  var colModel = new Roo.grid.ColumnModel([
5382         {header: "Ticker", width: 60, sortable: true, locked: true},
5383         {header: "Company Name", width: 150, sortable: true},
5384         {header: "Market Cap.", width: 100, sortable: true},
5385         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5386         {header: "Employees", width: 100, sortable: true, resizable: false}
5387  ]);
5388  </code></pre>
5389  * <p>
5390  
5391  * The config options listed for this class are options which may appear in each
5392  * individual column definition.
5393  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5394  * @constructor
5395  * @param {Object} config An Array of column config objects. See this class's
5396  * config objects for details.
5397 */
5398 Roo.grid.ColumnModel = function(config){
5399         /**
5400      * The config passed into the constructor
5401      */
5402     this.config = config;
5403     this.lookup = {};
5404
5405     // if no id, create one
5406     // if the column does not have a dataIndex mapping,
5407     // map it to the order it is in the config
5408     for(var i = 0, len = config.length; i < len; i++){
5409         var c = config[i];
5410         if(typeof c.dataIndex == "undefined"){
5411             c.dataIndex = i;
5412         }
5413         if(typeof c.renderer == "string"){
5414             c.renderer = Roo.util.Format[c.renderer];
5415         }
5416         if(typeof c.id == "undefined"){
5417             c.id = Roo.id();
5418         }
5419         if(c.editor && c.editor.xtype){
5420             c.editor  = Roo.factory(c.editor, Roo.grid);
5421         }
5422         if(c.editor && c.editor.isFormField){
5423             c.editor = new Roo.grid.GridEditor(c.editor);
5424         }
5425         this.lookup[c.id] = c;
5426     }
5427
5428     /**
5429      * The width of columns which have no width specified (defaults to 100)
5430      * @type Number
5431      */
5432     this.defaultWidth = 100;
5433
5434     /**
5435      * Default sortable of columns which have no sortable specified (defaults to false)
5436      * @type Boolean
5437      */
5438     this.defaultSortable = false;
5439
5440     this.addEvents({
5441         /**
5442              * @event widthchange
5443              * Fires when the width of a column changes.
5444              * @param {ColumnModel} this
5445              * @param {Number} columnIndex The column index
5446              * @param {Number} newWidth The new width
5447              */
5448             "widthchange": true,
5449         /**
5450              * @event headerchange
5451              * Fires when the text of a header changes.
5452              * @param {ColumnModel} this
5453              * @param {Number} columnIndex The column index
5454              * @param {Number} newText The new header text
5455              */
5456             "headerchange": true,
5457         /**
5458              * @event hiddenchange
5459              * Fires when a column is hidden or "unhidden".
5460              * @param {ColumnModel} this
5461              * @param {Number} columnIndex The column index
5462              * @param {Boolean} hidden true if hidden, false otherwise
5463              */
5464             "hiddenchange": true,
5465             /**
5466          * @event columnmoved
5467          * Fires when a column is moved.
5468          * @param {ColumnModel} this
5469          * @param {Number} oldIndex
5470          * @param {Number} newIndex
5471          */
5472         "columnmoved" : true,
5473         /**
5474          * @event columlockchange
5475          * Fires when a column's locked state is changed
5476          * @param {ColumnModel} this
5477          * @param {Number} colIndex
5478          * @param {Boolean} locked true if locked
5479          */
5480         "columnlockchange" : true
5481     });
5482     Roo.grid.ColumnModel.superclass.constructor.call(this);
5483 };
5484 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5485     /**
5486      * @cfg {String} header The header text to display in the Grid view.
5487      */
5488     /**
5489      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5490      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5491      * specified, the column's index is used as an index into the Record's data Array.
5492      */
5493     /**
5494      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5495      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5496      */
5497     /**
5498      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5499      * Defaults to the value of the {@link #defaultSortable} property.
5500      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5501      */
5502     /**
5503      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5504      */
5505     /**
5506      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5507      */
5508     /**
5509      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5510      */
5511     /**
5512      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5513      */
5514     /**
5515      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5516      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5517      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5518      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5519      */
5520        /**
5521      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5522      */
5523     /**
5524      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5525      */
5526     /**
5527      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5528      */
5529     /**
5530      * @cfg {String} cursor (Optional)
5531      */
5532     /**
5533      * @cfg {String} tooltip (Optional)
5534      */
5535     /**
5536      * @cfg {Number} xs (Optional)
5537      */
5538     /**
5539      * @cfg {Number} sm (Optional)
5540      */
5541     /**
5542      * @cfg {Number} md (Optional)
5543      */
5544     /**
5545      * @cfg {Number} lg (Optional)
5546      */
5547     /**
5548      * Returns the id of the column at the specified index.
5549      * @param {Number} index The column index
5550      * @return {String} the id
5551      */
5552     getColumnId : function(index){
5553         return this.config[index].id;
5554     },
5555
5556     /**
5557      * Returns the column for a specified id.
5558      * @param {String} id The column id
5559      * @return {Object} the column
5560      */
5561     getColumnById : function(id){
5562         return this.lookup[id];
5563     },
5564
5565     
5566     /**
5567      * Returns the column for a specified dataIndex.
5568      * @param {String} dataIndex The column dataIndex
5569      * @return {Object|Boolean} the column or false if not found
5570      */
5571     getColumnByDataIndex: function(dataIndex){
5572         var index = this.findColumnIndex(dataIndex);
5573         return index > -1 ? this.config[index] : false;
5574     },
5575     
5576     /**
5577      * Returns the index for a specified column id.
5578      * @param {String} id The column id
5579      * @return {Number} the index, or -1 if not found
5580      */
5581     getIndexById : function(id){
5582         for(var i = 0, len = this.config.length; i < len; i++){
5583             if(this.config[i].id == id){
5584                 return i;
5585             }
5586         }
5587         return -1;
5588     },
5589     
5590     /**
5591      * Returns the index for a specified column dataIndex.
5592      * @param {String} dataIndex The column dataIndex
5593      * @return {Number} the index, or -1 if not found
5594      */
5595     
5596     findColumnIndex : function(dataIndex){
5597         for(var i = 0, len = this.config.length; i < len; i++){
5598             if(this.config[i].dataIndex == dataIndex){
5599                 return i;
5600             }
5601         }
5602         return -1;
5603     },
5604     
5605     
5606     moveColumn : function(oldIndex, newIndex){
5607         var c = this.config[oldIndex];
5608         this.config.splice(oldIndex, 1);
5609         this.config.splice(newIndex, 0, c);
5610         this.dataMap = null;
5611         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5612     },
5613
5614     isLocked : function(colIndex){
5615         return this.config[colIndex].locked === true;
5616     },
5617
5618     setLocked : function(colIndex, value, suppressEvent){
5619         if(this.isLocked(colIndex) == value){
5620             return;
5621         }
5622         this.config[colIndex].locked = value;
5623         if(!suppressEvent){
5624             this.fireEvent("columnlockchange", this, colIndex, value);
5625         }
5626     },
5627
5628     getTotalLockedWidth : function(){
5629         var totalWidth = 0;
5630         for(var i = 0; i < this.config.length; i++){
5631             if(this.isLocked(i) && !this.isHidden(i)){
5632                 this.totalWidth += this.getColumnWidth(i);
5633             }
5634         }
5635         return totalWidth;
5636     },
5637
5638     getLockedCount : function(){
5639         for(var i = 0, len = this.config.length; i < len; i++){
5640             if(!this.isLocked(i)){
5641                 return i;
5642             }
5643         }
5644         
5645         return this.config.length;
5646     },
5647
5648     /**
5649      * Returns the number of columns.
5650      * @return {Number}
5651      */
5652     getColumnCount : function(visibleOnly){
5653         if(visibleOnly === true){
5654             var c = 0;
5655             for(var i = 0, len = this.config.length; i < len; i++){
5656                 if(!this.isHidden(i)){
5657                     c++;
5658                 }
5659             }
5660             return c;
5661         }
5662         return this.config.length;
5663     },
5664
5665     /**
5666      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5667      * @param {Function} fn
5668      * @param {Object} scope (optional)
5669      * @return {Array} result
5670      */
5671     getColumnsBy : function(fn, scope){
5672         var r = [];
5673         for(var i = 0, len = this.config.length; i < len; i++){
5674             var c = this.config[i];
5675             if(fn.call(scope||this, c, i) === true){
5676                 r[r.length] = c;
5677             }
5678         }
5679         return r;
5680     },
5681
5682     /**
5683      * Returns true if the specified column is sortable.
5684      * @param {Number} col The column index
5685      * @return {Boolean}
5686      */
5687     isSortable : function(col){
5688         if(typeof this.config[col].sortable == "undefined"){
5689             return this.defaultSortable;
5690         }
5691         return this.config[col].sortable;
5692     },
5693
5694     /**
5695      * Returns the rendering (formatting) function defined for the column.
5696      * @param {Number} col The column index.
5697      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5698      */
5699     getRenderer : function(col){
5700         if(!this.config[col].renderer){
5701             return Roo.grid.ColumnModel.defaultRenderer;
5702         }
5703         return this.config[col].renderer;
5704     },
5705
5706     /**
5707      * Sets the rendering (formatting) function for a column.
5708      * @param {Number} col The column index
5709      * @param {Function} fn The function to use to process the cell's raw data
5710      * to return HTML markup for the grid view. The render function is called with
5711      * the following parameters:<ul>
5712      * <li>Data value.</li>
5713      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5714      * <li>css A CSS style string to apply to the table cell.</li>
5715      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5716      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5717      * <li>Row index</li>
5718      * <li>Column index</li>
5719      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5720      */
5721     setRenderer : function(col, fn){
5722         this.config[col].renderer = fn;
5723     },
5724
5725     /**
5726      * Returns the width for the specified column.
5727      * @param {Number} col The column index
5728      * @return {Number}
5729      */
5730     getColumnWidth : function(col){
5731         return this.config[col].width * 1 || this.defaultWidth;
5732     },
5733
5734     /**
5735      * Sets the width for a column.
5736      * @param {Number} col The column index
5737      * @param {Number} width The new width
5738      */
5739     setColumnWidth : function(col, width, suppressEvent){
5740         this.config[col].width = width;
5741         this.totalWidth = null;
5742         if(!suppressEvent){
5743              this.fireEvent("widthchange", this, col, width);
5744         }
5745     },
5746
5747     /**
5748      * Returns the total width of all columns.
5749      * @param {Boolean} includeHidden True to include hidden column widths
5750      * @return {Number}
5751      */
5752     getTotalWidth : function(includeHidden){
5753         if(!this.totalWidth){
5754             this.totalWidth = 0;
5755             for(var i = 0, len = this.config.length; i < len; i++){
5756                 if(includeHidden || !this.isHidden(i)){
5757                     this.totalWidth += this.getColumnWidth(i);
5758                 }
5759             }
5760         }
5761         return this.totalWidth;
5762     },
5763
5764     /**
5765      * Returns the header for the specified column.
5766      * @param {Number} col The column index
5767      * @return {String}
5768      */
5769     getColumnHeader : function(col){
5770         return this.config[col].header;
5771     },
5772
5773     /**
5774      * Sets the header for a column.
5775      * @param {Number} col The column index
5776      * @param {String} header The new header
5777      */
5778     setColumnHeader : function(col, header){
5779         this.config[col].header = header;
5780         this.fireEvent("headerchange", this, col, header);
5781     },
5782
5783     /**
5784      * Returns the tooltip for the specified column.
5785      * @param {Number} col The column index
5786      * @return {String}
5787      */
5788     getColumnTooltip : function(col){
5789             return this.config[col].tooltip;
5790     },
5791     /**
5792      * Sets the tooltip for a column.
5793      * @param {Number} col The column index
5794      * @param {String} tooltip The new tooltip
5795      */
5796     setColumnTooltip : function(col, tooltip){
5797             this.config[col].tooltip = tooltip;
5798     },
5799
5800     /**
5801      * Returns the dataIndex for the specified column.
5802      * @param {Number} col The column index
5803      * @return {Number}
5804      */
5805     getDataIndex : function(col){
5806         return this.config[col].dataIndex;
5807     },
5808
5809     /**
5810      * Sets the dataIndex for a column.
5811      * @param {Number} col The column index
5812      * @param {Number} dataIndex The new dataIndex
5813      */
5814     setDataIndex : function(col, dataIndex){
5815         this.config[col].dataIndex = dataIndex;
5816     },
5817
5818     
5819     
5820     /**
5821      * Returns true if the cell is editable.
5822      * @param {Number} colIndex The column index
5823      * @param {Number} rowIndex The row index - this is nto actually used..?
5824      * @return {Boolean}
5825      */
5826     isCellEditable : function(colIndex, rowIndex){
5827         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5828     },
5829
5830     /**
5831      * Returns the editor defined for the cell/column.
5832      * return false or null to disable editing.
5833      * @param {Number} colIndex The column index
5834      * @param {Number} rowIndex The row index
5835      * @return {Object}
5836      */
5837     getCellEditor : function(colIndex, rowIndex){
5838         return this.config[colIndex].editor;
5839     },
5840
5841     /**
5842      * Sets if a column is editable.
5843      * @param {Number} col The column index
5844      * @param {Boolean} editable True if the column is editable
5845      */
5846     setEditable : function(col, editable){
5847         this.config[col].editable = editable;
5848     },
5849
5850
5851     /**
5852      * Returns true if the column is hidden.
5853      * @param {Number} colIndex The column index
5854      * @return {Boolean}
5855      */
5856     isHidden : function(colIndex){
5857         return this.config[colIndex].hidden;
5858     },
5859
5860
5861     /**
5862      * Returns true if the column width cannot be changed
5863      */
5864     isFixed : function(colIndex){
5865         return this.config[colIndex].fixed;
5866     },
5867
5868     /**
5869      * Returns true if the column can be resized
5870      * @return {Boolean}
5871      */
5872     isResizable : function(colIndex){
5873         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5874     },
5875     /**
5876      * Sets if a column is hidden.
5877      * @param {Number} colIndex The column index
5878      * @param {Boolean} hidden True if the column is hidden
5879      */
5880     setHidden : function(colIndex, hidden){
5881         this.config[colIndex].hidden = hidden;
5882         this.totalWidth = null;
5883         this.fireEvent("hiddenchange", this, colIndex, hidden);
5884     },
5885
5886     /**
5887      * Sets the editor for a column.
5888      * @param {Number} col The column index
5889      * @param {Object} editor The editor object
5890      */
5891     setEditor : function(col, editor){
5892         this.config[col].editor = editor;
5893     }
5894 });
5895
5896 Roo.grid.ColumnModel.defaultRenderer = function(value)
5897 {
5898     if(typeof value == "object") {
5899         return value;
5900     }
5901         if(typeof value == "string" && value.length < 1){
5902             return "&#160;";
5903         }
5904     
5905         return String.format("{0}", value);
5906 };
5907
5908 // Alias for backwards compatibility
5909 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5910 /*
5911  * Based on:
5912  * Ext JS Library 1.1.1
5913  * Copyright(c) 2006-2007, Ext JS, LLC.
5914  *
5915  * Originally Released Under LGPL - original licence link has changed is not relivant.
5916  *
5917  * Fork - LGPL
5918  * <script type="text/javascript">
5919  */
5920  
5921 /**
5922  * @class Roo.LoadMask
5923  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5924  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5925  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5926  * element's UpdateManager load indicator and will be destroyed after the initial load.
5927  * @constructor
5928  * Create a new LoadMask
5929  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5930  * @param {Object} config The config object
5931  */
5932 Roo.LoadMask = function(el, config){
5933     this.el = Roo.get(el);
5934     Roo.apply(this, config);
5935     if(this.store){
5936         this.store.on('beforeload', this.onBeforeLoad, this);
5937         this.store.on('load', this.onLoad, this);
5938         this.store.on('loadexception', this.onLoadException, this);
5939         this.removeMask = false;
5940     }else{
5941         var um = this.el.getUpdateManager();
5942         um.showLoadIndicator = false; // disable the default indicator
5943         um.on('beforeupdate', this.onBeforeLoad, this);
5944         um.on('update', this.onLoad, this);
5945         um.on('failure', this.onLoad, this);
5946         this.removeMask = true;
5947     }
5948 };
5949
5950 Roo.LoadMask.prototype = {
5951     /**
5952      * @cfg {Boolean} removeMask
5953      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5954      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5955      */
5956     /**
5957      * @cfg {String} msg
5958      * The text to display in a centered loading message box (defaults to 'Loading...')
5959      */
5960     msg : 'Loading...',
5961     /**
5962      * @cfg {String} msgCls
5963      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5964      */
5965     msgCls : 'x-mask-loading',
5966
5967     /**
5968      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5969      * @type Boolean
5970      */
5971     disabled: false,
5972
5973     /**
5974      * Disables the mask to prevent it from being displayed
5975      */
5976     disable : function(){
5977        this.disabled = true;
5978     },
5979
5980     /**
5981      * Enables the mask so that it can be displayed
5982      */
5983     enable : function(){
5984         this.disabled = false;
5985     },
5986     
5987     onLoadException : function()
5988     {
5989         Roo.log(arguments);
5990         
5991         if (typeof(arguments[3]) != 'undefined') {
5992             Roo.MessageBox.alert("Error loading",arguments[3]);
5993         } 
5994         /*
5995         try {
5996             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5997                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5998             }   
5999         } catch(e) {
6000             
6001         }
6002         */
6003     
6004         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6005     },
6006     // private
6007     onLoad : function()
6008     {
6009         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6010     },
6011
6012     // private
6013     onBeforeLoad : function(){
6014         if(!this.disabled){
6015             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6016         }
6017     },
6018
6019     // private
6020     destroy : function(){
6021         if(this.store){
6022             this.store.un('beforeload', this.onBeforeLoad, this);
6023             this.store.un('load', this.onLoad, this);
6024             this.store.un('loadexception', this.onLoadException, this);
6025         }else{
6026             var um = this.el.getUpdateManager();
6027             um.un('beforeupdate', this.onBeforeLoad, this);
6028             um.un('update', this.onLoad, this);
6029             um.un('failure', this.onLoad, this);
6030         }
6031     }
6032 };/*
6033  * - LGPL
6034  *
6035  * table
6036  * 
6037  */
6038
6039 /**
6040  * @class Roo.bootstrap.Table
6041  * @extends Roo.bootstrap.Component
6042  * Bootstrap Table class
6043  * @cfg {String} cls table class
6044  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6045  * @cfg {String} bgcolor Specifies the background color for a table
6046  * @cfg {Number} border Specifies whether the table cells should have borders or not
6047  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6048  * @cfg {Number} cellspacing Specifies the space between cells
6049  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6050  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6051  * @cfg {String} sortable Specifies that the table should be sortable
6052  * @cfg {String} summary Specifies a summary of the content of a table
6053  * @cfg {Number} width Specifies the width of a table
6054  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6055  * 
6056  * @cfg {boolean} striped Should the rows be alternative striped
6057  * @cfg {boolean} bordered Add borders to the table
6058  * @cfg {boolean} hover Add hover highlighting
6059  * @cfg {boolean} condensed Format condensed
6060  * @cfg {boolean} responsive Format condensed
6061  * @cfg {Boolean} loadMask (true|false) default false
6062  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6063  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6064  * @cfg {Boolean} rowSelection (true|false) default false
6065  * @cfg {Boolean} cellSelection (true|false) default false
6066  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6067  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6068  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6069  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6070  
6071  * 
6072  * @constructor
6073  * Create a new Table
6074  * @param {Object} config The config object
6075  */
6076
6077 Roo.bootstrap.Table = function(config){
6078     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6079     
6080   
6081     
6082     // BC...
6083     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6084     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6085     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6086     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6087     
6088     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6089     if (this.sm) {
6090         this.sm.grid = this;
6091         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6092         this.sm = this.selModel;
6093         this.sm.xmodule = this.xmodule || false;
6094     }
6095     
6096     if (this.cm && typeof(this.cm.config) == 'undefined') {
6097         this.colModel = new Roo.grid.ColumnModel(this.cm);
6098         this.cm = this.colModel;
6099         this.cm.xmodule = this.xmodule || false;
6100     }
6101     if (this.store) {
6102         this.store= Roo.factory(this.store, Roo.data);
6103         this.ds = this.store;
6104         this.ds.xmodule = this.xmodule || false;
6105          
6106     }
6107     if (this.footer && this.store) {
6108         this.footer.dataSource = this.ds;
6109         this.footer = Roo.factory(this.footer);
6110     }
6111     
6112     /** @private */
6113     this.addEvents({
6114         /**
6115          * @event cellclick
6116          * Fires when a cell is clicked
6117          * @param {Roo.bootstrap.Table} this
6118          * @param {Roo.Element} el
6119          * @param {Number} rowIndex
6120          * @param {Number} columnIndex
6121          * @param {Roo.EventObject} e
6122          */
6123         "cellclick" : true,
6124         /**
6125          * @event celldblclick
6126          * Fires when a cell is double clicked
6127          * @param {Roo.bootstrap.Table} this
6128          * @param {Roo.Element} el
6129          * @param {Number} rowIndex
6130          * @param {Number} columnIndex
6131          * @param {Roo.EventObject} e
6132          */
6133         "celldblclick" : true,
6134         /**
6135          * @event rowclick
6136          * Fires when a row is clicked
6137          * @param {Roo.bootstrap.Table} this
6138          * @param {Roo.Element} el
6139          * @param {Number} rowIndex
6140          * @param {Roo.EventObject} e
6141          */
6142         "rowclick" : true,
6143         /**
6144          * @event rowdblclick
6145          * Fires when a row is double clicked
6146          * @param {Roo.bootstrap.Table} this
6147          * @param {Roo.Element} el
6148          * @param {Number} rowIndex
6149          * @param {Roo.EventObject} e
6150          */
6151         "rowdblclick" : true,
6152         /**
6153          * @event mouseover
6154          * Fires when a mouseover occur
6155          * @param {Roo.bootstrap.Table} this
6156          * @param {Roo.Element} el
6157          * @param {Number} rowIndex
6158          * @param {Number} columnIndex
6159          * @param {Roo.EventObject} e
6160          */
6161         "mouseover" : true,
6162         /**
6163          * @event mouseout
6164          * Fires when a mouseout occur
6165          * @param {Roo.bootstrap.Table} this
6166          * @param {Roo.Element} el
6167          * @param {Number} rowIndex
6168          * @param {Number} columnIndex
6169          * @param {Roo.EventObject} e
6170          */
6171         "mouseout" : true,
6172         /**
6173          * @event rowclass
6174          * Fires when a row is rendered, so you can change add a style to it.
6175          * @param {Roo.bootstrap.Table} this
6176          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6177          */
6178         'rowclass' : true,
6179           /**
6180          * @event rowsrendered
6181          * Fires when all the  rows have been rendered
6182          * @param {Roo.bootstrap.Table} this
6183          */
6184         'rowsrendered' : true,
6185         /**
6186          * @event contextmenu
6187          * The raw contextmenu event for the entire grid.
6188          * @param {Roo.EventObject} e
6189          */
6190         "contextmenu" : true,
6191         /**
6192          * @event rowcontextmenu
6193          * Fires when a row is right clicked
6194          * @param {Roo.bootstrap.Table} this
6195          * @param {Number} rowIndex
6196          * @param {Roo.EventObject} e
6197          */
6198         "rowcontextmenu" : true,
6199         /**
6200          * @event cellcontextmenu
6201          * Fires when a cell is right clicked
6202          * @param {Roo.bootstrap.Table} this
6203          * @param {Number} rowIndex
6204          * @param {Number} cellIndex
6205          * @param {Roo.EventObject} e
6206          */
6207          "cellcontextmenu" : true,
6208          /**
6209          * @event headercontextmenu
6210          * Fires when a header is right clicked
6211          * @param {Roo.bootstrap.Table} this
6212          * @param {Number} columnIndex
6213          * @param {Roo.EventObject} e
6214          */
6215         "headercontextmenu" : true
6216     });
6217 };
6218
6219 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6220     
6221     cls: false,
6222     align: false,
6223     bgcolor: false,
6224     border: false,
6225     cellpadding: false,
6226     cellspacing: false,
6227     frame: false,
6228     rules: false,
6229     sortable: false,
6230     summary: false,
6231     width: false,
6232     striped : false,
6233     scrollBody : false,
6234     bordered: false,
6235     hover:  false,
6236     condensed : false,
6237     responsive : false,
6238     sm : false,
6239     cm : false,
6240     store : false,
6241     loadMask : false,
6242     footerShow : true,
6243     headerShow : true,
6244   
6245     rowSelection : false,
6246     cellSelection : false,
6247     layout : false,
6248     
6249     // Roo.Element - the tbody
6250     mainBody: false,
6251     // Roo.Element - thead element
6252     mainHead: false,
6253     
6254     container: false, // used by gridpanel...
6255     
6256     lazyLoad : false,
6257     
6258     CSS : Roo.util.CSS,
6259     
6260     auto_hide_footer : false,
6261     
6262     getAutoCreate : function()
6263     {
6264         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6265         
6266         cfg = {
6267             tag: 'table',
6268             cls : 'table',
6269             cn : []
6270         };
6271         if (this.scrollBody) {
6272             cfg.cls += ' table-body-fixed';
6273         }    
6274         if (this.striped) {
6275             cfg.cls += ' table-striped';
6276         }
6277         
6278         if (this.hover) {
6279             cfg.cls += ' table-hover';
6280         }
6281         if (this.bordered) {
6282             cfg.cls += ' table-bordered';
6283         }
6284         if (this.condensed) {
6285             cfg.cls += ' table-condensed';
6286         }
6287         if (this.responsive) {
6288             cfg.cls += ' table-responsive';
6289         }
6290         
6291         if (this.cls) {
6292             cfg.cls+=  ' ' +this.cls;
6293         }
6294         
6295         // this lot should be simplifed...
6296         var _t = this;
6297         var cp = [
6298             'align',
6299             'bgcolor',
6300             'border',
6301             'cellpadding',
6302             'cellspacing',
6303             'frame',
6304             'rules',
6305             'sortable',
6306             'summary',
6307             'width'
6308         ].forEach(function(k) {
6309             if (_t[k]) {
6310                 cfg[k] = _t[k];
6311             }
6312         });
6313         
6314         
6315         if (this.layout) {
6316             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6317         }
6318         
6319         if(this.store || this.cm){
6320             if(this.headerShow){
6321                 cfg.cn.push(this.renderHeader());
6322             }
6323             
6324             cfg.cn.push(this.renderBody());
6325             
6326             if(this.footerShow){
6327                 cfg.cn.push(this.renderFooter());
6328             }
6329             // where does this come from?
6330             //cfg.cls+=  ' TableGrid';
6331         }
6332         
6333         return { cn : [ cfg ] };
6334     },
6335     
6336     initEvents : function()
6337     {   
6338         if(!this.store || !this.cm){
6339             return;
6340         }
6341         if (this.selModel) {
6342             this.selModel.initEvents();
6343         }
6344         
6345         
6346         //Roo.log('initEvents with ds!!!!');
6347         
6348         this.mainBody = this.el.select('tbody', true).first();
6349         this.mainHead = this.el.select('thead', true).first();
6350         this.mainFoot = this.el.select('tfoot', true).first();
6351         
6352         
6353         
6354         var _this = this;
6355         
6356         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6357             e.on('click', _this.sort, _this);
6358         });
6359         
6360         this.mainBody.on("click", this.onClick, this);
6361         this.mainBody.on("dblclick", this.onDblClick, this);
6362         
6363         // why is this done????? = it breaks dialogs??
6364         //this.parent().el.setStyle('position', 'relative');
6365         
6366         
6367         if (this.footer) {
6368             this.footer.parentId = this.id;
6369             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6370             
6371             if(this.lazyLoad){
6372                 this.el.select('tfoot tr td').first().addClass('hide');
6373             }
6374         } 
6375         
6376         if(this.loadMask) {
6377             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6378         }
6379         
6380         this.store.on('load', this.onLoad, this);
6381         this.store.on('beforeload', this.onBeforeLoad, this);
6382         this.store.on('update', this.onUpdate, this);
6383         this.store.on('add', this.onAdd, this);
6384         this.store.on("clear", this.clear, this);
6385         
6386         this.el.on("contextmenu", this.onContextMenu, this);
6387         
6388         this.mainBody.on('scroll', this.onBodyScroll, this);
6389         
6390         this.cm.on("headerchange", this.onHeaderChange, this);
6391         
6392         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6393         
6394     },
6395     
6396     onContextMenu : function(e, t)
6397     {
6398         this.processEvent("contextmenu", e);
6399     },
6400     
6401     processEvent : function(name, e)
6402     {
6403         if (name != 'touchstart' ) {
6404             this.fireEvent(name, e);    
6405         }
6406         
6407         var t = e.getTarget();
6408         
6409         var cell = Roo.get(t);
6410         
6411         if(!cell){
6412             return;
6413         }
6414         
6415         if(cell.findParent('tfoot', false, true)){
6416             return;
6417         }
6418         
6419         if(cell.findParent('thead', false, true)){
6420             
6421             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6422                 cell = Roo.get(t).findParent('th', false, true);
6423                 if (!cell) {
6424                     Roo.log("failed to find th in thead?");
6425                     Roo.log(e.getTarget());
6426                     return;
6427                 }
6428             }
6429             
6430             var cellIndex = cell.dom.cellIndex;
6431             
6432             var ename = name == 'touchstart' ? 'click' : name;
6433             this.fireEvent("header" + ename, this, cellIndex, e);
6434             
6435             return;
6436         }
6437         
6438         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6439             cell = Roo.get(t).findParent('td', false, true);
6440             if (!cell) {
6441                 Roo.log("failed to find th in tbody?");
6442                 Roo.log(e.getTarget());
6443                 return;
6444             }
6445         }
6446         
6447         var row = cell.findParent('tr', false, true);
6448         var cellIndex = cell.dom.cellIndex;
6449         var rowIndex = row.dom.rowIndex - 1;
6450         
6451         if(row !== false){
6452             
6453             this.fireEvent("row" + name, this, rowIndex, e);
6454             
6455             if(cell !== false){
6456             
6457                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6458             }
6459         }
6460         
6461     },
6462     
6463     onMouseover : function(e, el)
6464     {
6465         var cell = Roo.get(el);
6466         
6467         if(!cell){
6468             return;
6469         }
6470         
6471         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6472             cell = cell.findParent('td', false, true);
6473         }
6474         
6475         var row = cell.findParent('tr', false, true);
6476         var cellIndex = cell.dom.cellIndex;
6477         var rowIndex = row.dom.rowIndex - 1; // start from 0
6478         
6479         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6480         
6481     },
6482     
6483     onMouseout : function(e, el)
6484     {
6485         var cell = Roo.get(el);
6486         
6487         if(!cell){
6488             return;
6489         }
6490         
6491         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6492             cell = cell.findParent('td', false, true);
6493         }
6494         
6495         var row = cell.findParent('tr', false, true);
6496         var cellIndex = cell.dom.cellIndex;
6497         var rowIndex = row.dom.rowIndex - 1; // start from 0
6498         
6499         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6500         
6501     },
6502     
6503     onClick : function(e, el)
6504     {
6505         var cell = Roo.get(el);
6506         
6507         if(!cell || (!this.cellSelection && !this.rowSelection)){
6508             return;
6509         }
6510         
6511         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6512             cell = cell.findParent('td', false, true);
6513         }
6514         
6515         if(!cell || typeof(cell) == 'undefined'){
6516             return;
6517         }
6518         
6519         var row = cell.findParent('tr', false, true);
6520         
6521         if(!row || typeof(row) == 'undefined'){
6522             return;
6523         }
6524         
6525         var cellIndex = cell.dom.cellIndex;
6526         var rowIndex = this.getRowIndex(row);
6527         
6528         // why??? - should these not be based on SelectionModel?
6529         if(this.cellSelection){
6530             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6531         }
6532         
6533         if(this.rowSelection){
6534             this.fireEvent('rowclick', this, row, rowIndex, e);
6535         }
6536         
6537         
6538     },
6539         
6540     onDblClick : function(e,el)
6541     {
6542         var cell = Roo.get(el);
6543         
6544         if(!cell || (!this.cellSelection && !this.rowSelection)){
6545             return;
6546         }
6547         
6548         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6549             cell = cell.findParent('td', false, true);
6550         }
6551         
6552         if(!cell || typeof(cell) == 'undefined'){
6553             return;
6554         }
6555         
6556         var row = cell.findParent('tr', false, true);
6557         
6558         if(!row || typeof(row) == 'undefined'){
6559             return;
6560         }
6561         
6562         var cellIndex = cell.dom.cellIndex;
6563         var rowIndex = this.getRowIndex(row);
6564         
6565         if(this.cellSelection){
6566             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6567         }
6568         
6569         if(this.rowSelection){
6570             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6571         }
6572     },
6573     
6574     sort : function(e,el)
6575     {
6576         var col = Roo.get(el);
6577         
6578         if(!col.hasClass('sortable')){
6579             return;
6580         }
6581         
6582         var sort = col.attr('sort');
6583         var dir = 'ASC';
6584         
6585         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6586             dir = 'DESC';
6587         }
6588         
6589         this.store.sortInfo = {field : sort, direction : dir};
6590         
6591         if (this.footer) {
6592             Roo.log("calling footer first");
6593             this.footer.onClick('first');
6594         } else {
6595         
6596             this.store.load({ params : { start : 0 } });
6597         }
6598     },
6599     
6600     renderHeader : function()
6601     {
6602         var header = {
6603             tag: 'thead',
6604             cn : []
6605         };
6606         
6607         var cm = this.cm;
6608         this.totalWidth = 0;
6609         
6610         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6611             
6612             var config = cm.config[i];
6613             
6614             var c = {
6615                 tag: 'th',
6616                 cls : 'x-hcol-' + i,
6617                 style : '',
6618                 html: cm.getColumnHeader(i)
6619             };
6620             
6621             var hh = '';
6622             
6623             if(typeof(config.sortable) != 'undefined' && config.sortable){
6624                 c.cls = 'sortable';
6625                 c.html = '<i class="glyphicon"></i>' + c.html;
6626             }
6627             
6628             if(typeof(config.lgHeader) != 'undefined'){
6629                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6630             }
6631             
6632             if(typeof(config.mdHeader) != 'undefined'){
6633                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6634             }
6635             
6636             if(typeof(config.smHeader) != 'undefined'){
6637                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6638             }
6639             
6640             if(typeof(config.xsHeader) != 'undefined'){
6641                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6642             }
6643             
6644             if(hh.length){
6645                 c.html = hh;
6646             }
6647             
6648             if(typeof(config.tooltip) != 'undefined'){
6649                 c.tooltip = config.tooltip;
6650             }
6651             
6652             if(typeof(config.colspan) != 'undefined'){
6653                 c.colspan = config.colspan;
6654             }
6655             
6656             if(typeof(config.hidden) != 'undefined' && config.hidden){
6657                 c.style += ' display:none;';
6658             }
6659             
6660             if(typeof(config.dataIndex) != 'undefined'){
6661                 c.sort = config.dataIndex;
6662             }
6663             
6664            
6665             
6666             if(typeof(config.align) != 'undefined' && config.align.length){
6667                 c.style += ' text-align:' + config.align + ';';
6668             }
6669             
6670             if(typeof(config.width) != 'undefined'){
6671                 c.style += ' width:' + config.width + 'px;';
6672                 this.totalWidth += config.width;
6673             } else {
6674                 this.totalWidth += 100; // assume minimum of 100 per column?
6675             }
6676             
6677             if(typeof(config.cls) != 'undefined'){
6678                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6679             }
6680             
6681             ['xs','sm','md','lg'].map(function(size){
6682                 
6683                 if(typeof(config[size]) == 'undefined'){
6684                     return;
6685                 }
6686                 
6687                 if (!config[size]) { // 0 = hidden
6688                     c.cls += ' hidden-' + size;
6689                     return;
6690                 }
6691                 
6692                 c.cls += ' col-' + size + '-' + config[size];
6693
6694             });
6695             
6696             header.cn.push(c)
6697         }
6698         
6699         return header;
6700     },
6701     
6702     renderBody : function()
6703     {
6704         var body = {
6705             tag: 'tbody',
6706             cn : [
6707                 {
6708                     tag: 'tr',
6709                     cn : [
6710                         {
6711                             tag : 'td',
6712                             colspan :  this.cm.getColumnCount()
6713                         }
6714                     ]
6715                 }
6716             ]
6717         };
6718         
6719         return body;
6720     },
6721     
6722     renderFooter : function()
6723     {
6724         var footer = {
6725             tag: 'tfoot',
6726             cn : [
6727                 {
6728                     tag: 'tr',
6729                     cn : [
6730                         {
6731                             tag : 'td',
6732                             colspan :  this.cm.getColumnCount()
6733                         }
6734                     ]
6735                 }
6736             ]
6737         };
6738         
6739         return footer;
6740     },
6741     
6742     
6743     
6744     onLoad : function()
6745     {
6746 //        Roo.log('ds onload');
6747         this.clear();
6748         
6749         var _this = this;
6750         var cm = this.cm;
6751         var ds = this.store;
6752         
6753         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6754             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6755             if (_this.store.sortInfo) {
6756                     
6757                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6758                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6759                 }
6760                 
6761                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6762                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6763                 }
6764             }
6765         });
6766         
6767         var tbody =  this.mainBody;
6768               
6769         if(ds.getCount() > 0){
6770             ds.data.each(function(d,rowIndex){
6771                 var row =  this.renderRow(cm, ds, rowIndex);
6772                 
6773                 tbody.createChild(row);
6774                 
6775                 var _this = this;
6776                 
6777                 if(row.cellObjects.length){
6778                     Roo.each(row.cellObjects, function(r){
6779                         _this.renderCellObject(r);
6780                     })
6781                 }
6782                 
6783             }, this);
6784         }
6785         
6786         var tfoot = this.el.select('tfoot', true).first();
6787         
6788         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6789             
6790             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6791             
6792             var total = this.ds.getTotalCount();
6793             
6794             if(this.footer.pageSize < total){
6795                 this.mainFoot.show();
6796             }
6797         }
6798         
6799         Roo.each(this.el.select('tbody td', true).elements, function(e){
6800             e.on('mouseover', _this.onMouseover, _this);
6801         });
6802         
6803         Roo.each(this.el.select('tbody td', true).elements, function(e){
6804             e.on('mouseout', _this.onMouseout, _this);
6805         });
6806         this.fireEvent('rowsrendered', this);
6807         
6808         this.autoSize();
6809     },
6810     
6811     
6812     onUpdate : function(ds,record)
6813     {
6814         this.refreshRow(record);
6815         this.autoSize();
6816     },
6817     
6818     onRemove : function(ds, record, index, isUpdate){
6819         if(isUpdate !== true){
6820             this.fireEvent("beforerowremoved", this, index, record);
6821         }
6822         var bt = this.mainBody.dom;
6823         
6824         var rows = this.el.select('tbody > tr', true).elements;
6825         
6826         if(typeof(rows[index]) != 'undefined'){
6827             bt.removeChild(rows[index].dom);
6828         }
6829         
6830 //        if(bt.rows[index]){
6831 //            bt.removeChild(bt.rows[index]);
6832 //        }
6833         
6834         if(isUpdate !== true){
6835             //this.stripeRows(index);
6836             //this.syncRowHeights(index, index);
6837             //this.layout();
6838             this.fireEvent("rowremoved", this, index, record);
6839         }
6840     },
6841     
6842     onAdd : function(ds, records, rowIndex)
6843     {
6844         //Roo.log('on Add called');
6845         // - note this does not handle multiple adding very well..
6846         var bt = this.mainBody.dom;
6847         for (var i =0 ; i < records.length;i++) {
6848             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6849             //Roo.log(records[i]);
6850             //Roo.log(this.store.getAt(rowIndex+i));
6851             this.insertRow(this.store, rowIndex + i, false);
6852             return;
6853         }
6854         
6855     },
6856     
6857     
6858     refreshRow : function(record){
6859         var ds = this.store, index;
6860         if(typeof record == 'number'){
6861             index = record;
6862             record = ds.getAt(index);
6863         }else{
6864             index = ds.indexOf(record);
6865         }
6866         this.insertRow(ds, index, true);
6867         this.autoSize();
6868         this.onRemove(ds, record, index+1, true);
6869         this.autoSize();
6870         //this.syncRowHeights(index, index);
6871         //this.layout();
6872         this.fireEvent("rowupdated", this, index, record);
6873     },
6874     
6875     insertRow : function(dm, rowIndex, isUpdate){
6876         
6877         if(!isUpdate){
6878             this.fireEvent("beforerowsinserted", this, rowIndex);
6879         }
6880             //var s = this.getScrollState();
6881         var row = this.renderRow(this.cm, this.store, rowIndex);
6882         // insert before rowIndex..
6883         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6884         
6885         var _this = this;
6886                 
6887         if(row.cellObjects.length){
6888             Roo.each(row.cellObjects, function(r){
6889                 _this.renderCellObject(r);
6890             })
6891         }
6892             
6893         if(!isUpdate){
6894             this.fireEvent("rowsinserted", this, rowIndex);
6895             //this.syncRowHeights(firstRow, lastRow);
6896             //this.stripeRows(firstRow);
6897             //this.layout();
6898         }
6899         
6900     },
6901     
6902     
6903     getRowDom : function(rowIndex)
6904     {
6905         var rows = this.el.select('tbody > tr', true).elements;
6906         
6907         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6908         
6909     },
6910     // returns the object tree for a tr..
6911   
6912     
6913     renderRow : function(cm, ds, rowIndex) 
6914     {
6915         var d = ds.getAt(rowIndex);
6916         
6917         var row = {
6918             tag : 'tr',
6919             cls : 'x-row-' + rowIndex,
6920             cn : []
6921         };
6922             
6923         var cellObjects = [];
6924         
6925         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6926             var config = cm.config[i];
6927             
6928             var renderer = cm.getRenderer(i);
6929             var value = '';
6930             var id = false;
6931             
6932             if(typeof(renderer) !== 'undefined'){
6933                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6934             }
6935             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6936             // and are rendered into the cells after the row is rendered - using the id for the element.
6937             
6938             if(typeof(value) === 'object'){
6939                 id = Roo.id();
6940                 cellObjects.push({
6941                     container : id,
6942                     cfg : value 
6943                 })
6944             }
6945             
6946             var rowcfg = {
6947                 record: d,
6948                 rowIndex : rowIndex,
6949                 colIndex : i,
6950                 rowClass : ''
6951             };
6952
6953             this.fireEvent('rowclass', this, rowcfg);
6954             
6955             var td = {
6956                 tag: 'td',
6957                 cls : rowcfg.rowClass + ' x-col-' + i,
6958                 style: '',
6959                 html: (typeof(value) === 'object') ? '' : value
6960             };
6961             
6962             if (id) {
6963                 td.id = id;
6964             }
6965             
6966             if(typeof(config.colspan) != 'undefined'){
6967                 td.colspan = config.colspan;
6968             }
6969             
6970             if(typeof(config.hidden) != 'undefined' && config.hidden){
6971                 td.style += ' display:none;';
6972             }
6973             
6974             if(typeof(config.align) != 'undefined' && config.align.length){
6975                 td.style += ' text-align:' + config.align + ';';
6976             }
6977             if(typeof(config.valign) != 'undefined' && config.valign.length){
6978                 td.style += ' vertical-align:' + config.valign + ';';
6979             }
6980             
6981             if(typeof(config.width) != 'undefined'){
6982                 td.style += ' width:' +  config.width + 'px;';
6983             }
6984             
6985             if(typeof(config.cursor) != 'undefined'){
6986                 td.style += ' cursor:' +  config.cursor + ';';
6987             }
6988             
6989             if(typeof(config.cls) != 'undefined'){
6990                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6991             }
6992             
6993             ['xs','sm','md','lg'].map(function(size){
6994                 
6995                 if(typeof(config[size]) == 'undefined'){
6996                     return;
6997                 }
6998                 
6999                 if (!config[size]) { // 0 = hidden
7000                     td.cls += ' hidden-' + size;
7001                     return;
7002                 }
7003                 
7004                 td.cls += ' col-' + size + '-' + config[size];
7005
7006             });
7007             
7008             row.cn.push(td);
7009            
7010         }
7011         
7012         row.cellObjects = cellObjects;
7013         
7014         return row;
7015           
7016     },
7017     
7018     
7019     
7020     onBeforeLoad : function()
7021     {
7022         
7023     },
7024      /**
7025      * Remove all rows
7026      */
7027     clear : function()
7028     {
7029         this.el.select('tbody', true).first().dom.innerHTML = '';
7030     },
7031     /**
7032      * Show or hide a row.
7033      * @param {Number} rowIndex to show or hide
7034      * @param {Boolean} state hide
7035      */
7036     setRowVisibility : function(rowIndex, state)
7037     {
7038         var bt = this.mainBody.dom;
7039         
7040         var rows = this.el.select('tbody > tr', true).elements;
7041         
7042         if(typeof(rows[rowIndex]) == 'undefined'){
7043             return;
7044         }
7045         rows[rowIndex].dom.style.display = state ? '' : 'none';
7046     },
7047     
7048     
7049     getSelectionModel : function(){
7050         if(!this.selModel){
7051             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7052         }
7053         return this.selModel;
7054     },
7055     /*
7056      * Render the Roo.bootstrap object from renderder
7057      */
7058     renderCellObject : function(r)
7059     {
7060         var _this = this;
7061         
7062         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7063         
7064         var t = r.cfg.render(r.container);
7065         
7066         if(r.cfg.cn){
7067             Roo.each(r.cfg.cn, function(c){
7068                 var child = {
7069                     container: t.getChildContainer(),
7070                     cfg: c
7071                 };
7072                 _this.renderCellObject(child);
7073             })
7074         }
7075     },
7076     
7077     getRowIndex : function(row)
7078     {
7079         var rowIndex = -1;
7080         
7081         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7082             if(el != row){
7083                 return;
7084             }
7085             
7086             rowIndex = index;
7087         });
7088         
7089         return rowIndex;
7090     },
7091      /**
7092      * Returns the grid's underlying element = used by panel.Grid
7093      * @return {Element} The element
7094      */
7095     getGridEl : function(){
7096         return this.el;
7097     },
7098      /**
7099      * Forces a resize - used by panel.Grid
7100      * @return {Element} The element
7101      */
7102     autoSize : function()
7103     {
7104         //var ctr = Roo.get(this.container.dom.parentElement);
7105         var ctr = Roo.get(this.el.dom);
7106         
7107         var thd = this.getGridEl().select('thead',true).first();
7108         var tbd = this.getGridEl().select('tbody', true).first();
7109         var tfd = this.getGridEl().select('tfoot', true).first();
7110         
7111         var cw = ctr.getWidth();
7112         
7113         if (tbd) {
7114             
7115             tbd.setSize(ctr.getWidth(),
7116                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7117             );
7118             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7119             cw -= barsize;
7120         }
7121         cw = Math.max(cw, this.totalWidth);
7122         this.getGridEl().select('tr',true).setWidth(cw);
7123         // resize 'expandable coloumn?
7124         
7125         return; // we doe not have a view in this design..
7126         
7127     },
7128     onBodyScroll: function()
7129     {
7130         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7131         if(this.mainHead){
7132             this.mainHead.setStyle({
7133                 'position' : 'relative',
7134                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7135             });
7136         }
7137         
7138         if(this.lazyLoad){
7139             
7140             var scrollHeight = this.mainBody.dom.scrollHeight;
7141             
7142             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7143             
7144             var height = this.mainBody.getHeight();
7145             
7146             if(scrollHeight - height == scrollTop) {
7147                 
7148                 var total = this.ds.getTotalCount();
7149                 
7150                 if(this.footer.cursor + this.footer.pageSize < total){
7151                     
7152                     this.footer.ds.load({
7153                         params : {
7154                             start : this.footer.cursor + this.footer.pageSize,
7155                             limit : this.footer.pageSize
7156                         },
7157                         add : true
7158                     });
7159                 }
7160             }
7161             
7162         }
7163     },
7164     
7165     onHeaderChange : function()
7166     {
7167         var header = this.renderHeader();
7168         var table = this.el.select('table', true).first();
7169         
7170         this.mainHead.remove();
7171         this.mainHead = table.createChild(header, this.mainBody, false);
7172     },
7173     
7174     onHiddenChange : function(colModel, colIndex, hidden)
7175     {
7176         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7177         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7178         
7179         this.CSS.updateRule(thSelector, "display", "");
7180         this.CSS.updateRule(tdSelector, "display", "");
7181         
7182         if(hidden){
7183             this.CSS.updateRule(thSelector, "display", "none");
7184             this.CSS.updateRule(tdSelector, "display", "none");
7185         }
7186         
7187         this.onHeaderChange();
7188         this.onLoad();
7189     },
7190     
7191     setColumnWidth: function(col_index, width)
7192     {
7193         // width = "md-2 xs-2..."
7194         if(!this.colModel.config[col_index]) {
7195             return;
7196         }
7197         
7198         var w = width.split(" ");
7199         
7200         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7201         
7202         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7203         
7204         
7205         for(var j = 0; j < w.length; j++) {
7206             
7207             if(!w[j]) {
7208                 continue;
7209             }
7210             
7211             var size_cls = w[j].split("-");
7212             
7213             if(!Number.isInteger(size_cls[1] * 1)) {
7214                 continue;
7215             }
7216             
7217             if(!this.colModel.config[col_index][size_cls[0]]) {
7218                 continue;
7219             }
7220             
7221             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7222                 continue;
7223             }
7224             
7225             h_row[0].classList.replace(
7226                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7227                 "col-"+size_cls[0]+"-"+size_cls[1]
7228             );
7229             
7230             for(var i = 0; i < rows.length; i++) {
7231                 
7232                 var size_cls = w[j].split("-");
7233                 
7234                 if(!Number.isInteger(size_cls[1] * 1)) {
7235                     continue;
7236                 }
7237                 
7238                 if(!this.colModel.config[col_index][size_cls[0]]) {
7239                     continue;
7240                 }
7241                 
7242                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7243                     continue;
7244                 }
7245                 
7246                 rows[i].classList.replace(
7247                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7248                     "col-"+size_cls[0]+"-"+size_cls[1]
7249                 );
7250             }
7251             
7252             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7253         }
7254     }
7255 });
7256
7257  
7258
7259  /*
7260  * - LGPL
7261  *
7262  * table cell
7263  * 
7264  */
7265
7266 /**
7267  * @class Roo.bootstrap.TableCell
7268  * @extends Roo.bootstrap.Component
7269  * Bootstrap TableCell class
7270  * @cfg {String} html cell contain text
7271  * @cfg {String} cls cell class
7272  * @cfg {String} tag cell tag (td|th) default td
7273  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7274  * @cfg {String} align Aligns the content in a cell
7275  * @cfg {String} axis Categorizes cells
7276  * @cfg {String} bgcolor Specifies the background color of a cell
7277  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7278  * @cfg {Number} colspan Specifies the number of columns a cell should span
7279  * @cfg {String} headers Specifies one or more header cells a cell is related to
7280  * @cfg {Number} height Sets the height of a cell
7281  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7282  * @cfg {Number} rowspan Sets the number of rows a cell should span
7283  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7284  * @cfg {String} valign Vertical aligns the content in a cell
7285  * @cfg {Number} width Specifies the width of a cell
7286  * 
7287  * @constructor
7288  * Create a new TableCell
7289  * @param {Object} config The config object
7290  */
7291
7292 Roo.bootstrap.TableCell = function(config){
7293     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7294 };
7295
7296 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7297     
7298     html: false,
7299     cls: false,
7300     tag: false,
7301     abbr: false,
7302     align: false,
7303     axis: false,
7304     bgcolor: false,
7305     charoff: false,
7306     colspan: false,
7307     headers: false,
7308     height: false,
7309     nowrap: false,
7310     rowspan: false,
7311     scope: false,
7312     valign: false,
7313     width: false,
7314     
7315     
7316     getAutoCreate : function(){
7317         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7318         
7319         cfg = {
7320             tag: 'td'
7321         };
7322         
7323         if(this.tag){
7324             cfg.tag = this.tag;
7325         }
7326         
7327         if (this.html) {
7328             cfg.html=this.html
7329         }
7330         if (this.cls) {
7331             cfg.cls=this.cls
7332         }
7333         if (this.abbr) {
7334             cfg.abbr=this.abbr
7335         }
7336         if (this.align) {
7337             cfg.align=this.align
7338         }
7339         if (this.axis) {
7340             cfg.axis=this.axis
7341         }
7342         if (this.bgcolor) {
7343             cfg.bgcolor=this.bgcolor
7344         }
7345         if (this.charoff) {
7346             cfg.charoff=this.charoff
7347         }
7348         if (this.colspan) {
7349             cfg.colspan=this.colspan
7350         }
7351         if (this.headers) {
7352             cfg.headers=this.headers
7353         }
7354         if (this.height) {
7355             cfg.height=this.height
7356         }
7357         if (this.nowrap) {
7358             cfg.nowrap=this.nowrap
7359         }
7360         if (this.rowspan) {
7361             cfg.rowspan=this.rowspan
7362         }
7363         if (this.scope) {
7364             cfg.scope=this.scope
7365         }
7366         if (this.valign) {
7367             cfg.valign=this.valign
7368         }
7369         if (this.width) {
7370             cfg.width=this.width
7371         }
7372         
7373         
7374         return cfg;
7375     }
7376    
7377 });
7378
7379  
7380
7381  /*
7382  * - LGPL
7383  *
7384  * table row
7385  * 
7386  */
7387
7388 /**
7389  * @class Roo.bootstrap.TableRow
7390  * @extends Roo.bootstrap.Component
7391  * Bootstrap TableRow class
7392  * @cfg {String} cls row class
7393  * @cfg {String} align Aligns the content in a table row
7394  * @cfg {String} bgcolor Specifies a background color for a table row
7395  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7396  * @cfg {String} valign Vertical aligns the content in a table row
7397  * 
7398  * @constructor
7399  * Create a new TableRow
7400  * @param {Object} config The config object
7401  */
7402
7403 Roo.bootstrap.TableRow = function(config){
7404     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7405 };
7406
7407 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7408     
7409     cls: false,
7410     align: false,
7411     bgcolor: false,
7412     charoff: false,
7413     valign: false,
7414     
7415     getAutoCreate : function(){
7416         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7417         
7418         cfg = {
7419             tag: 'tr'
7420         };
7421             
7422         if(this.cls){
7423             cfg.cls = this.cls;
7424         }
7425         if(this.align){
7426             cfg.align = this.align;
7427         }
7428         if(this.bgcolor){
7429             cfg.bgcolor = this.bgcolor;
7430         }
7431         if(this.charoff){
7432             cfg.charoff = this.charoff;
7433         }
7434         if(this.valign){
7435             cfg.valign = this.valign;
7436         }
7437         
7438         return cfg;
7439     }
7440    
7441 });
7442
7443  
7444
7445  /*
7446  * - LGPL
7447  *
7448  * table body
7449  * 
7450  */
7451
7452 /**
7453  * @class Roo.bootstrap.TableBody
7454  * @extends Roo.bootstrap.Component
7455  * Bootstrap TableBody class
7456  * @cfg {String} cls element class
7457  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7458  * @cfg {String} align Aligns the content inside the element
7459  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7460  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7461  * 
7462  * @constructor
7463  * Create a new TableBody
7464  * @param {Object} config The config object
7465  */
7466
7467 Roo.bootstrap.TableBody = function(config){
7468     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7469 };
7470
7471 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7472     
7473     cls: false,
7474     tag: false,
7475     align: false,
7476     charoff: false,
7477     valign: false,
7478     
7479     getAutoCreate : function(){
7480         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7481         
7482         cfg = {
7483             tag: 'tbody'
7484         };
7485             
7486         if (this.cls) {
7487             cfg.cls=this.cls
7488         }
7489         if(this.tag){
7490             cfg.tag = this.tag;
7491         }
7492         
7493         if(this.align){
7494             cfg.align = this.align;
7495         }
7496         if(this.charoff){
7497             cfg.charoff = this.charoff;
7498         }
7499         if(this.valign){
7500             cfg.valign = this.valign;
7501         }
7502         
7503         return cfg;
7504     }
7505     
7506     
7507 //    initEvents : function()
7508 //    {
7509 //        
7510 //        if(!this.store){
7511 //            return;
7512 //        }
7513 //        
7514 //        this.store = Roo.factory(this.store, Roo.data);
7515 //        this.store.on('load', this.onLoad, this);
7516 //        
7517 //        this.store.load();
7518 //        
7519 //    },
7520 //    
7521 //    onLoad: function () 
7522 //    {   
7523 //        this.fireEvent('load', this);
7524 //    }
7525 //    
7526 //   
7527 });
7528
7529  
7530
7531  /*
7532  * Based on:
7533  * Ext JS Library 1.1.1
7534  * Copyright(c) 2006-2007, Ext JS, LLC.
7535  *
7536  * Originally Released Under LGPL - original licence link has changed is not relivant.
7537  *
7538  * Fork - LGPL
7539  * <script type="text/javascript">
7540  */
7541
7542 // as we use this in bootstrap.
7543 Roo.namespace('Roo.form');
7544  /**
7545  * @class Roo.form.Action
7546  * Internal Class used to handle form actions
7547  * @constructor
7548  * @param {Roo.form.BasicForm} el The form element or its id
7549  * @param {Object} config Configuration options
7550  */
7551
7552  
7553  
7554 // define the action interface
7555 Roo.form.Action = function(form, options){
7556     this.form = form;
7557     this.options = options || {};
7558 };
7559 /**
7560  * Client Validation Failed
7561  * @const 
7562  */
7563 Roo.form.Action.CLIENT_INVALID = 'client';
7564 /**
7565  * Server Validation Failed
7566  * @const 
7567  */
7568 Roo.form.Action.SERVER_INVALID = 'server';
7569  /**
7570  * Connect to Server Failed
7571  * @const 
7572  */
7573 Roo.form.Action.CONNECT_FAILURE = 'connect';
7574 /**
7575  * Reading Data from Server Failed
7576  * @const 
7577  */
7578 Roo.form.Action.LOAD_FAILURE = 'load';
7579
7580 Roo.form.Action.prototype = {
7581     type : 'default',
7582     failureType : undefined,
7583     response : undefined,
7584     result : undefined,
7585
7586     // interface method
7587     run : function(options){
7588
7589     },
7590
7591     // interface method
7592     success : function(response){
7593
7594     },
7595
7596     // interface method
7597     handleResponse : function(response){
7598
7599     },
7600
7601     // default connection failure
7602     failure : function(response){
7603         
7604         this.response = response;
7605         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7606         this.form.afterAction(this, false);
7607     },
7608
7609     processResponse : function(response){
7610         this.response = response;
7611         if(!response.responseText){
7612             return true;
7613         }
7614         this.result = this.handleResponse(response);
7615         return this.result;
7616     },
7617
7618     // utility functions used internally
7619     getUrl : function(appendParams){
7620         var url = this.options.url || this.form.url || this.form.el.dom.action;
7621         if(appendParams){
7622             var p = this.getParams();
7623             if(p){
7624                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7625             }
7626         }
7627         return url;
7628     },
7629
7630     getMethod : function(){
7631         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7632     },
7633
7634     getParams : function(){
7635         var bp = this.form.baseParams;
7636         var p = this.options.params;
7637         if(p){
7638             if(typeof p == "object"){
7639                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7640             }else if(typeof p == 'string' && bp){
7641                 p += '&' + Roo.urlEncode(bp);
7642             }
7643         }else if(bp){
7644             p = Roo.urlEncode(bp);
7645         }
7646         return p;
7647     },
7648
7649     createCallback : function(){
7650         return {
7651             success: this.success,
7652             failure: this.failure,
7653             scope: this,
7654             timeout: (this.form.timeout*1000),
7655             upload: this.form.fileUpload ? this.success : undefined
7656         };
7657     }
7658 };
7659
7660 Roo.form.Action.Submit = function(form, options){
7661     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7662 };
7663
7664 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7665     type : 'submit',
7666
7667     haveProgress : false,
7668     uploadComplete : false,
7669     
7670     // uploadProgress indicator.
7671     uploadProgress : function()
7672     {
7673         if (!this.form.progressUrl) {
7674             return;
7675         }
7676         
7677         if (!this.haveProgress) {
7678             Roo.MessageBox.progress("Uploading", "Uploading");
7679         }
7680         if (this.uploadComplete) {
7681            Roo.MessageBox.hide();
7682            return;
7683         }
7684         
7685         this.haveProgress = true;
7686    
7687         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7688         
7689         var c = new Roo.data.Connection();
7690         c.request({
7691             url : this.form.progressUrl,
7692             params: {
7693                 id : uid
7694             },
7695             method: 'GET',
7696             success : function(req){
7697                //console.log(data);
7698                 var rdata = false;
7699                 var edata;
7700                 try  {
7701                    rdata = Roo.decode(req.responseText)
7702                 } catch (e) {
7703                     Roo.log("Invalid data from server..");
7704                     Roo.log(edata);
7705                     return;
7706                 }
7707                 if (!rdata || !rdata.success) {
7708                     Roo.log(rdata);
7709                     Roo.MessageBox.alert(Roo.encode(rdata));
7710                     return;
7711                 }
7712                 var data = rdata.data;
7713                 
7714                 if (this.uploadComplete) {
7715                    Roo.MessageBox.hide();
7716                    return;
7717                 }
7718                    
7719                 if (data){
7720                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7721                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7722                     );
7723                 }
7724                 this.uploadProgress.defer(2000,this);
7725             },
7726        
7727             failure: function(data) {
7728                 Roo.log('progress url failed ');
7729                 Roo.log(data);
7730             },
7731             scope : this
7732         });
7733            
7734     },
7735     
7736     
7737     run : function()
7738     {
7739         // run get Values on the form, so it syncs any secondary forms.
7740         this.form.getValues();
7741         
7742         var o = this.options;
7743         var method = this.getMethod();
7744         var isPost = method == 'POST';
7745         if(o.clientValidation === false || this.form.isValid()){
7746             
7747             if (this.form.progressUrl) {
7748                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7749                     (new Date() * 1) + '' + Math.random());
7750                     
7751             } 
7752             
7753             
7754             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7755                 form:this.form.el.dom,
7756                 url:this.getUrl(!isPost),
7757                 method: method,
7758                 params:isPost ? this.getParams() : null,
7759                 isUpload: this.form.fileUpload
7760             }));
7761             
7762             this.uploadProgress();
7763
7764         }else if (o.clientValidation !== false){ // client validation failed
7765             this.failureType = Roo.form.Action.CLIENT_INVALID;
7766             this.form.afterAction(this, false);
7767         }
7768     },
7769
7770     success : function(response)
7771     {
7772         this.uploadComplete= true;
7773         if (this.haveProgress) {
7774             Roo.MessageBox.hide();
7775         }
7776         
7777         
7778         var result = this.processResponse(response);
7779         if(result === true || result.success){
7780             this.form.afterAction(this, true);
7781             return;
7782         }
7783         if(result.errors){
7784             this.form.markInvalid(result.errors);
7785             this.failureType = Roo.form.Action.SERVER_INVALID;
7786         }
7787         this.form.afterAction(this, false);
7788     },
7789     failure : function(response)
7790     {
7791         this.uploadComplete= true;
7792         if (this.haveProgress) {
7793             Roo.MessageBox.hide();
7794         }
7795         
7796         this.response = response;
7797         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7798         this.form.afterAction(this, false);
7799     },
7800     
7801     handleResponse : function(response){
7802         if(this.form.errorReader){
7803             var rs = this.form.errorReader.read(response);
7804             var errors = [];
7805             if(rs.records){
7806                 for(var i = 0, len = rs.records.length; i < len; i++) {
7807                     var r = rs.records[i];
7808                     errors[i] = r.data;
7809                 }
7810             }
7811             if(errors.length < 1){
7812                 errors = null;
7813             }
7814             return {
7815                 success : rs.success,
7816                 errors : errors
7817             };
7818         }
7819         var ret = false;
7820         try {
7821             ret = Roo.decode(response.responseText);
7822         } catch (e) {
7823             ret = {
7824                 success: false,
7825                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7826                 errors : []
7827             };
7828         }
7829         return ret;
7830         
7831     }
7832 });
7833
7834
7835 Roo.form.Action.Load = function(form, options){
7836     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7837     this.reader = this.form.reader;
7838 };
7839
7840 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7841     type : 'load',
7842
7843     run : function(){
7844         
7845         Roo.Ajax.request(Roo.apply(
7846                 this.createCallback(), {
7847                     method:this.getMethod(),
7848                     url:this.getUrl(false),
7849                     params:this.getParams()
7850         }));
7851     },
7852
7853     success : function(response){
7854         
7855         var result = this.processResponse(response);
7856         if(result === true || !result.success || !result.data){
7857             this.failureType = Roo.form.Action.LOAD_FAILURE;
7858             this.form.afterAction(this, false);
7859             return;
7860         }
7861         this.form.clearInvalid();
7862         this.form.setValues(result.data);
7863         this.form.afterAction(this, true);
7864     },
7865
7866     handleResponse : function(response){
7867         if(this.form.reader){
7868             var rs = this.form.reader.read(response);
7869             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7870             return {
7871                 success : rs.success,
7872                 data : data
7873             };
7874         }
7875         return Roo.decode(response.responseText);
7876     }
7877 });
7878
7879 Roo.form.Action.ACTION_TYPES = {
7880     'load' : Roo.form.Action.Load,
7881     'submit' : Roo.form.Action.Submit
7882 };/*
7883  * - LGPL
7884  *
7885  * form
7886  *
7887  */
7888
7889 /**
7890  * @class Roo.bootstrap.Form
7891  * @extends Roo.bootstrap.Component
7892  * Bootstrap Form class
7893  * @cfg {String} method  GET | POST (default POST)
7894  * @cfg {String} labelAlign top | left (default top)
7895  * @cfg {String} align left  | right - for navbars
7896  * @cfg {Boolean} loadMask load mask when submit (default true)
7897
7898  *
7899  * @constructor
7900  * Create a new Form
7901  * @param {Object} config The config object
7902  */
7903
7904
7905 Roo.bootstrap.Form = function(config){
7906     
7907     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7908     
7909     Roo.bootstrap.Form.popover.apply();
7910     
7911     this.addEvents({
7912         /**
7913          * @event clientvalidation
7914          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7915          * @param {Form} this
7916          * @param {Boolean} valid true if the form has passed client-side validation
7917          */
7918         clientvalidation: true,
7919         /**
7920          * @event beforeaction
7921          * Fires before any action is performed. Return false to cancel the action.
7922          * @param {Form} this
7923          * @param {Action} action The action to be performed
7924          */
7925         beforeaction: true,
7926         /**
7927          * @event actionfailed
7928          * Fires when an action fails.
7929          * @param {Form} this
7930          * @param {Action} action The action that failed
7931          */
7932         actionfailed : true,
7933         /**
7934          * @event actioncomplete
7935          * Fires when an action is completed.
7936          * @param {Form} this
7937          * @param {Action} action The action that completed
7938          */
7939         actioncomplete : true
7940     });
7941 };
7942
7943 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7944
7945      /**
7946      * @cfg {String} method
7947      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7948      */
7949     method : 'POST',
7950     /**
7951      * @cfg {String} url
7952      * The URL to use for form actions if one isn't supplied in the action options.
7953      */
7954     /**
7955      * @cfg {Boolean} fileUpload
7956      * Set to true if this form is a file upload.
7957      */
7958
7959     /**
7960      * @cfg {Object} baseParams
7961      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7962      */
7963
7964     /**
7965      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7966      */
7967     timeout: 30,
7968     /**
7969      * @cfg {Sting} align (left|right) for navbar forms
7970      */
7971     align : 'left',
7972
7973     // private
7974     activeAction : null,
7975
7976     /**
7977      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7978      * element by passing it or its id or mask the form itself by passing in true.
7979      * @type Mixed
7980      */
7981     waitMsgTarget : false,
7982
7983     loadMask : true,
7984     
7985     /**
7986      * @cfg {Boolean} errorMask (true|false) default false
7987      */
7988     errorMask : false,
7989     
7990     /**
7991      * @cfg {Number} maskOffset Default 100
7992      */
7993     maskOffset : 100,
7994     
7995     /**
7996      * @cfg {Boolean} maskBody
7997      */
7998     maskBody : false,
7999
8000     getAutoCreate : function(){
8001
8002         var cfg = {
8003             tag: 'form',
8004             method : this.method || 'POST',
8005             id : this.id || Roo.id(),
8006             cls : ''
8007         };
8008         if (this.parent().xtype.match(/^Nav/)) {
8009             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8010
8011         }
8012
8013         if (this.labelAlign == 'left' ) {
8014             cfg.cls += ' form-horizontal';
8015         }
8016
8017
8018         return cfg;
8019     },
8020     initEvents : function()
8021     {
8022         this.el.on('submit', this.onSubmit, this);
8023         // this was added as random key presses on the form where triggering form submit.
8024         this.el.on('keypress', function(e) {
8025             if (e.getCharCode() != 13) {
8026                 return true;
8027             }
8028             // we might need to allow it for textareas.. and some other items.
8029             // check e.getTarget().
8030
8031             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8032                 return true;
8033             }
8034
8035             Roo.log("keypress blocked");
8036
8037             e.preventDefault();
8038             return false;
8039         });
8040         
8041     },
8042     // private
8043     onSubmit : function(e){
8044         e.stopEvent();
8045     },
8046
8047      /**
8048      * Returns true if client-side validation on the form is successful.
8049      * @return Boolean
8050      */
8051     isValid : function(){
8052         var items = this.getItems();
8053         var valid = true;
8054         var target = false;
8055         
8056         items.each(function(f){
8057             
8058             if(f.validate()){
8059                 return;
8060             }
8061             
8062             Roo.log('invalid field: ' + f.name);
8063             
8064             valid = false;
8065
8066             if(!target && f.el.isVisible(true)){
8067                 target = f;
8068             }
8069            
8070         });
8071         
8072         if(this.errorMask && !valid){
8073             Roo.bootstrap.Form.popover.mask(this, target);
8074         }
8075         
8076         return valid;
8077     },
8078     
8079     /**
8080      * Returns true if any fields in this form have changed since their original load.
8081      * @return Boolean
8082      */
8083     isDirty : function(){
8084         var dirty = false;
8085         var items = this.getItems();
8086         items.each(function(f){
8087            if(f.isDirty()){
8088                dirty = true;
8089                return false;
8090            }
8091            return true;
8092         });
8093         return dirty;
8094     },
8095      /**
8096      * Performs a predefined action (submit or load) or custom actions you define on this form.
8097      * @param {String} actionName The name of the action type
8098      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8099      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8100      * accept other config options):
8101      * <pre>
8102 Property          Type             Description
8103 ----------------  ---------------  ----------------------------------------------------------------------------------
8104 url               String           The url for the action (defaults to the form's url)
8105 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8106 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8107 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8108                                    validate the form on the client (defaults to false)
8109      * </pre>
8110      * @return {BasicForm} this
8111      */
8112     doAction : function(action, options){
8113         if(typeof action == 'string'){
8114             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8115         }
8116         if(this.fireEvent('beforeaction', this, action) !== false){
8117             this.beforeAction(action);
8118             action.run.defer(100, action);
8119         }
8120         return this;
8121     },
8122
8123     // private
8124     beforeAction : function(action){
8125         var o = action.options;
8126         
8127         if(this.loadMask){
8128             
8129             if(this.maskBody){
8130                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8131             } else {
8132                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8133             }
8134         }
8135         // not really supported yet.. ??
8136
8137         //if(this.waitMsgTarget === true){
8138         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8139         //}else if(this.waitMsgTarget){
8140         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8141         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8142         //}else {
8143         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8144        // }
8145
8146     },
8147
8148     // private
8149     afterAction : function(action, success){
8150         this.activeAction = null;
8151         var o = action.options;
8152
8153         if(this.loadMask){
8154             
8155             if(this.maskBody){
8156                 Roo.get(document.body).unmask();
8157             } else {
8158                 this.el.unmask();
8159             }
8160         }
8161         
8162         //if(this.waitMsgTarget === true){
8163 //            this.el.unmask();
8164         //}else if(this.waitMsgTarget){
8165         //    this.waitMsgTarget.unmask();
8166         //}else{
8167         //    Roo.MessageBox.updateProgress(1);
8168         //    Roo.MessageBox.hide();
8169        // }
8170         //
8171         if(success){
8172             if(o.reset){
8173                 this.reset();
8174             }
8175             Roo.callback(o.success, o.scope, [this, action]);
8176             this.fireEvent('actioncomplete', this, action);
8177
8178         }else{
8179
8180             // failure condition..
8181             // we have a scenario where updates need confirming.
8182             // eg. if a locking scenario exists..
8183             // we look for { errors : { needs_confirm : true }} in the response.
8184             if (
8185                 (typeof(action.result) != 'undefined')  &&
8186                 (typeof(action.result.errors) != 'undefined')  &&
8187                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8188            ){
8189                 var _t = this;
8190                 Roo.log("not supported yet");
8191                  /*
8192
8193                 Roo.MessageBox.confirm(
8194                     "Change requires confirmation",
8195                     action.result.errorMsg,
8196                     function(r) {
8197                         if (r != 'yes') {
8198                             return;
8199                         }
8200                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8201                     }
8202
8203                 );
8204                 */
8205
8206
8207                 return;
8208             }
8209
8210             Roo.callback(o.failure, o.scope, [this, action]);
8211             // show an error message if no failed handler is set..
8212             if (!this.hasListener('actionfailed')) {
8213                 Roo.log("need to add dialog support");
8214                 /*
8215                 Roo.MessageBox.alert("Error",
8216                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8217                         action.result.errorMsg :
8218                         "Saving Failed, please check your entries or try again"
8219                 );
8220                 */
8221             }
8222
8223             this.fireEvent('actionfailed', this, action);
8224         }
8225
8226     },
8227     /**
8228      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8229      * @param {String} id The value to search for
8230      * @return Field
8231      */
8232     findField : function(id){
8233         var items = this.getItems();
8234         var field = items.get(id);
8235         if(!field){
8236              items.each(function(f){
8237                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8238                     field = f;
8239                     return false;
8240                 }
8241                 return true;
8242             });
8243         }
8244         return field || null;
8245     },
8246      /**
8247      * Mark fields in this form invalid in bulk.
8248      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8249      * @return {BasicForm} this
8250      */
8251     markInvalid : function(errors){
8252         if(errors instanceof Array){
8253             for(var i = 0, len = errors.length; i < len; i++){
8254                 var fieldError = errors[i];
8255                 var f = this.findField(fieldError.id);
8256                 if(f){
8257                     f.markInvalid(fieldError.msg);
8258                 }
8259             }
8260         }else{
8261             var field, id;
8262             for(id in errors){
8263                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8264                     field.markInvalid(errors[id]);
8265                 }
8266             }
8267         }
8268         //Roo.each(this.childForms || [], function (f) {
8269         //    f.markInvalid(errors);
8270         //});
8271
8272         return this;
8273     },
8274
8275     /**
8276      * Set values for fields in this form in bulk.
8277      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8278      * @return {BasicForm} this
8279      */
8280     setValues : function(values){
8281         if(values instanceof Array){ // array of objects
8282             for(var i = 0, len = values.length; i < len; i++){
8283                 var v = values[i];
8284                 var f = this.findField(v.id);
8285                 if(f){
8286                     f.setValue(v.value);
8287                     if(this.trackResetOnLoad){
8288                         f.originalValue = f.getValue();
8289                     }
8290                 }
8291             }
8292         }else{ // object hash
8293             var field, id;
8294             for(id in values){
8295                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8296
8297                     if (field.setFromData &&
8298                         field.valueField &&
8299                         field.displayField &&
8300                         // combos' with local stores can
8301                         // be queried via setValue()
8302                         // to set their value..
8303                         (field.store && !field.store.isLocal)
8304                         ) {
8305                         // it's a combo
8306                         var sd = { };
8307                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8308                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8309                         field.setFromData(sd);
8310
8311                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8312                         
8313                         field.setFromData(values);
8314                         
8315                     } else {
8316                         field.setValue(values[id]);
8317                     }
8318
8319
8320                     if(this.trackResetOnLoad){
8321                         field.originalValue = field.getValue();
8322                     }
8323                 }
8324             }
8325         }
8326
8327         //Roo.each(this.childForms || [], function (f) {
8328         //    f.setValues(values);
8329         //});
8330
8331         return this;
8332     },
8333
8334     /**
8335      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8336      * they are returned as an array.
8337      * @param {Boolean} asString
8338      * @return {Object}
8339      */
8340     getValues : function(asString){
8341         //if (this.childForms) {
8342             // copy values from the child forms
8343         //    Roo.each(this.childForms, function (f) {
8344         //        this.setValues(f.getValues());
8345         //    }, this);
8346         //}
8347
8348
8349
8350         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8351         if(asString === true){
8352             return fs;
8353         }
8354         return Roo.urlDecode(fs);
8355     },
8356
8357     /**
8358      * Returns the fields in this form as an object with key/value pairs.
8359      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8360      * @return {Object}
8361      */
8362     getFieldValues : function(with_hidden)
8363     {
8364         var items = this.getItems();
8365         var ret = {};
8366         items.each(function(f){
8367             
8368             if (!f.getName()) {
8369                 return;
8370             }
8371             
8372             var v = f.getValue();
8373             
8374             if (f.inputType =='radio') {
8375                 if (typeof(ret[f.getName()]) == 'undefined') {
8376                     ret[f.getName()] = ''; // empty..
8377                 }
8378
8379                 if (!f.el.dom.checked) {
8380                     return;
8381
8382                 }
8383                 v = f.el.dom.value;
8384
8385             }
8386             
8387             if(f.xtype == 'MoneyField'){
8388                 ret[f.currencyName] = f.getCurrency();
8389             }
8390
8391             // not sure if this supported any more..
8392             if ((typeof(v) == 'object') && f.getRawValue) {
8393                 v = f.getRawValue() ; // dates..
8394             }
8395             // combo boxes where name != hiddenName...
8396             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8397                 ret[f.name] = f.getRawValue();
8398             }
8399             ret[f.getName()] = v;
8400         });
8401
8402         return ret;
8403     },
8404
8405     /**
8406      * Clears all invalid messages in this form.
8407      * @return {BasicForm} this
8408      */
8409     clearInvalid : function(){
8410         var items = this.getItems();
8411
8412         items.each(function(f){
8413            f.clearInvalid();
8414         });
8415
8416         return this;
8417     },
8418
8419     /**
8420      * Resets this form.
8421      * @return {BasicForm} this
8422      */
8423     reset : function(){
8424         var items = this.getItems();
8425         items.each(function(f){
8426             f.reset();
8427         });
8428
8429         Roo.each(this.childForms || [], function (f) {
8430             f.reset();
8431         });
8432
8433
8434         return this;
8435     },
8436     
8437     getItems : function()
8438     {
8439         var r=new Roo.util.MixedCollection(false, function(o){
8440             return o.id || (o.id = Roo.id());
8441         });
8442         var iter = function(el) {
8443             if (el.inputEl) {
8444                 r.add(el);
8445             }
8446             if (!el.items) {
8447                 return;
8448             }
8449             Roo.each(el.items,function(e) {
8450                 iter(e);
8451             });
8452         };
8453
8454         iter(this);
8455         return r;
8456     },
8457     
8458     hideFields : function(items)
8459     {
8460         Roo.each(items, function(i){
8461             
8462             var f = this.findField(i);
8463             
8464             if(!f){
8465                 return;
8466             }
8467             
8468             f.hide();
8469             
8470         }, this);
8471     },
8472     
8473     showFields : function(items)
8474     {
8475         Roo.each(items, function(i){
8476             
8477             var f = this.findField(i);
8478             
8479             if(!f){
8480                 return;
8481             }
8482             
8483             f.show();
8484             
8485         }, this);
8486     }
8487
8488 });
8489
8490 Roo.apply(Roo.bootstrap.Form, {
8491     
8492     popover : {
8493         
8494         padding : 5,
8495         
8496         isApplied : false,
8497         
8498         isMasked : false,
8499         
8500         form : false,
8501         
8502         target : false,
8503         
8504         toolTip : false,
8505         
8506         intervalID : false,
8507         
8508         maskEl : false,
8509         
8510         apply : function()
8511         {
8512             if(this.isApplied){
8513                 return;
8514             }
8515             
8516             this.maskEl = {
8517                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8518                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8519                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8520                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8521             };
8522             
8523             this.maskEl.top.enableDisplayMode("block");
8524             this.maskEl.left.enableDisplayMode("block");
8525             this.maskEl.bottom.enableDisplayMode("block");
8526             this.maskEl.right.enableDisplayMode("block");
8527             
8528             this.toolTip = new Roo.bootstrap.Tooltip({
8529                 cls : 'roo-form-error-popover',
8530                 alignment : {
8531                     'left' : ['r-l', [-2,0], 'right'],
8532                     'right' : ['l-r', [2,0], 'left'],
8533                     'bottom' : ['tl-bl', [0,2], 'top'],
8534                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8535                 }
8536             });
8537             
8538             this.toolTip.render(Roo.get(document.body));
8539
8540             this.toolTip.el.enableDisplayMode("block");
8541             
8542             Roo.get(document.body).on('click', function(){
8543                 this.unmask();
8544             }, this);
8545             
8546             Roo.get(document.body).on('touchstart', function(){
8547                 this.unmask();
8548             }, this);
8549             
8550             this.isApplied = true
8551         },
8552         
8553         mask : function(form, target)
8554         {
8555             this.form = form;
8556             
8557             this.target = target;
8558             
8559             if(!this.form.errorMask || !target.el){
8560                 return;
8561             }
8562             
8563             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8564             
8565             Roo.log(scrollable);
8566             
8567             var ot = this.target.el.calcOffsetsTo(scrollable);
8568             
8569             var scrollTo = ot[1] - this.form.maskOffset;
8570             
8571             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8572             
8573             scrollable.scrollTo('top', scrollTo);
8574             
8575             var box = this.target.el.getBox();
8576             Roo.log(box);
8577             var zIndex = Roo.bootstrap.Modal.zIndex++;
8578
8579             
8580             this.maskEl.top.setStyle('position', 'absolute');
8581             this.maskEl.top.setStyle('z-index', zIndex);
8582             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8583             this.maskEl.top.setLeft(0);
8584             this.maskEl.top.setTop(0);
8585             this.maskEl.top.show();
8586             
8587             this.maskEl.left.setStyle('position', 'absolute');
8588             this.maskEl.left.setStyle('z-index', zIndex);
8589             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8590             this.maskEl.left.setLeft(0);
8591             this.maskEl.left.setTop(box.y - this.padding);
8592             this.maskEl.left.show();
8593
8594             this.maskEl.bottom.setStyle('position', 'absolute');
8595             this.maskEl.bottom.setStyle('z-index', zIndex);
8596             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8597             this.maskEl.bottom.setLeft(0);
8598             this.maskEl.bottom.setTop(box.bottom + this.padding);
8599             this.maskEl.bottom.show();
8600
8601             this.maskEl.right.setStyle('position', 'absolute');
8602             this.maskEl.right.setStyle('z-index', zIndex);
8603             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8604             this.maskEl.right.setLeft(box.right + this.padding);
8605             this.maskEl.right.setTop(box.y - this.padding);
8606             this.maskEl.right.show();
8607
8608             this.toolTip.bindEl = this.target.el;
8609
8610             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8611
8612             var tip = this.target.blankText;
8613
8614             if(this.target.getValue() !== '' ) {
8615                 
8616                 if (this.target.invalidText.length) {
8617                     tip = this.target.invalidText;
8618                 } else if (this.target.regexText.length){
8619                     tip = this.target.regexText;
8620                 }
8621             }
8622
8623             this.toolTip.show(tip);
8624
8625             this.intervalID = window.setInterval(function() {
8626                 Roo.bootstrap.Form.popover.unmask();
8627             }, 10000);
8628
8629             window.onwheel = function(){ return false;};
8630             
8631             (function(){ this.isMasked = true; }).defer(500, this);
8632             
8633         },
8634         
8635         unmask : function()
8636         {
8637             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8638                 return;
8639             }
8640             
8641             this.maskEl.top.setStyle('position', 'absolute');
8642             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8643             this.maskEl.top.hide();
8644
8645             this.maskEl.left.setStyle('position', 'absolute');
8646             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8647             this.maskEl.left.hide();
8648
8649             this.maskEl.bottom.setStyle('position', 'absolute');
8650             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8651             this.maskEl.bottom.hide();
8652
8653             this.maskEl.right.setStyle('position', 'absolute');
8654             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8655             this.maskEl.right.hide();
8656             
8657             this.toolTip.hide();
8658             
8659             this.toolTip.el.hide();
8660             
8661             window.onwheel = function(){ return true;};
8662             
8663             if(this.intervalID){
8664                 window.clearInterval(this.intervalID);
8665                 this.intervalID = false;
8666             }
8667             
8668             this.isMasked = false;
8669             
8670         }
8671         
8672     }
8673     
8674 });
8675
8676 /*
8677  * Based on:
8678  * Ext JS Library 1.1.1
8679  * Copyright(c) 2006-2007, Ext JS, LLC.
8680  *
8681  * Originally Released Under LGPL - original licence link has changed is not relivant.
8682  *
8683  * Fork - LGPL
8684  * <script type="text/javascript">
8685  */
8686 /**
8687  * @class Roo.form.VTypes
8688  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8689  * @singleton
8690  */
8691 Roo.form.VTypes = function(){
8692     // closure these in so they are only created once.
8693     var alpha = /^[a-zA-Z_]+$/;
8694     var alphanum = /^[a-zA-Z0-9_]+$/;
8695     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8696     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8697
8698     // All these messages and functions are configurable
8699     return {
8700         /**
8701          * The function used to validate email addresses
8702          * @param {String} value The email address
8703          */
8704         'email' : function(v){
8705             return email.test(v);
8706         },
8707         /**
8708          * The error text to display when the email validation function returns false
8709          * @type String
8710          */
8711         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8712         /**
8713          * The keystroke filter mask to be applied on email input
8714          * @type RegExp
8715          */
8716         'emailMask' : /[a-z0-9_\.\-@]/i,
8717
8718         /**
8719          * The function used to validate URLs
8720          * @param {String} value The URL
8721          */
8722         'url' : function(v){
8723             return url.test(v);
8724         },
8725         /**
8726          * The error text to display when the url validation function returns false
8727          * @type String
8728          */
8729         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8730         
8731         /**
8732          * The function used to validate alpha values
8733          * @param {String} value The value
8734          */
8735         'alpha' : function(v){
8736             return alpha.test(v);
8737         },
8738         /**
8739          * The error text to display when the alpha validation function returns false
8740          * @type String
8741          */
8742         'alphaText' : 'This field should only contain letters and _',
8743         /**
8744          * The keystroke filter mask to be applied on alpha input
8745          * @type RegExp
8746          */
8747         'alphaMask' : /[a-z_]/i,
8748
8749         /**
8750          * The function used to validate alphanumeric values
8751          * @param {String} value The value
8752          */
8753         'alphanum' : function(v){
8754             return alphanum.test(v);
8755         },
8756         /**
8757          * The error text to display when the alphanumeric validation function returns false
8758          * @type String
8759          */
8760         'alphanumText' : 'This field should only contain letters, numbers and _',
8761         /**
8762          * The keystroke filter mask to be applied on alphanumeric input
8763          * @type RegExp
8764          */
8765         'alphanumMask' : /[a-z0-9_]/i
8766     };
8767 }();/*
8768  * - LGPL
8769  *
8770  * Input
8771  * 
8772  */
8773
8774 /**
8775  * @class Roo.bootstrap.Input
8776  * @extends Roo.bootstrap.Component
8777  * Bootstrap Input class
8778  * @cfg {Boolean} disabled is it disabled
8779  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8780  * @cfg {String} name name of the input
8781  * @cfg {string} fieldLabel - the label associated
8782  * @cfg {string} placeholder - placeholder to put in text.
8783  * @cfg {string}  before - input group add on before
8784  * @cfg {string} after - input group add on after
8785  * @cfg {string} size - (lg|sm) or leave empty..
8786  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8787  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8788  * @cfg {Number} md colspan out of 12 for computer-sized screens
8789  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8790  * @cfg {string} value default value of the input
8791  * @cfg {Number} labelWidth set the width of label 
8792  * @cfg {Number} labellg set the width of label (1-12)
8793  * @cfg {Number} labelmd set the width of label (1-12)
8794  * @cfg {Number} labelsm set the width of label (1-12)
8795  * @cfg {Number} labelxs set the width of label (1-12)
8796  * @cfg {String} labelAlign (top|left)
8797  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8798  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8799  * @cfg {String} indicatorpos (left|right) default left
8800  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8801  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8802
8803  * @cfg {String} align (left|center|right) Default left
8804  * @cfg {Boolean} forceFeedback (true|false) Default false
8805  * 
8806  * @constructor
8807  * Create a new Input
8808  * @param {Object} config The config object
8809  */
8810
8811 Roo.bootstrap.Input = function(config){
8812     
8813     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8814     
8815     this.addEvents({
8816         /**
8817          * @event focus
8818          * Fires when this field receives input focus.
8819          * @param {Roo.form.Field} this
8820          */
8821         focus : true,
8822         /**
8823          * @event blur
8824          * Fires when this field loses input focus.
8825          * @param {Roo.form.Field} this
8826          */
8827         blur : true,
8828         /**
8829          * @event specialkey
8830          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8831          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8832          * @param {Roo.form.Field} this
8833          * @param {Roo.EventObject} e The event object
8834          */
8835         specialkey : true,
8836         /**
8837          * @event change
8838          * Fires just before the field blurs if the field value has changed.
8839          * @param {Roo.form.Field} this
8840          * @param {Mixed} newValue The new value
8841          * @param {Mixed} oldValue The original value
8842          */
8843         change : true,
8844         /**
8845          * @event invalid
8846          * Fires after the field has been marked as invalid.
8847          * @param {Roo.form.Field} this
8848          * @param {String} msg The validation message
8849          */
8850         invalid : true,
8851         /**
8852          * @event valid
8853          * Fires after the field has been validated with no errors.
8854          * @param {Roo.form.Field} this
8855          */
8856         valid : true,
8857          /**
8858          * @event keyup
8859          * Fires after the key up
8860          * @param {Roo.form.Field} this
8861          * @param {Roo.EventObject}  e The event Object
8862          */
8863         keyup : true
8864     });
8865 };
8866
8867 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8868      /**
8869      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8870       automatic validation (defaults to "keyup").
8871      */
8872     validationEvent : "keyup",
8873      /**
8874      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8875      */
8876     validateOnBlur : true,
8877     /**
8878      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8879      */
8880     validationDelay : 250,
8881      /**
8882      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8883      */
8884     focusClass : "x-form-focus",  // not needed???
8885     
8886        
8887     /**
8888      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8889      */
8890     invalidClass : "has-warning",
8891     
8892     /**
8893      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8894      */
8895     validClass : "has-success",
8896     
8897     /**
8898      * @cfg {Boolean} hasFeedback (true|false) default true
8899      */
8900     hasFeedback : true,
8901     
8902     /**
8903      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8904      */
8905     invalidFeedbackClass : "glyphicon-warning-sign",
8906     
8907     /**
8908      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8909      */
8910     validFeedbackClass : "glyphicon-ok",
8911     
8912     /**
8913      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8914      */
8915     selectOnFocus : false,
8916     
8917      /**
8918      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8919      */
8920     maskRe : null,
8921        /**
8922      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8923      */
8924     vtype : null,
8925     
8926       /**
8927      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8928      */
8929     disableKeyFilter : false,
8930     
8931        /**
8932      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8933      */
8934     disabled : false,
8935      /**
8936      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8937      */
8938     allowBlank : true,
8939     /**
8940      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8941      */
8942     blankText : "Please complete this mandatory field",
8943     
8944      /**
8945      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8946      */
8947     minLength : 0,
8948     /**
8949      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8950      */
8951     maxLength : Number.MAX_VALUE,
8952     /**
8953      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8954      */
8955     minLengthText : "The minimum length for this field is {0}",
8956     /**
8957      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8958      */
8959     maxLengthText : "The maximum length for this field is {0}",
8960   
8961     
8962     /**
8963      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8964      * If available, this function will be called only after the basic validators all return true, and will be passed the
8965      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8966      */
8967     validator : null,
8968     /**
8969      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8970      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8971      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8972      */
8973     regex : null,
8974     /**
8975      * @cfg {String} regexText -- Depricated - use Invalid Text
8976      */
8977     regexText : "",
8978     
8979     /**
8980      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8981      */
8982     invalidText : "",
8983     
8984     
8985     
8986     autocomplete: false,
8987     
8988     
8989     fieldLabel : '',
8990     inputType : 'text',
8991     
8992     name : false,
8993     placeholder: false,
8994     before : false,
8995     after : false,
8996     size : false,
8997     hasFocus : false,
8998     preventMark: false,
8999     isFormField : true,
9000     value : '',
9001     labelWidth : 2,
9002     labelAlign : false,
9003     readOnly : false,
9004     align : false,
9005     formatedValue : false,
9006     forceFeedback : false,
9007     
9008     indicatorpos : 'left',
9009     
9010     labellg : 0,
9011     labelmd : 0,
9012     labelsm : 0,
9013     labelxs : 0,
9014     
9015     capture : '',
9016     accept : '',
9017     
9018     parentLabelAlign : function()
9019     {
9020         var parent = this;
9021         while (parent.parent()) {
9022             parent = parent.parent();
9023             if (typeof(parent.labelAlign) !='undefined') {
9024                 return parent.labelAlign;
9025             }
9026         }
9027         return 'left';
9028         
9029     },
9030     
9031     getAutoCreate : function()
9032     {
9033         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9034         
9035         var id = Roo.id();
9036         
9037         var cfg = {};
9038         
9039         if(this.inputType != 'hidden'){
9040             cfg.cls = 'form-group' //input-group
9041         }
9042         
9043         var input =  {
9044             tag: 'input',
9045             id : id,
9046             type : this.inputType,
9047             value : this.value,
9048             cls : 'form-control',
9049             placeholder : this.placeholder || '',
9050             autocomplete : this.autocomplete || 'new-password'
9051         };
9052         
9053         if(this.capture.length){
9054             input.capture = this.capture;
9055         }
9056         
9057         if(this.accept.length){
9058             input.accept = this.accept + "/*";
9059         }
9060         
9061         if(this.align){
9062             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9063         }
9064         
9065         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9066             input.maxLength = this.maxLength;
9067         }
9068         
9069         if (this.disabled) {
9070             input.disabled=true;
9071         }
9072         
9073         if (this.readOnly) {
9074             input.readonly=true;
9075         }
9076         
9077         if (this.name) {
9078             input.name = this.name;
9079         }
9080         
9081         if (this.size) {
9082             input.cls += ' input-' + this.size;
9083         }
9084         
9085         var settings=this;
9086         ['xs','sm','md','lg'].map(function(size){
9087             if (settings[size]) {
9088                 cfg.cls += ' col-' + size + '-' + settings[size];
9089             }
9090         });
9091         
9092         var inputblock = input;
9093         
9094         var feedback = {
9095             tag: 'span',
9096             cls: 'glyphicon form-control-feedback'
9097         };
9098             
9099         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9100             
9101             inputblock = {
9102                 cls : 'has-feedback',
9103                 cn :  [
9104                     input,
9105                     feedback
9106                 ] 
9107             };  
9108         }
9109         
9110         if (this.before || this.after) {
9111             
9112             inputblock = {
9113                 cls : 'input-group',
9114                 cn :  [] 
9115             };
9116             
9117             if (this.before && typeof(this.before) == 'string') {
9118                 
9119                 inputblock.cn.push({
9120                     tag :'span',
9121                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9122                     html : this.before
9123                 });
9124             }
9125             if (this.before && typeof(this.before) == 'object') {
9126                 this.before = Roo.factory(this.before);
9127                 
9128                 inputblock.cn.push({
9129                     tag :'span',
9130                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9131                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9132                 });
9133             }
9134             
9135             inputblock.cn.push(input);
9136             
9137             if (this.after && typeof(this.after) == 'string') {
9138                 inputblock.cn.push({
9139                     tag :'span',
9140                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9141                     html : this.after
9142                 });
9143             }
9144             if (this.after && typeof(this.after) == 'object') {
9145                 this.after = Roo.factory(this.after);
9146                 
9147                 inputblock.cn.push({
9148                     tag :'span',
9149                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9150                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9151                 });
9152             }
9153             
9154             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9155                 inputblock.cls += ' has-feedback';
9156                 inputblock.cn.push(feedback);
9157             }
9158         };
9159         var indicator = {
9160             tag : 'i',
9161             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9162             tooltip : 'This field is required'
9163         };
9164         if (Roo.bootstrap.version == 4) {
9165             indicator = {
9166                 tag : 'i',
9167                 style : 'display-none'
9168             };
9169         }
9170         if (align ==='left' && this.fieldLabel.length) {
9171             
9172             cfg.cls += ' roo-form-group-label-left row';
9173             
9174             cfg.cn = [
9175                 indicator,
9176                 {
9177                     tag: 'label',
9178                     'for' :  id,
9179                     cls : 'control-label col-form-label',
9180                     html : this.fieldLabel
9181
9182                 },
9183                 {
9184                     cls : "", 
9185                     cn: [
9186                         inputblock
9187                     ]
9188                 }
9189             ];
9190             
9191             var labelCfg = cfg.cn[1];
9192             var contentCfg = cfg.cn[2];
9193             
9194             if(this.indicatorpos == 'right'){
9195                 cfg.cn = [
9196                     {
9197                         tag: 'label',
9198                         'for' :  id,
9199                         cls : 'control-label col-form-label',
9200                         cn : [
9201                             {
9202                                 tag : 'span',
9203                                 html : this.fieldLabel
9204                             },
9205                             indicator
9206                         ]
9207                     },
9208                     {
9209                         cls : "",
9210                         cn: [
9211                             inputblock
9212                         ]
9213                     }
9214
9215                 ];
9216                 
9217                 labelCfg = cfg.cn[0];
9218                 contentCfg = cfg.cn[1];
9219             
9220             }
9221             
9222             if(this.labelWidth > 12){
9223                 labelCfg.style = "width: " + this.labelWidth + 'px';
9224             }
9225             
9226             if(this.labelWidth < 13 && this.labelmd == 0){
9227                 this.labelmd = this.labelWidth;
9228             }
9229             
9230             if(this.labellg > 0){
9231                 labelCfg.cls += ' col-lg-' + this.labellg;
9232                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9233             }
9234             
9235             if(this.labelmd > 0){
9236                 labelCfg.cls += ' col-md-' + this.labelmd;
9237                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9238             }
9239             
9240             if(this.labelsm > 0){
9241                 labelCfg.cls += ' col-sm-' + this.labelsm;
9242                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9243             }
9244             
9245             if(this.labelxs > 0){
9246                 labelCfg.cls += ' col-xs-' + this.labelxs;
9247                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9248             }
9249             
9250             
9251         } else if ( this.fieldLabel.length) {
9252                 
9253             cfg.cn = [
9254                 {
9255                     tag : 'i',
9256                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9257                     tooltip : 'This field is required'
9258                 },
9259                 {
9260                     tag: 'label',
9261                    //cls : 'input-group-addon',
9262                     html : this.fieldLabel
9263
9264                 },
9265
9266                inputblock
9267
9268            ];
9269            
9270            if(this.indicatorpos == 'right'){
9271                 
9272                 cfg.cn = [
9273                     {
9274                         tag: 'label',
9275                        //cls : 'input-group-addon',
9276                         html : this.fieldLabel
9277
9278                     },
9279                     {
9280                         tag : 'i',
9281                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9282                         tooltip : 'This field is required'
9283                     },
9284
9285                    inputblock
9286
9287                ];
9288
9289             }
9290
9291         } else {
9292             
9293             cfg.cn = [
9294
9295                     inputblock
9296
9297             ];
9298                 
9299                 
9300         };
9301         
9302         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9303            cfg.cls += ' navbar-form';
9304         }
9305         
9306         if (this.parentType === 'NavGroup') {
9307            cfg.cls += ' navbar-form';
9308            cfg.tag = 'li';
9309         }
9310         
9311         return cfg;
9312         
9313     },
9314     /**
9315      * return the real input element.
9316      */
9317     inputEl: function ()
9318     {
9319         return this.el.select('input.form-control',true).first();
9320     },
9321     
9322     tooltipEl : function()
9323     {
9324         return this.inputEl();
9325     },
9326     
9327     indicatorEl : function()
9328     {
9329         if (Roo.bootstrap.version == 4) {
9330             return false; // not enabled in v4 yet.
9331         }
9332         
9333         var indicator = this.el.select('i.roo-required-indicator',true).first();
9334         
9335         if(!indicator){
9336             return false;
9337         }
9338         
9339         return indicator;
9340         
9341     },
9342     
9343     setDisabled : function(v)
9344     {
9345         var i  = this.inputEl().dom;
9346         if (!v) {
9347             i.removeAttribute('disabled');
9348             return;
9349             
9350         }
9351         i.setAttribute('disabled','true');
9352     },
9353     initEvents : function()
9354     {
9355           
9356         this.inputEl().on("keydown" , this.fireKey,  this);
9357         this.inputEl().on("focus", this.onFocus,  this);
9358         this.inputEl().on("blur", this.onBlur,  this);
9359         
9360         this.inputEl().relayEvent('keyup', this);
9361         
9362         this.indicator = this.indicatorEl();
9363         
9364         if(this.indicator){
9365             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9366         }
9367  
9368         // reference to original value for reset
9369         this.originalValue = this.getValue();
9370         //Roo.form.TextField.superclass.initEvents.call(this);
9371         if(this.validationEvent == 'keyup'){
9372             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9373             this.inputEl().on('keyup', this.filterValidation, this);
9374         }
9375         else if(this.validationEvent !== false){
9376             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9377         }
9378         
9379         if(this.selectOnFocus){
9380             this.on("focus", this.preFocus, this);
9381             
9382         }
9383         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9384             this.inputEl().on("keypress", this.filterKeys, this);
9385         } else {
9386             this.inputEl().relayEvent('keypress', this);
9387         }
9388        /* if(this.grow){
9389             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9390             this.el.on("click", this.autoSize,  this);
9391         }
9392         */
9393         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9394             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9395         }
9396         
9397         if (typeof(this.before) == 'object') {
9398             this.before.render(this.el.select('.roo-input-before',true).first());
9399         }
9400         if (typeof(this.after) == 'object') {
9401             this.after.render(this.el.select('.roo-input-after',true).first());
9402         }
9403         
9404         this.inputEl().on('change', this.onChange, this);
9405         
9406     },
9407     filterValidation : function(e){
9408         if(!e.isNavKeyPress()){
9409             this.validationTask.delay(this.validationDelay);
9410         }
9411     },
9412      /**
9413      * Validates the field value
9414      * @return {Boolean} True if the value is valid, else false
9415      */
9416     validate : function(){
9417         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9418         if(this.disabled || this.validateValue(this.getRawValue())){
9419             this.markValid();
9420             return true;
9421         }
9422         
9423         this.markInvalid();
9424         return false;
9425     },
9426     
9427     
9428     /**
9429      * Validates a value according to the field's validation rules and marks the field as invalid
9430      * if the validation fails
9431      * @param {Mixed} value The value to validate
9432      * @return {Boolean} True if the value is valid, else false
9433      */
9434     validateValue : function(value)
9435     {
9436         if(this.getVisibilityEl().hasClass('hidden')){
9437             return true;
9438         }
9439         
9440         if(value.length < 1)  { // if it's blank
9441             if(this.allowBlank){
9442                 return true;
9443             }
9444             return false;
9445         }
9446         
9447         if(value.length < this.minLength){
9448             return false;
9449         }
9450         if(value.length > this.maxLength){
9451             return false;
9452         }
9453         if(this.vtype){
9454             var vt = Roo.form.VTypes;
9455             if(!vt[this.vtype](value, this)){
9456                 return false;
9457             }
9458         }
9459         if(typeof this.validator == "function"){
9460             var msg = this.validator(value);
9461             if(msg !== true){
9462                 return false;
9463             }
9464             if (typeof(msg) == 'string') {
9465                 this.invalidText = msg;
9466             }
9467         }
9468         
9469         if(this.regex && !this.regex.test(value)){
9470             return false;
9471         }
9472         
9473         return true;
9474     },
9475     
9476      // private
9477     fireKey : function(e){
9478         //Roo.log('field ' + e.getKey());
9479         if(e.isNavKeyPress()){
9480             this.fireEvent("specialkey", this, e);
9481         }
9482     },
9483     focus : function (selectText){
9484         if(this.rendered){
9485             this.inputEl().focus();
9486             if(selectText === true){
9487                 this.inputEl().dom.select();
9488             }
9489         }
9490         return this;
9491     } ,
9492     
9493     onFocus : function(){
9494         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9495            // this.el.addClass(this.focusClass);
9496         }
9497         if(!this.hasFocus){
9498             this.hasFocus = true;
9499             this.startValue = this.getValue();
9500             this.fireEvent("focus", this);
9501         }
9502     },
9503     
9504     beforeBlur : Roo.emptyFn,
9505
9506     
9507     // private
9508     onBlur : function(){
9509         this.beforeBlur();
9510         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9511             //this.el.removeClass(this.focusClass);
9512         }
9513         this.hasFocus = false;
9514         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9515             this.validate();
9516         }
9517         var v = this.getValue();
9518         if(String(v) !== String(this.startValue)){
9519             this.fireEvent('change', this, v, this.startValue);
9520         }
9521         this.fireEvent("blur", this);
9522     },
9523     
9524     onChange : function(e)
9525     {
9526         var v = this.getValue();
9527         if(String(v) !== String(this.startValue)){
9528             this.fireEvent('change', this, v, this.startValue);
9529         }
9530         
9531     },
9532     
9533     /**
9534      * Resets the current field value to the originally loaded value and clears any validation messages
9535      */
9536     reset : function(){
9537         this.setValue(this.originalValue);
9538         this.validate();
9539     },
9540      /**
9541      * Returns the name of the field
9542      * @return {Mixed} name The name field
9543      */
9544     getName: function(){
9545         return this.name;
9546     },
9547      /**
9548      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9549      * @return {Mixed} value The field value
9550      */
9551     getValue : function(){
9552         
9553         var v = this.inputEl().getValue();
9554         
9555         return v;
9556     },
9557     /**
9558      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9559      * @return {Mixed} value The field value
9560      */
9561     getRawValue : function(){
9562         var v = this.inputEl().getValue();
9563         
9564         return v;
9565     },
9566     
9567     /**
9568      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9569      * @param {Mixed} value The value to set
9570      */
9571     setRawValue : function(v){
9572         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9573     },
9574     
9575     selectText : function(start, end){
9576         var v = this.getRawValue();
9577         if(v.length > 0){
9578             start = start === undefined ? 0 : start;
9579             end = end === undefined ? v.length : end;
9580             var d = this.inputEl().dom;
9581             if(d.setSelectionRange){
9582                 d.setSelectionRange(start, end);
9583             }else if(d.createTextRange){
9584                 var range = d.createTextRange();
9585                 range.moveStart("character", start);
9586                 range.moveEnd("character", v.length-end);
9587                 range.select();
9588             }
9589         }
9590     },
9591     
9592     /**
9593      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9594      * @param {Mixed} value The value to set
9595      */
9596     setValue : function(v){
9597         this.value = v;
9598         if(this.rendered){
9599             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9600             this.validate();
9601         }
9602     },
9603     
9604     /*
9605     processValue : function(value){
9606         if(this.stripCharsRe){
9607             var newValue = value.replace(this.stripCharsRe, '');
9608             if(newValue !== value){
9609                 this.setRawValue(newValue);
9610                 return newValue;
9611             }
9612         }
9613         return value;
9614     },
9615   */
9616     preFocus : function(){
9617         
9618         if(this.selectOnFocus){
9619             this.inputEl().dom.select();
9620         }
9621     },
9622     filterKeys : function(e){
9623         var k = e.getKey();
9624         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9625             return;
9626         }
9627         var c = e.getCharCode(), cc = String.fromCharCode(c);
9628         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9629             return;
9630         }
9631         if(!this.maskRe.test(cc)){
9632             e.stopEvent();
9633         }
9634     },
9635      /**
9636      * Clear any invalid styles/messages for this field
9637      */
9638     clearInvalid : function(){
9639         
9640         if(!this.el || this.preventMark){ // not rendered
9641             return;
9642         }
9643         
9644      
9645         this.el.removeClass(this.invalidClass);
9646         
9647         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9648             
9649             var feedback = this.el.select('.form-control-feedback', true).first();
9650             
9651             if(feedback){
9652                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9653             }
9654             
9655         }
9656         
9657         if(this.indicator){
9658             this.indicator.removeClass('visible');
9659             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9660         }
9661         
9662         this.fireEvent('valid', this);
9663     },
9664     
9665      /**
9666      * Mark this field as valid
9667      */
9668     markValid : function()
9669     {
9670         if(!this.el  || this.preventMark){ // not rendered...
9671             return;
9672         }
9673         
9674         this.el.removeClass([this.invalidClass, this.validClass]);
9675         
9676         var feedback = this.el.select('.form-control-feedback', true).first();
9677             
9678         if(feedback){
9679             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9680         }
9681         
9682         if(this.indicator){
9683             this.indicator.removeClass('visible');
9684             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9685         }
9686         
9687         if(this.disabled){
9688             return;
9689         }
9690         
9691         if(this.allowBlank && !this.getRawValue().length){
9692             return;
9693         }
9694         
9695         this.el.addClass(this.validClass);
9696         
9697         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9698             
9699             var feedback = this.el.select('.form-control-feedback', true).first();
9700             
9701             if(feedback){
9702                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9703                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9704             }
9705             
9706         }
9707         
9708         this.fireEvent('valid', this);
9709     },
9710     
9711      /**
9712      * Mark this field as invalid
9713      * @param {String} msg The validation message
9714      */
9715     markInvalid : function(msg)
9716     {
9717         if(!this.el  || this.preventMark){ // not rendered
9718             return;
9719         }
9720         
9721         this.el.removeClass([this.invalidClass, this.validClass]);
9722         
9723         var feedback = this.el.select('.form-control-feedback', true).first();
9724             
9725         if(feedback){
9726             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9727         }
9728
9729         if(this.disabled){
9730             return;
9731         }
9732         
9733         if(this.allowBlank && !this.getRawValue().length){
9734             return;
9735         }
9736         
9737         if(this.indicator){
9738             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9739             this.indicator.addClass('visible');
9740         }
9741         
9742         this.el.addClass(this.invalidClass);
9743         
9744         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9745             
9746             var feedback = this.el.select('.form-control-feedback', true).first();
9747             
9748             if(feedback){
9749                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9750                 
9751                 if(this.getValue().length || this.forceFeedback){
9752                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9753                 }
9754                 
9755             }
9756             
9757         }
9758         
9759         this.fireEvent('invalid', this, msg);
9760     },
9761     // private
9762     SafariOnKeyDown : function(event)
9763     {
9764         // this is a workaround for a password hang bug on chrome/ webkit.
9765         if (this.inputEl().dom.type != 'password') {
9766             return;
9767         }
9768         
9769         var isSelectAll = false;
9770         
9771         if(this.inputEl().dom.selectionEnd > 0){
9772             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9773         }
9774         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9775             event.preventDefault();
9776             this.setValue('');
9777             return;
9778         }
9779         
9780         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9781             
9782             event.preventDefault();
9783             // this is very hacky as keydown always get's upper case.
9784             //
9785             var cc = String.fromCharCode(event.getCharCode());
9786             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9787             
9788         }
9789     },
9790     adjustWidth : function(tag, w){
9791         tag = tag.toLowerCase();
9792         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9793             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9794                 if(tag == 'input'){
9795                     return w + 2;
9796                 }
9797                 if(tag == 'textarea'){
9798                     return w-2;
9799                 }
9800             }else if(Roo.isOpera){
9801                 if(tag == 'input'){
9802                     return w + 2;
9803                 }
9804                 if(tag == 'textarea'){
9805                     return w-2;
9806                 }
9807             }
9808         }
9809         return w;
9810     },
9811     
9812     setFieldLabel : function(v)
9813     {
9814         if(!this.rendered){
9815             return;
9816         }
9817         
9818         if(this.indicatorEl()){
9819             var ar = this.el.select('label > span',true);
9820             
9821             if (ar.elements.length) {
9822                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9823                 this.fieldLabel = v;
9824                 return;
9825             }
9826             
9827             var br = this.el.select('label',true);
9828             
9829             if(br.elements.length) {
9830                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9831                 this.fieldLabel = v;
9832                 return;
9833             }
9834             
9835             Roo.log('Cannot Found any of label > span || label in input');
9836             return;
9837         }
9838         
9839         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9840         this.fieldLabel = v;
9841         
9842         
9843     }
9844 });
9845
9846  
9847 /*
9848  * - LGPL
9849  *
9850  * Input
9851  * 
9852  */
9853
9854 /**
9855  * @class Roo.bootstrap.TextArea
9856  * @extends Roo.bootstrap.Input
9857  * Bootstrap TextArea class
9858  * @cfg {Number} cols Specifies the visible width of a text area
9859  * @cfg {Number} rows Specifies the visible number of lines in a text area
9860  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9861  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9862  * @cfg {string} html text
9863  * 
9864  * @constructor
9865  * Create a new TextArea
9866  * @param {Object} config The config object
9867  */
9868
9869 Roo.bootstrap.TextArea = function(config){
9870     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9871    
9872 };
9873
9874 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9875      
9876     cols : false,
9877     rows : 5,
9878     readOnly : false,
9879     warp : 'soft',
9880     resize : false,
9881     value: false,
9882     html: false,
9883     
9884     getAutoCreate : function(){
9885         
9886         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9887         
9888         var id = Roo.id();
9889         
9890         var cfg = {};
9891         
9892         if(this.inputType != 'hidden'){
9893             cfg.cls = 'form-group' //input-group
9894         }
9895         
9896         var input =  {
9897             tag: 'textarea',
9898             id : id,
9899             warp : this.warp,
9900             rows : this.rows,
9901             value : this.value || '',
9902             html: this.html || '',
9903             cls : 'form-control',
9904             placeholder : this.placeholder || '' 
9905             
9906         };
9907         
9908         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9909             input.maxLength = this.maxLength;
9910         }
9911         
9912         if(this.resize){
9913             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9914         }
9915         
9916         if(this.cols){
9917             input.cols = this.cols;
9918         }
9919         
9920         if (this.readOnly) {
9921             input.readonly = true;
9922         }
9923         
9924         if (this.name) {
9925             input.name = this.name;
9926         }
9927         
9928         if (this.size) {
9929             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9930         }
9931         
9932         var settings=this;
9933         ['xs','sm','md','lg'].map(function(size){
9934             if (settings[size]) {
9935                 cfg.cls += ' col-' + size + '-' + settings[size];
9936             }
9937         });
9938         
9939         var inputblock = input;
9940         
9941         if(this.hasFeedback && !this.allowBlank){
9942             
9943             var feedback = {
9944                 tag: 'span',
9945                 cls: 'glyphicon form-control-feedback'
9946             };
9947
9948             inputblock = {
9949                 cls : 'has-feedback',
9950                 cn :  [
9951                     input,
9952                     feedback
9953                 ] 
9954             };  
9955         }
9956         
9957         
9958         if (this.before || this.after) {
9959             
9960             inputblock = {
9961                 cls : 'input-group',
9962                 cn :  [] 
9963             };
9964             if (this.before) {
9965                 inputblock.cn.push({
9966                     tag :'span',
9967                     cls : 'input-group-addon',
9968                     html : this.before
9969                 });
9970             }
9971             
9972             inputblock.cn.push(input);
9973             
9974             if(this.hasFeedback && !this.allowBlank){
9975                 inputblock.cls += ' has-feedback';
9976                 inputblock.cn.push(feedback);
9977             }
9978             
9979             if (this.after) {
9980                 inputblock.cn.push({
9981                     tag :'span',
9982                     cls : 'input-group-addon',
9983                     html : this.after
9984                 });
9985             }
9986             
9987         }
9988         
9989         if (align ==='left' && this.fieldLabel.length) {
9990             cfg.cn = [
9991                 {
9992                     tag: 'label',
9993                     'for' :  id,
9994                     cls : 'control-label',
9995                     html : this.fieldLabel
9996                 },
9997                 {
9998                     cls : "",
9999                     cn: [
10000                         inputblock
10001                     ]
10002                 }
10003
10004             ];
10005             
10006             if(this.labelWidth > 12){
10007                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10008             }
10009
10010             if(this.labelWidth < 13 && this.labelmd == 0){
10011                 this.labelmd = this.labelWidth;
10012             }
10013
10014             if(this.labellg > 0){
10015                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10016                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10017             }
10018
10019             if(this.labelmd > 0){
10020                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10021                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10022             }
10023
10024             if(this.labelsm > 0){
10025                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10026                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10027             }
10028
10029             if(this.labelxs > 0){
10030                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10031                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10032             }
10033             
10034         } else if ( this.fieldLabel.length) {
10035             cfg.cn = [
10036
10037                {
10038                    tag: 'label',
10039                    //cls : 'input-group-addon',
10040                    html : this.fieldLabel
10041
10042                },
10043
10044                inputblock
10045
10046            ];
10047
10048         } else {
10049
10050             cfg.cn = [
10051
10052                 inputblock
10053
10054             ];
10055                 
10056         }
10057         
10058         if (this.disabled) {
10059             input.disabled=true;
10060         }
10061         
10062         return cfg;
10063         
10064     },
10065     /**
10066      * return the real textarea element.
10067      */
10068     inputEl: function ()
10069     {
10070         return this.el.select('textarea.form-control',true).first();
10071     },
10072     
10073     /**
10074      * Clear any invalid styles/messages for this field
10075      */
10076     clearInvalid : function()
10077     {
10078         
10079         if(!this.el || this.preventMark){ // not rendered
10080             return;
10081         }
10082         
10083         var label = this.el.select('label', true).first();
10084         var icon = this.el.select('i.fa-star', true).first();
10085         
10086         if(label && icon){
10087             icon.remove();
10088         }
10089         
10090         this.el.removeClass(this.invalidClass);
10091         
10092         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10093             
10094             var feedback = this.el.select('.form-control-feedback', true).first();
10095             
10096             if(feedback){
10097                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10098             }
10099             
10100         }
10101         
10102         this.fireEvent('valid', this);
10103     },
10104     
10105      /**
10106      * Mark this field as valid
10107      */
10108     markValid : function()
10109     {
10110         if(!this.el  || this.preventMark){ // not rendered
10111             return;
10112         }
10113         
10114         this.el.removeClass([this.invalidClass, this.validClass]);
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, this.validFeedbackClass]);
10120         }
10121
10122         if(this.disabled || this.allowBlank){
10123             return;
10124         }
10125         
10126         var label = this.el.select('label', true).first();
10127         var icon = this.el.select('i.fa-star', true).first();
10128         
10129         if(label && icon){
10130             icon.remove();
10131         }
10132         
10133         this.el.addClass(this.validClass);
10134         
10135         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10136             
10137             var feedback = this.el.select('.form-control-feedback', true).first();
10138             
10139             if(feedback){
10140                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10141                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10142             }
10143             
10144         }
10145         
10146         this.fireEvent('valid', this);
10147     },
10148     
10149      /**
10150      * Mark this field as invalid
10151      * @param {String} msg The validation message
10152      */
10153     markInvalid : function(msg)
10154     {
10155         if(!this.el  || this.preventMark){ // not rendered
10156             return;
10157         }
10158         
10159         this.el.removeClass([this.invalidClass, this.validClass]);
10160         
10161         var feedback = this.el.select('.form-control-feedback', true).first();
10162             
10163         if(feedback){
10164             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10165         }
10166
10167         if(this.disabled || this.allowBlank){
10168             return;
10169         }
10170         
10171         var label = this.el.select('label', true).first();
10172         var icon = this.el.select('i.fa-star', true).first();
10173         
10174         if(!this.getValue().length && label && !icon){
10175             this.el.createChild({
10176                 tag : 'i',
10177                 cls : 'text-danger fa fa-lg fa-star',
10178                 tooltip : 'This field is required',
10179                 style : 'margin-right:5px;'
10180             }, label, true);
10181         }
10182
10183         this.el.addClass(this.invalidClass);
10184         
10185         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10186             
10187             var feedback = this.el.select('.form-control-feedback', true).first();
10188             
10189             if(feedback){
10190                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10191                 
10192                 if(this.getValue().length || this.forceFeedback){
10193                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10194                 }
10195                 
10196             }
10197             
10198         }
10199         
10200         this.fireEvent('invalid', this, msg);
10201     }
10202 });
10203
10204  
10205 /*
10206  * - LGPL
10207  *
10208  * trigger field - base class for combo..
10209  * 
10210  */
10211  
10212 /**
10213  * @class Roo.bootstrap.TriggerField
10214  * @extends Roo.bootstrap.Input
10215  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10216  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10217  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10218  * for which you can provide a custom implementation.  For example:
10219  * <pre><code>
10220 var trigger = new Roo.bootstrap.TriggerField();
10221 trigger.onTriggerClick = myTriggerFn;
10222 trigger.applyTo('my-field');
10223 </code></pre>
10224  *
10225  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10226  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10227  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10228  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10229  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10230
10231  * @constructor
10232  * Create a new TriggerField.
10233  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10234  * to the base TextField)
10235  */
10236 Roo.bootstrap.TriggerField = function(config){
10237     this.mimicing = false;
10238     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10239 };
10240
10241 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10242     /**
10243      * @cfg {String} triggerClass A CSS class to apply to the trigger
10244      */
10245      /**
10246      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10247      */
10248     hideTrigger:false,
10249
10250     /**
10251      * @cfg {Boolean} removable (true|false) special filter default false
10252      */
10253     removable : false,
10254     
10255     /** @cfg {Boolean} grow @hide */
10256     /** @cfg {Number} growMin @hide */
10257     /** @cfg {Number} growMax @hide */
10258
10259     /**
10260      * @hide 
10261      * @method
10262      */
10263     autoSize: Roo.emptyFn,
10264     // private
10265     monitorTab : true,
10266     // private
10267     deferHeight : true,
10268
10269     
10270     actionMode : 'wrap',
10271     
10272     caret : false,
10273     
10274     
10275     getAutoCreate : function(){
10276        
10277         var align = this.labelAlign || this.parentLabelAlign();
10278         
10279         var id = Roo.id();
10280         
10281         var cfg = {
10282             cls: 'form-group' //input-group
10283         };
10284         
10285         
10286         var input =  {
10287             tag: 'input',
10288             id : id,
10289             type : this.inputType,
10290             cls : 'form-control',
10291             autocomplete: 'new-password',
10292             placeholder : this.placeholder || '' 
10293             
10294         };
10295         if (this.name) {
10296             input.name = this.name;
10297         }
10298         if (this.size) {
10299             input.cls += ' input-' + this.size;
10300         }
10301         
10302         if (this.disabled) {
10303             input.disabled=true;
10304         }
10305         
10306         var inputblock = input;
10307         
10308         if(this.hasFeedback && !this.allowBlank){
10309             
10310             var feedback = {
10311                 tag: 'span',
10312                 cls: 'glyphicon form-control-feedback'
10313             };
10314             
10315             if(this.removable && !this.editable && !this.tickable){
10316                 inputblock = {
10317                     cls : 'has-feedback',
10318                     cn :  [
10319                         inputblock,
10320                         {
10321                             tag: 'button',
10322                             html : 'x',
10323                             cls : 'roo-combo-removable-btn close'
10324                         },
10325                         feedback
10326                     ] 
10327                 };
10328             } else {
10329                 inputblock = {
10330                     cls : 'has-feedback',
10331                     cn :  [
10332                         inputblock,
10333                         feedback
10334                     ] 
10335                 };
10336             }
10337
10338         } else {
10339             if(this.removable && !this.editable && !this.tickable){
10340                 inputblock = {
10341                     cls : 'roo-removable',
10342                     cn :  [
10343                         inputblock,
10344                         {
10345                             tag: 'button',
10346                             html : 'x',
10347                             cls : 'roo-combo-removable-btn close'
10348                         }
10349                     ] 
10350                 };
10351             }
10352         }
10353         
10354         if (this.before || this.after) {
10355             
10356             inputblock = {
10357                 cls : 'input-group',
10358                 cn :  [] 
10359             };
10360             if (this.before) {
10361                 inputblock.cn.push({
10362                     tag :'span',
10363                     cls : 'input-group-addon input-group-prepend input-group-text',
10364                     html : this.before
10365                 });
10366             }
10367             
10368             inputblock.cn.push(input);
10369             
10370             if(this.hasFeedback && !this.allowBlank){
10371                 inputblock.cls += ' has-feedback';
10372                 inputblock.cn.push(feedback);
10373             }
10374             
10375             if (this.after) {
10376                 inputblock.cn.push({
10377                     tag :'span',
10378                     cls : 'input-group-addon input-group-append input-group-text',
10379                     html : this.after
10380                 });
10381             }
10382             
10383         };
10384         
10385       
10386         
10387         var ibwrap = inputblock;
10388         
10389         if(this.multiple){
10390             ibwrap = {
10391                 tag: 'ul',
10392                 cls: 'roo-select2-choices',
10393                 cn:[
10394                     {
10395                         tag: 'li',
10396                         cls: 'roo-select2-search-field',
10397                         cn: [
10398
10399                             inputblock
10400                         ]
10401                     }
10402                 ]
10403             };
10404                 
10405         }
10406         
10407         var combobox = {
10408             cls: 'roo-select2-container input-group',
10409             cn: [
10410                  {
10411                     tag: 'input',
10412                     type : 'hidden',
10413                     cls: 'form-hidden-field'
10414                 },
10415                 ibwrap
10416             ]
10417         };
10418         
10419         if(!this.multiple && this.showToggleBtn){
10420             
10421             var caret = {
10422                         tag: 'span',
10423                         cls: 'caret'
10424              };
10425             if (this.caret != false) {
10426                 caret = {
10427                      tag: 'i',
10428                      cls: 'fa fa-' + this.caret
10429                 };
10430                 
10431             }
10432             
10433             combobox.cn.push({
10434                 tag :'span',
10435                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10436                 cn : [
10437                     caret,
10438                     {
10439                         tag: 'span',
10440                         cls: 'combobox-clear',
10441                         cn  : [
10442                             {
10443                                 tag : 'i',
10444                                 cls: 'icon-remove'
10445                             }
10446                         ]
10447                     }
10448                 ]
10449
10450             })
10451         }
10452         
10453         if(this.multiple){
10454             combobox.cls += ' roo-select2-container-multi';
10455         }
10456          var indicator = {
10457             tag : 'i',
10458             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10459             tooltip : 'This field is required'
10460         };
10461         if (Roo.bootstrap.version == 4) {
10462             indicator = {
10463                 tag : 'i',
10464                 style : 'display:none'
10465             };
10466         }
10467         
10468         
10469         if (align ==='left' && this.fieldLabel.length) {
10470             
10471             cfg.cls += ' roo-form-group-label-left row';
10472
10473             cfg.cn = [
10474                 indicator,
10475                 {
10476                     tag: 'label',
10477                     'for' :  id,
10478                     cls : 'control-label',
10479                     html : this.fieldLabel
10480
10481                 },
10482                 {
10483                     cls : "", 
10484                     cn: [
10485                         combobox
10486                     ]
10487                 }
10488
10489             ];
10490             
10491             var labelCfg = cfg.cn[1];
10492             var contentCfg = cfg.cn[2];
10493             
10494             if(this.indicatorpos == 'right'){
10495                 cfg.cn = [
10496                     {
10497                         tag: 'label',
10498                         'for' :  id,
10499                         cls : 'control-label',
10500                         cn : [
10501                             {
10502                                 tag : 'span',
10503                                 html : this.fieldLabel
10504                             },
10505                             indicator
10506                         ]
10507                     },
10508                     {
10509                         cls : "", 
10510                         cn: [
10511                             combobox
10512                         ]
10513                     }
10514
10515                 ];
10516                 
10517                 labelCfg = cfg.cn[0];
10518                 contentCfg = cfg.cn[1];
10519             }
10520             
10521             if(this.labelWidth > 12){
10522                 labelCfg.style = "width: " + this.labelWidth + 'px';
10523             }
10524             
10525             if(this.labelWidth < 13 && this.labelmd == 0){
10526                 this.labelmd = this.labelWidth;
10527             }
10528             
10529             if(this.labellg > 0){
10530                 labelCfg.cls += ' col-lg-' + this.labellg;
10531                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10532             }
10533             
10534             if(this.labelmd > 0){
10535                 labelCfg.cls += ' col-md-' + this.labelmd;
10536                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10537             }
10538             
10539             if(this.labelsm > 0){
10540                 labelCfg.cls += ' col-sm-' + this.labelsm;
10541                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10542             }
10543             
10544             if(this.labelxs > 0){
10545                 labelCfg.cls += ' col-xs-' + this.labelxs;
10546                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10547             }
10548             
10549         } else if ( this.fieldLabel.length) {
10550 //                Roo.log(" label");
10551             cfg.cn = [
10552                 indicator,
10553                {
10554                    tag: 'label',
10555                    //cls : 'input-group-addon',
10556                    html : this.fieldLabel
10557
10558                },
10559
10560                combobox
10561
10562             ];
10563             
10564             if(this.indicatorpos == 'right'){
10565                 
10566                 cfg.cn = [
10567                     {
10568                        tag: 'label',
10569                        cn : [
10570                            {
10571                                tag : 'span',
10572                                html : this.fieldLabel
10573                            },
10574                            indicator
10575                        ]
10576
10577                     },
10578                     combobox
10579
10580                 ];
10581
10582             }
10583
10584         } else {
10585             
10586 //                Roo.log(" no label && no align");
10587                 cfg = combobox
10588                      
10589                 
10590         }
10591         
10592         var settings=this;
10593         ['xs','sm','md','lg'].map(function(size){
10594             if (settings[size]) {
10595                 cfg.cls += ' col-' + size + '-' + settings[size];
10596             }
10597         });
10598         
10599         return cfg;
10600         
10601     },
10602     
10603     
10604     
10605     // private
10606     onResize : function(w, h){
10607 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10608 //        if(typeof w == 'number'){
10609 //            var x = w - this.trigger.getWidth();
10610 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10611 //            this.trigger.setStyle('left', x+'px');
10612 //        }
10613     },
10614
10615     // private
10616     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10617
10618     // private
10619     getResizeEl : function(){
10620         return this.inputEl();
10621     },
10622
10623     // private
10624     getPositionEl : function(){
10625         return this.inputEl();
10626     },
10627
10628     // private
10629     alignErrorIcon : function(){
10630         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10631     },
10632
10633     // private
10634     initEvents : function(){
10635         
10636         this.createList();
10637         
10638         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10639         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10640         if(!this.multiple && this.showToggleBtn){
10641             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10642             if(this.hideTrigger){
10643                 this.trigger.setDisplayed(false);
10644             }
10645             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10646         }
10647         
10648         if(this.multiple){
10649             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10650         }
10651         
10652         if(this.removable && !this.editable && !this.tickable){
10653             var close = this.closeTriggerEl();
10654             
10655             if(close){
10656                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10657                 close.on('click', this.removeBtnClick, this, close);
10658             }
10659         }
10660         
10661         //this.trigger.addClassOnOver('x-form-trigger-over');
10662         //this.trigger.addClassOnClick('x-form-trigger-click');
10663         
10664         //if(!this.width){
10665         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10666         //}
10667     },
10668     
10669     closeTriggerEl : function()
10670     {
10671         var close = this.el.select('.roo-combo-removable-btn', true).first();
10672         return close ? close : false;
10673     },
10674     
10675     removeBtnClick : function(e, h, el)
10676     {
10677         e.preventDefault();
10678         
10679         if(this.fireEvent("remove", this) !== false){
10680             this.reset();
10681             this.fireEvent("afterremove", this)
10682         }
10683     },
10684     
10685     createList : function()
10686     {
10687         this.list = Roo.get(document.body).createChild({
10688             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10689             cls: 'typeahead typeahead-long dropdown-menu',
10690             style: 'display:none'
10691         });
10692         
10693         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10694         
10695     },
10696
10697     // private
10698     initTrigger : function(){
10699        
10700     },
10701
10702     // private
10703     onDestroy : function(){
10704         if(this.trigger){
10705             this.trigger.removeAllListeners();
10706           //  this.trigger.remove();
10707         }
10708         //if(this.wrap){
10709         //    this.wrap.remove();
10710         //}
10711         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10712     },
10713
10714     // private
10715     onFocus : function(){
10716         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10717         /*
10718         if(!this.mimicing){
10719             this.wrap.addClass('x-trigger-wrap-focus');
10720             this.mimicing = true;
10721             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10722             if(this.monitorTab){
10723                 this.el.on("keydown", this.checkTab, this);
10724             }
10725         }
10726         */
10727     },
10728
10729     // private
10730     checkTab : function(e){
10731         if(e.getKey() == e.TAB){
10732             this.triggerBlur();
10733         }
10734     },
10735
10736     // private
10737     onBlur : function(){
10738         // do nothing
10739     },
10740
10741     // private
10742     mimicBlur : function(e, t){
10743         /*
10744         if(!this.wrap.contains(t) && this.validateBlur()){
10745             this.triggerBlur();
10746         }
10747         */
10748     },
10749
10750     // private
10751     triggerBlur : function(){
10752         this.mimicing = false;
10753         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10754         if(this.monitorTab){
10755             this.el.un("keydown", this.checkTab, this);
10756         }
10757         //this.wrap.removeClass('x-trigger-wrap-focus');
10758         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10759     },
10760
10761     // private
10762     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10763     validateBlur : function(e, t){
10764         return true;
10765     },
10766
10767     // private
10768     onDisable : function(){
10769         this.inputEl().dom.disabled = true;
10770         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10771         //if(this.wrap){
10772         //    this.wrap.addClass('x-item-disabled');
10773         //}
10774     },
10775
10776     // private
10777     onEnable : function(){
10778         this.inputEl().dom.disabled = false;
10779         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10780         //if(this.wrap){
10781         //    this.el.removeClass('x-item-disabled');
10782         //}
10783     },
10784
10785     // private
10786     onShow : function(){
10787         var ae = this.getActionEl();
10788         
10789         if(ae){
10790             ae.dom.style.display = '';
10791             ae.dom.style.visibility = 'visible';
10792         }
10793     },
10794
10795     // private
10796     
10797     onHide : function(){
10798         var ae = this.getActionEl();
10799         ae.dom.style.display = 'none';
10800     },
10801
10802     /**
10803      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10804      * by an implementing function.
10805      * @method
10806      * @param {EventObject} e
10807      */
10808     onTriggerClick : Roo.emptyFn
10809 });
10810  /*
10811  * Based on:
10812  * Ext JS Library 1.1.1
10813  * Copyright(c) 2006-2007, Ext JS, LLC.
10814  *
10815  * Originally Released Under LGPL - original licence link has changed is not relivant.
10816  *
10817  * Fork - LGPL
10818  * <script type="text/javascript">
10819  */
10820
10821
10822 /**
10823  * @class Roo.data.SortTypes
10824  * @singleton
10825  * Defines the default sorting (casting?) comparison functions used when sorting data.
10826  */
10827 Roo.data.SortTypes = {
10828     /**
10829      * Default sort that does nothing
10830      * @param {Mixed} s The value being converted
10831      * @return {Mixed} The comparison value
10832      */
10833     none : function(s){
10834         return s;
10835     },
10836     
10837     /**
10838      * The regular expression used to strip tags
10839      * @type {RegExp}
10840      * @property
10841      */
10842     stripTagsRE : /<\/?[^>]+>/gi,
10843     
10844     /**
10845      * Strips all HTML tags to sort on text only
10846      * @param {Mixed} s The value being converted
10847      * @return {String} The comparison value
10848      */
10849     asText : function(s){
10850         return String(s).replace(this.stripTagsRE, "");
10851     },
10852     
10853     /**
10854      * Strips all HTML tags to sort on text only - Case insensitive
10855      * @param {Mixed} s The value being converted
10856      * @return {String} The comparison value
10857      */
10858     asUCText : function(s){
10859         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10860     },
10861     
10862     /**
10863      * Case insensitive string
10864      * @param {Mixed} s The value being converted
10865      * @return {String} The comparison value
10866      */
10867     asUCString : function(s) {
10868         return String(s).toUpperCase();
10869     },
10870     
10871     /**
10872      * Date sorting
10873      * @param {Mixed} s The value being converted
10874      * @return {Number} The comparison value
10875      */
10876     asDate : function(s) {
10877         if(!s){
10878             return 0;
10879         }
10880         if(s instanceof Date){
10881             return s.getTime();
10882         }
10883         return Date.parse(String(s));
10884     },
10885     
10886     /**
10887      * Float sorting
10888      * @param {Mixed} s The value being converted
10889      * @return {Float} The comparison value
10890      */
10891     asFloat : function(s) {
10892         var val = parseFloat(String(s).replace(/,/g, ""));
10893         if(isNaN(val)) {
10894             val = 0;
10895         }
10896         return val;
10897     },
10898     
10899     /**
10900      * Integer sorting
10901      * @param {Mixed} s The value being converted
10902      * @return {Number} The comparison value
10903      */
10904     asInt : function(s) {
10905         var val = parseInt(String(s).replace(/,/g, ""));
10906         if(isNaN(val)) {
10907             val = 0;
10908         }
10909         return val;
10910     }
10911 };/*
10912  * Based on:
10913  * Ext JS Library 1.1.1
10914  * Copyright(c) 2006-2007, Ext JS, LLC.
10915  *
10916  * Originally Released Under LGPL - original licence link has changed is not relivant.
10917  *
10918  * Fork - LGPL
10919  * <script type="text/javascript">
10920  */
10921
10922 /**
10923 * @class Roo.data.Record
10924  * Instances of this class encapsulate both record <em>definition</em> information, and record
10925  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10926  * to access Records cached in an {@link Roo.data.Store} object.<br>
10927  * <p>
10928  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10929  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10930  * objects.<br>
10931  * <p>
10932  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10933  * @constructor
10934  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10935  * {@link #create}. The parameters are the same.
10936  * @param {Array} data An associative Array of data values keyed by the field name.
10937  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10938  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10939  * not specified an integer id is generated.
10940  */
10941 Roo.data.Record = function(data, id){
10942     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10943     this.data = data;
10944 };
10945
10946 /**
10947  * Generate a constructor for a specific record layout.
10948  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10949  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10950  * Each field definition object may contain the following properties: <ul>
10951  * <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,
10952  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10953  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10954  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10955  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10956  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10957  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10958  * this may be omitted.</p></li>
10959  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10960  * <ul><li>auto (Default, implies no conversion)</li>
10961  * <li>string</li>
10962  * <li>int</li>
10963  * <li>float</li>
10964  * <li>boolean</li>
10965  * <li>date</li></ul></p></li>
10966  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10967  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10968  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10969  * by the Reader into an object that will be stored in the Record. It is passed the
10970  * following parameters:<ul>
10971  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10972  * </ul></p></li>
10973  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10974  * </ul>
10975  * <br>usage:<br><pre><code>
10976 var TopicRecord = Roo.data.Record.create(
10977     {name: 'title', mapping: 'topic_title'},
10978     {name: 'author', mapping: 'username'},
10979     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10980     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10981     {name: 'lastPoster', mapping: 'user2'},
10982     {name: 'excerpt', mapping: 'post_text'}
10983 );
10984
10985 var myNewRecord = new TopicRecord({
10986     title: 'Do my job please',
10987     author: 'noobie',
10988     totalPosts: 1,
10989     lastPost: new Date(),
10990     lastPoster: 'Animal',
10991     excerpt: 'No way dude!'
10992 });
10993 myStore.add(myNewRecord);
10994 </code></pre>
10995  * @method create
10996  * @static
10997  */
10998 Roo.data.Record.create = function(o){
10999     var f = function(){
11000         f.superclass.constructor.apply(this, arguments);
11001     };
11002     Roo.extend(f, Roo.data.Record);
11003     var p = f.prototype;
11004     p.fields = new Roo.util.MixedCollection(false, function(field){
11005         return field.name;
11006     });
11007     for(var i = 0, len = o.length; i < len; i++){
11008         p.fields.add(new Roo.data.Field(o[i]));
11009     }
11010     f.getField = function(name){
11011         return p.fields.get(name);  
11012     };
11013     return f;
11014 };
11015
11016 Roo.data.Record.AUTO_ID = 1000;
11017 Roo.data.Record.EDIT = 'edit';
11018 Roo.data.Record.REJECT = 'reject';
11019 Roo.data.Record.COMMIT = 'commit';
11020
11021 Roo.data.Record.prototype = {
11022     /**
11023      * Readonly flag - true if this record has been modified.
11024      * @type Boolean
11025      */
11026     dirty : false,
11027     editing : false,
11028     error: null,
11029     modified: null,
11030
11031     // private
11032     join : function(store){
11033         this.store = store;
11034     },
11035
11036     /**
11037      * Set the named field to the specified value.
11038      * @param {String} name The name of the field to set.
11039      * @param {Object} value The value to set the field to.
11040      */
11041     set : function(name, value){
11042         if(this.data[name] == value){
11043             return;
11044         }
11045         this.dirty = true;
11046         if(!this.modified){
11047             this.modified = {};
11048         }
11049         if(typeof this.modified[name] == 'undefined'){
11050             this.modified[name] = this.data[name];
11051         }
11052         this.data[name] = value;
11053         if(!this.editing && this.store){
11054             this.store.afterEdit(this);
11055         }       
11056     },
11057
11058     /**
11059      * Get the value of the named field.
11060      * @param {String} name The name of the field to get the value of.
11061      * @return {Object} The value of the field.
11062      */
11063     get : function(name){
11064         return this.data[name]; 
11065     },
11066
11067     // private
11068     beginEdit : function(){
11069         this.editing = true;
11070         this.modified = {}; 
11071     },
11072
11073     // private
11074     cancelEdit : function(){
11075         this.editing = false;
11076         delete this.modified;
11077     },
11078
11079     // private
11080     endEdit : function(){
11081         this.editing = false;
11082         if(this.dirty && this.store){
11083             this.store.afterEdit(this);
11084         }
11085     },
11086
11087     /**
11088      * Usually called by the {@link Roo.data.Store} which owns the Record.
11089      * Rejects all changes made to the Record since either creation, or the last commit operation.
11090      * Modified fields are reverted to their original values.
11091      * <p>
11092      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11093      * of reject operations.
11094      */
11095     reject : function(){
11096         var m = this.modified;
11097         for(var n in m){
11098             if(typeof m[n] != "function"){
11099                 this.data[n] = m[n];
11100             }
11101         }
11102         this.dirty = false;
11103         delete this.modified;
11104         this.editing = false;
11105         if(this.store){
11106             this.store.afterReject(this);
11107         }
11108     },
11109
11110     /**
11111      * Usually called by the {@link Roo.data.Store} which owns the Record.
11112      * Commits all changes made to the Record since either creation, or the last commit operation.
11113      * <p>
11114      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11115      * of commit operations.
11116      */
11117     commit : function(){
11118         this.dirty = false;
11119         delete this.modified;
11120         this.editing = false;
11121         if(this.store){
11122             this.store.afterCommit(this);
11123         }
11124     },
11125
11126     // private
11127     hasError : function(){
11128         return this.error != null;
11129     },
11130
11131     // private
11132     clearError : function(){
11133         this.error = null;
11134     },
11135
11136     /**
11137      * Creates a copy of this record.
11138      * @param {String} id (optional) A new record id if you don't want to use this record's id
11139      * @return {Record}
11140      */
11141     copy : function(newId) {
11142         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11143     }
11144 };/*
11145  * Based on:
11146  * Ext JS Library 1.1.1
11147  * Copyright(c) 2006-2007, Ext JS, LLC.
11148  *
11149  * Originally Released Under LGPL - original licence link has changed is not relivant.
11150  *
11151  * Fork - LGPL
11152  * <script type="text/javascript">
11153  */
11154
11155
11156
11157 /**
11158  * @class Roo.data.Store
11159  * @extends Roo.util.Observable
11160  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11161  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11162  * <p>
11163  * 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
11164  * has no knowledge of the format of the data returned by the Proxy.<br>
11165  * <p>
11166  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11167  * instances from the data object. These records are cached and made available through accessor functions.
11168  * @constructor
11169  * Creates a new Store.
11170  * @param {Object} config A config object containing the objects needed for the Store to access data,
11171  * and read the data into Records.
11172  */
11173 Roo.data.Store = function(config){
11174     this.data = new Roo.util.MixedCollection(false);
11175     this.data.getKey = function(o){
11176         return o.id;
11177     };
11178     this.baseParams = {};
11179     // private
11180     this.paramNames = {
11181         "start" : "start",
11182         "limit" : "limit",
11183         "sort" : "sort",
11184         "dir" : "dir",
11185         "multisort" : "_multisort"
11186     };
11187
11188     if(config && config.data){
11189         this.inlineData = config.data;
11190         delete config.data;
11191     }
11192
11193     Roo.apply(this, config);
11194     
11195     if(this.reader){ // reader passed
11196         this.reader = Roo.factory(this.reader, Roo.data);
11197         this.reader.xmodule = this.xmodule || false;
11198         if(!this.recordType){
11199             this.recordType = this.reader.recordType;
11200         }
11201         if(this.reader.onMetaChange){
11202             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11203         }
11204     }
11205
11206     if(this.recordType){
11207         this.fields = this.recordType.prototype.fields;
11208     }
11209     this.modified = [];
11210
11211     this.addEvents({
11212         /**
11213          * @event datachanged
11214          * Fires when the data cache has changed, and a widget which is using this Store
11215          * as a Record cache should refresh its view.
11216          * @param {Store} this
11217          */
11218         datachanged : true,
11219         /**
11220          * @event metachange
11221          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11222          * @param {Store} this
11223          * @param {Object} meta The JSON metadata
11224          */
11225         metachange : true,
11226         /**
11227          * @event add
11228          * Fires when Records have been added to the Store
11229          * @param {Store} this
11230          * @param {Roo.data.Record[]} records The array of Records added
11231          * @param {Number} index The index at which the record(s) were added
11232          */
11233         add : true,
11234         /**
11235          * @event remove
11236          * Fires when a Record has been removed from the Store
11237          * @param {Store} this
11238          * @param {Roo.data.Record} record The Record that was removed
11239          * @param {Number} index The index at which the record was removed
11240          */
11241         remove : true,
11242         /**
11243          * @event update
11244          * Fires when a Record has been updated
11245          * @param {Store} this
11246          * @param {Roo.data.Record} record The Record that was updated
11247          * @param {String} operation The update operation being performed.  Value may be one of:
11248          * <pre><code>
11249  Roo.data.Record.EDIT
11250  Roo.data.Record.REJECT
11251  Roo.data.Record.COMMIT
11252          * </code></pre>
11253          */
11254         update : true,
11255         /**
11256          * @event clear
11257          * Fires when the data cache has been cleared.
11258          * @param {Store} this
11259          */
11260         clear : true,
11261         /**
11262          * @event beforeload
11263          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11264          * the load action will be canceled.
11265          * @param {Store} this
11266          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11267          */
11268         beforeload : true,
11269         /**
11270          * @event beforeloadadd
11271          * Fires after a new set of Records has been loaded.
11272          * @param {Store} this
11273          * @param {Roo.data.Record[]} records The Records that were loaded
11274          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11275          */
11276         beforeloadadd : true,
11277         /**
11278          * @event load
11279          * Fires after a new set of Records has been loaded, before they are added to the store.
11280          * @param {Store} this
11281          * @param {Roo.data.Record[]} records The Records that were loaded
11282          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11283          * @params {Object} return from reader
11284          */
11285         load : true,
11286         /**
11287          * @event loadexception
11288          * Fires if an exception occurs in the Proxy during loading.
11289          * Called with the signature of the Proxy's "loadexception" event.
11290          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11291          * 
11292          * @param {Proxy} 
11293          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11294          * @param {Object} load options 
11295          * @param {Object} jsonData from your request (normally this contains the Exception)
11296          */
11297         loadexception : true
11298     });
11299     
11300     if(this.proxy){
11301         this.proxy = Roo.factory(this.proxy, Roo.data);
11302         this.proxy.xmodule = this.xmodule || false;
11303         this.relayEvents(this.proxy,  ["loadexception"]);
11304     }
11305     this.sortToggle = {};
11306     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11307
11308     Roo.data.Store.superclass.constructor.call(this);
11309
11310     if(this.inlineData){
11311         this.loadData(this.inlineData);
11312         delete this.inlineData;
11313     }
11314 };
11315
11316 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11317      /**
11318     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11319     * without a remote query - used by combo/forms at present.
11320     */
11321     
11322     /**
11323     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11324     */
11325     /**
11326     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11327     */
11328     /**
11329     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11330     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11331     */
11332     /**
11333     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11334     * on any HTTP request
11335     */
11336     /**
11337     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11338     */
11339     /**
11340     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11341     */
11342     multiSort: false,
11343     /**
11344     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11345     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11346     */
11347     remoteSort : false,
11348
11349     /**
11350     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11351      * loaded or when a record is removed. (defaults to false).
11352     */
11353     pruneModifiedRecords : false,
11354
11355     // private
11356     lastOptions : null,
11357
11358     /**
11359      * Add Records to the Store and fires the add event.
11360      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11361      */
11362     add : function(records){
11363         records = [].concat(records);
11364         for(var i = 0, len = records.length; i < len; i++){
11365             records[i].join(this);
11366         }
11367         var index = this.data.length;
11368         this.data.addAll(records);
11369         this.fireEvent("add", this, records, index);
11370     },
11371
11372     /**
11373      * Remove a Record from the Store and fires the remove event.
11374      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11375      */
11376     remove : function(record){
11377         var index = this.data.indexOf(record);
11378         this.data.removeAt(index);
11379  
11380         if(this.pruneModifiedRecords){
11381             this.modified.remove(record);
11382         }
11383         this.fireEvent("remove", this, record, index);
11384     },
11385
11386     /**
11387      * Remove all Records from the Store and fires the clear event.
11388      */
11389     removeAll : function(){
11390         this.data.clear();
11391         if(this.pruneModifiedRecords){
11392             this.modified = [];
11393         }
11394         this.fireEvent("clear", this);
11395     },
11396
11397     /**
11398      * Inserts Records to the Store at the given index and fires the add event.
11399      * @param {Number} index The start index at which to insert the passed Records.
11400      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11401      */
11402     insert : function(index, records){
11403         records = [].concat(records);
11404         for(var i = 0, len = records.length; i < len; i++){
11405             this.data.insert(index, records[i]);
11406             records[i].join(this);
11407         }
11408         this.fireEvent("add", this, records, index);
11409     },
11410
11411     /**
11412      * Get the index within the cache of the passed Record.
11413      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11414      * @return {Number} The index of the passed Record. Returns -1 if not found.
11415      */
11416     indexOf : function(record){
11417         return this.data.indexOf(record);
11418     },
11419
11420     /**
11421      * Get the index within the cache of the Record with the passed id.
11422      * @param {String} id The id of the Record to find.
11423      * @return {Number} The index of the Record. Returns -1 if not found.
11424      */
11425     indexOfId : function(id){
11426         return this.data.indexOfKey(id);
11427     },
11428
11429     /**
11430      * Get the Record with the specified id.
11431      * @param {String} id The id of the Record to find.
11432      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11433      */
11434     getById : function(id){
11435         return this.data.key(id);
11436     },
11437
11438     /**
11439      * Get the Record at the specified index.
11440      * @param {Number} index The index of the Record to find.
11441      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11442      */
11443     getAt : function(index){
11444         return this.data.itemAt(index);
11445     },
11446
11447     /**
11448      * Returns a range of Records between specified indices.
11449      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11450      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11451      * @return {Roo.data.Record[]} An array of Records
11452      */
11453     getRange : function(start, end){
11454         return this.data.getRange(start, end);
11455     },
11456
11457     // private
11458     storeOptions : function(o){
11459         o = Roo.apply({}, o);
11460         delete o.callback;
11461         delete o.scope;
11462         this.lastOptions = o;
11463     },
11464
11465     /**
11466      * Loads the Record cache from the configured Proxy using the configured Reader.
11467      * <p>
11468      * If using remote paging, then the first load call must specify the <em>start</em>
11469      * and <em>limit</em> properties in the options.params property to establish the initial
11470      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11471      * <p>
11472      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11473      * and this call will return before the new data has been loaded. Perform any post-processing
11474      * in a callback function, or in a "load" event handler.</strong>
11475      * <p>
11476      * @param {Object} options An object containing properties which control loading options:<ul>
11477      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11478      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11479      * passed the following arguments:<ul>
11480      * <li>r : Roo.data.Record[]</li>
11481      * <li>options: Options object from the load call</li>
11482      * <li>success: Boolean success indicator</li></ul></li>
11483      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11484      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11485      * </ul>
11486      */
11487     load : function(options){
11488         options = options || {};
11489         if(this.fireEvent("beforeload", this, options) !== false){
11490             this.storeOptions(options);
11491             var p = Roo.apply(options.params || {}, this.baseParams);
11492             // if meta was not loaded from remote source.. try requesting it.
11493             if (!this.reader.metaFromRemote) {
11494                 p._requestMeta = 1;
11495             }
11496             if(this.sortInfo && this.remoteSort){
11497                 var pn = this.paramNames;
11498                 p[pn["sort"]] = this.sortInfo.field;
11499                 p[pn["dir"]] = this.sortInfo.direction;
11500             }
11501             if (this.multiSort) {
11502                 var pn = this.paramNames;
11503                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11504             }
11505             
11506             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11507         }
11508     },
11509
11510     /**
11511      * Reloads the Record cache from the configured Proxy using the configured Reader and
11512      * the options from the last load operation performed.
11513      * @param {Object} options (optional) An object containing properties which may override the options
11514      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11515      * the most recently used options are reused).
11516      */
11517     reload : function(options){
11518         this.load(Roo.applyIf(options||{}, this.lastOptions));
11519     },
11520
11521     // private
11522     // Called as a callback by the Reader during a load operation.
11523     loadRecords : function(o, options, success){
11524         if(!o || success === false){
11525             if(success !== false){
11526                 this.fireEvent("load", this, [], options, o);
11527             }
11528             if(options.callback){
11529                 options.callback.call(options.scope || this, [], options, false);
11530             }
11531             return;
11532         }
11533         // if data returned failure - throw an exception.
11534         if (o.success === false) {
11535             // show a message if no listener is registered.
11536             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11537                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11538             }
11539             // loadmask wil be hooked into this..
11540             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11541             return;
11542         }
11543         var r = o.records, t = o.totalRecords || r.length;
11544         
11545         this.fireEvent("beforeloadadd", this, r, options, o);
11546         
11547         if(!options || options.add !== true){
11548             if(this.pruneModifiedRecords){
11549                 this.modified = [];
11550             }
11551             for(var i = 0, len = r.length; i < len; i++){
11552                 r[i].join(this);
11553             }
11554             if(this.snapshot){
11555                 this.data = this.snapshot;
11556                 delete this.snapshot;
11557             }
11558             this.data.clear();
11559             this.data.addAll(r);
11560             this.totalLength = t;
11561             this.applySort();
11562             this.fireEvent("datachanged", this);
11563         }else{
11564             this.totalLength = Math.max(t, this.data.length+r.length);
11565             this.add(r);
11566         }
11567         
11568         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11569                 
11570             var e = new Roo.data.Record({});
11571
11572             e.set(this.parent.displayField, this.parent.emptyTitle);
11573             e.set(this.parent.valueField, '');
11574
11575             this.insert(0, e);
11576         }
11577             
11578         this.fireEvent("load", this, r, options, o);
11579         if(options.callback){
11580             options.callback.call(options.scope || this, r, options, true);
11581         }
11582     },
11583
11584
11585     /**
11586      * Loads data from a passed data block. A Reader which understands the format of the data
11587      * must have been configured in the constructor.
11588      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11589      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11590      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11591      */
11592     loadData : function(o, append){
11593         var r = this.reader.readRecords(o);
11594         this.loadRecords(r, {add: append}, true);
11595     },
11596
11597     /**
11598      * Gets the number of cached records.
11599      * <p>
11600      * <em>If using paging, this may not be the total size of the dataset. If the data object
11601      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11602      * the data set size</em>
11603      */
11604     getCount : function(){
11605         return this.data.length || 0;
11606     },
11607
11608     /**
11609      * Gets the total number of records in the dataset as returned by the server.
11610      * <p>
11611      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11612      * the dataset size</em>
11613      */
11614     getTotalCount : function(){
11615         return this.totalLength || 0;
11616     },
11617
11618     /**
11619      * Returns the sort state of the Store as an object with two properties:
11620      * <pre><code>
11621  field {String} The name of the field by which the Records are sorted
11622  direction {String} The sort order, "ASC" or "DESC"
11623      * </code></pre>
11624      */
11625     getSortState : function(){
11626         return this.sortInfo;
11627     },
11628
11629     // private
11630     applySort : function(){
11631         if(this.sortInfo && !this.remoteSort){
11632             var s = this.sortInfo, f = s.field;
11633             var st = this.fields.get(f).sortType;
11634             var fn = function(r1, r2){
11635                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11636                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11637             };
11638             this.data.sort(s.direction, fn);
11639             if(this.snapshot && this.snapshot != this.data){
11640                 this.snapshot.sort(s.direction, fn);
11641             }
11642         }
11643     },
11644
11645     /**
11646      * Sets the default sort column and order to be used by the next load operation.
11647      * @param {String} fieldName The name of the field to sort by.
11648      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11649      */
11650     setDefaultSort : function(field, dir){
11651         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11652     },
11653
11654     /**
11655      * Sort the Records.
11656      * If remote sorting is used, the sort is performed on the server, and the cache is
11657      * reloaded. If local sorting is used, the cache is sorted internally.
11658      * @param {String} fieldName The name of the field to sort by.
11659      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11660      */
11661     sort : function(fieldName, dir){
11662         var f = this.fields.get(fieldName);
11663         if(!dir){
11664             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11665             
11666             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11667                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11668             }else{
11669                 dir = f.sortDir;
11670             }
11671         }
11672         this.sortToggle[f.name] = dir;
11673         this.sortInfo = {field: f.name, direction: dir};
11674         if(!this.remoteSort){
11675             this.applySort();
11676             this.fireEvent("datachanged", this);
11677         }else{
11678             this.load(this.lastOptions);
11679         }
11680     },
11681
11682     /**
11683      * Calls the specified function for each of the Records in the cache.
11684      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11685      * Returning <em>false</em> aborts and exits the iteration.
11686      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11687      */
11688     each : function(fn, scope){
11689         this.data.each(fn, scope);
11690     },
11691
11692     /**
11693      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11694      * (e.g., during paging).
11695      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11696      */
11697     getModifiedRecords : function(){
11698         return this.modified;
11699     },
11700
11701     // private
11702     createFilterFn : function(property, value, anyMatch){
11703         if(!value.exec){ // not a regex
11704             value = String(value);
11705             if(value.length == 0){
11706                 return false;
11707             }
11708             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11709         }
11710         return function(r){
11711             return value.test(r.data[property]);
11712         };
11713     },
11714
11715     /**
11716      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11717      * @param {String} property A field on your records
11718      * @param {Number} start The record index to start at (defaults to 0)
11719      * @param {Number} end The last record index to include (defaults to length - 1)
11720      * @return {Number} The sum
11721      */
11722     sum : function(property, start, end){
11723         var rs = this.data.items, v = 0;
11724         start = start || 0;
11725         end = (end || end === 0) ? end : rs.length-1;
11726
11727         for(var i = start; i <= end; i++){
11728             v += (rs[i].data[property] || 0);
11729         }
11730         return v;
11731     },
11732
11733     /**
11734      * Filter the records by a specified property.
11735      * @param {String} field A field on your records
11736      * @param {String/RegExp} value Either a string that the field
11737      * should start with or a RegExp to test against the field
11738      * @param {Boolean} anyMatch True to match any part not just the beginning
11739      */
11740     filter : function(property, value, anyMatch){
11741         var fn = this.createFilterFn(property, value, anyMatch);
11742         return fn ? this.filterBy(fn) : this.clearFilter();
11743     },
11744
11745     /**
11746      * Filter by a function. The specified function will be called with each
11747      * record in this data source. If the function returns true the record is included,
11748      * otherwise it is filtered.
11749      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11750      * @param {Object} scope (optional) The scope of the function (defaults to this)
11751      */
11752     filterBy : function(fn, scope){
11753         this.snapshot = this.snapshot || this.data;
11754         this.data = this.queryBy(fn, scope||this);
11755         this.fireEvent("datachanged", this);
11756     },
11757
11758     /**
11759      * Query the records by a specified property.
11760      * @param {String} field A field on your records
11761      * @param {String/RegExp} value Either a string that the field
11762      * should start with or a RegExp to test against the field
11763      * @param {Boolean} anyMatch True to match any part not just the beginning
11764      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11765      */
11766     query : function(property, value, anyMatch){
11767         var fn = this.createFilterFn(property, value, anyMatch);
11768         return fn ? this.queryBy(fn) : this.data.clone();
11769     },
11770
11771     /**
11772      * Query by a function. The specified function will be called with each
11773      * record in this data source. If the function returns true the record is included
11774      * in the results.
11775      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11776      * @param {Object} scope (optional) The scope of the function (defaults to this)
11777       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11778      **/
11779     queryBy : function(fn, scope){
11780         var data = this.snapshot || this.data;
11781         return data.filterBy(fn, scope||this);
11782     },
11783
11784     /**
11785      * Collects unique values for a particular dataIndex from this store.
11786      * @param {String} dataIndex The property to collect
11787      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11788      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11789      * @return {Array} An array of the unique values
11790      **/
11791     collect : function(dataIndex, allowNull, bypassFilter){
11792         var d = (bypassFilter === true && this.snapshot) ?
11793                 this.snapshot.items : this.data.items;
11794         var v, sv, r = [], l = {};
11795         for(var i = 0, len = d.length; i < len; i++){
11796             v = d[i].data[dataIndex];
11797             sv = String(v);
11798             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11799                 l[sv] = true;
11800                 r[r.length] = v;
11801             }
11802         }
11803         return r;
11804     },
11805
11806     /**
11807      * Revert to a view of the Record cache with no filtering applied.
11808      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11809      */
11810     clearFilter : function(suppressEvent){
11811         if(this.snapshot && this.snapshot != this.data){
11812             this.data = this.snapshot;
11813             delete this.snapshot;
11814             if(suppressEvent !== true){
11815                 this.fireEvent("datachanged", this);
11816             }
11817         }
11818     },
11819
11820     // private
11821     afterEdit : function(record){
11822         if(this.modified.indexOf(record) == -1){
11823             this.modified.push(record);
11824         }
11825         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11826     },
11827     
11828     // private
11829     afterReject : function(record){
11830         this.modified.remove(record);
11831         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11832     },
11833
11834     // private
11835     afterCommit : function(record){
11836         this.modified.remove(record);
11837         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11838     },
11839
11840     /**
11841      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11842      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11843      */
11844     commitChanges : function(){
11845         var m = this.modified.slice(0);
11846         this.modified = [];
11847         for(var i = 0, len = m.length; i < len; i++){
11848             m[i].commit();
11849         }
11850     },
11851
11852     /**
11853      * Cancel outstanding changes on all changed records.
11854      */
11855     rejectChanges : function(){
11856         var m = this.modified.slice(0);
11857         this.modified = [];
11858         for(var i = 0, len = m.length; i < len; i++){
11859             m[i].reject();
11860         }
11861     },
11862
11863     onMetaChange : function(meta, rtype, o){
11864         this.recordType = rtype;
11865         this.fields = rtype.prototype.fields;
11866         delete this.snapshot;
11867         this.sortInfo = meta.sortInfo || this.sortInfo;
11868         this.modified = [];
11869         this.fireEvent('metachange', this, this.reader.meta);
11870     },
11871     
11872     moveIndex : function(data, type)
11873     {
11874         var index = this.indexOf(data);
11875         
11876         var newIndex = index + type;
11877         
11878         this.remove(data);
11879         
11880         this.insert(newIndex, data);
11881         
11882     }
11883 });/*
11884  * Based on:
11885  * Ext JS Library 1.1.1
11886  * Copyright(c) 2006-2007, Ext JS, LLC.
11887  *
11888  * Originally Released Under LGPL - original licence link has changed is not relivant.
11889  *
11890  * Fork - LGPL
11891  * <script type="text/javascript">
11892  */
11893
11894 /**
11895  * @class Roo.data.SimpleStore
11896  * @extends Roo.data.Store
11897  * Small helper class to make creating Stores from Array data easier.
11898  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11899  * @cfg {Array} fields An array of field definition objects, or field name strings.
11900  * @cfg {Array} data The multi-dimensional array of data
11901  * @constructor
11902  * @param {Object} config
11903  */
11904 Roo.data.SimpleStore = function(config){
11905     Roo.data.SimpleStore.superclass.constructor.call(this, {
11906         isLocal : true,
11907         reader: new Roo.data.ArrayReader({
11908                 id: config.id
11909             },
11910             Roo.data.Record.create(config.fields)
11911         ),
11912         proxy : new Roo.data.MemoryProxy(config.data)
11913     });
11914     this.load();
11915 };
11916 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11917  * Based on:
11918  * Ext JS Library 1.1.1
11919  * Copyright(c) 2006-2007, Ext JS, LLC.
11920  *
11921  * Originally Released Under LGPL - original licence link has changed is not relivant.
11922  *
11923  * Fork - LGPL
11924  * <script type="text/javascript">
11925  */
11926
11927 /**
11928 /**
11929  * @extends Roo.data.Store
11930  * @class Roo.data.JsonStore
11931  * Small helper class to make creating Stores for JSON data easier. <br/>
11932 <pre><code>
11933 var store = new Roo.data.JsonStore({
11934     url: 'get-images.php',
11935     root: 'images',
11936     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11937 });
11938 </code></pre>
11939  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11940  * JsonReader and HttpProxy (unless inline data is provided).</b>
11941  * @cfg {Array} fields An array of field definition objects, or field name strings.
11942  * @constructor
11943  * @param {Object} config
11944  */
11945 Roo.data.JsonStore = function(c){
11946     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11947         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11948         reader: new Roo.data.JsonReader(c, c.fields)
11949     }));
11950 };
11951 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11952  * Based on:
11953  * Ext JS Library 1.1.1
11954  * Copyright(c) 2006-2007, Ext JS, LLC.
11955  *
11956  * Originally Released Under LGPL - original licence link has changed is not relivant.
11957  *
11958  * Fork - LGPL
11959  * <script type="text/javascript">
11960  */
11961
11962  
11963 Roo.data.Field = function(config){
11964     if(typeof config == "string"){
11965         config = {name: config};
11966     }
11967     Roo.apply(this, config);
11968     
11969     if(!this.type){
11970         this.type = "auto";
11971     }
11972     
11973     var st = Roo.data.SortTypes;
11974     // named sortTypes are supported, here we look them up
11975     if(typeof this.sortType == "string"){
11976         this.sortType = st[this.sortType];
11977     }
11978     
11979     // set default sortType for strings and dates
11980     if(!this.sortType){
11981         switch(this.type){
11982             case "string":
11983                 this.sortType = st.asUCString;
11984                 break;
11985             case "date":
11986                 this.sortType = st.asDate;
11987                 break;
11988             default:
11989                 this.sortType = st.none;
11990         }
11991     }
11992
11993     // define once
11994     var stripRe = /[\$,%]/g;
11995
11996     // prebuilt conversion function for this field, instead of
11997     // switching every time we're reading a value
11998     if(!this.convert){
11999         var cv, dateFormat = this.dateFormat;
12000         switch(this.type){
12001             case "":
12002             case "auto":
12003             case undefined:
12004                 cv = function(v){ return v; };
12005                 break;
12006             case "string":
12007                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12008                 break;
12009             case "int":
12010                 cv = function(v){
12011                     return v !== undefined && v !== null && v !== '' ?
12012                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12013                     };
12014                 break;
12015             case "float":
12016                 cv = function(v){
12017                     return v !== undefined && v !== null && v !== '' ?
12018                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12019                     };
12020                 break;
12021             case "bool":
12022             case "boolean":
12023                 cv = function(v){ return v === true || v === "true" || v == 1; };
12024                 break;
12025             case "date":
12026                 cv = function(v){
12027                     if(!v){
12028                         return '';
12029                     }
12030                     if(v instanceof Date){
12031                         return v;
12032                     }
12033                     if(dateFormat){
12034                         if(dateFormat == "timestamp"){
12035                             return new Date(v*1000);
12036                         }
12037                         return Date.parseDate(v, dateFormat);
12038                     }
12039                     var parsed = Date.parse(v);
12040                     return parsed ? new Date(parsed) : null;
12041                 };
12042              break;
12043             
12044         }
12045         this.convert = cv;
12046     }
12047 };
12048
12049 Roo.data.Field.prototype = {
12050     dateFormat: null,
12051     defaultValue: "",
12052     mapping: null,
12053     sortType : null,
12054     sortDir : "ASC"
12055 };/*
12056  * Based on:
12057  * Ext JS Library 1.1.1
12058  * Copyright(c) 2006-2007, Ext JS, LLC.
12059  *
12060  * Originally Released Under LGPL - original licence link has changed is not relivant.
12061  *
12062  * Fork - LGPL
12063  * <script type="text/javascript">
12064  */
12065  
12066 // Base class for reading structured data from a data source.  This class is intended to be
12067 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12068
12069 /**
12070  * @class Roo.data.DataReader
12071  * Base class for reading structured data from a data source.  This class is intended to be
12072  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12073  */
12074
12075 Roo.data.DataReader = function(meta, recordType){
12076     
12077     this.meta = meta;
12078     
12079     this.recordType = recordType instanceof Array ? 
12080         Roo.data.Record.create(recordType) : recordType;
12081 };
12082
12083 Roo.data.DataReader.prototype = {
12084      /**
12085      * Create an empty record
12086      * @param {Object} data (optional) - overlay some values
12087      * @return {Roo.data.Record} record created.
12088      */
12089     newRow :  function(d) {
12090         var da =  {};
12091         this.recordType.prototype.fields.each(function(c) {
12092             switch( c.type) {
12093                 case 'int' : da[c.name] = 0; break;
12094                 case 'date' : da[c.name] = new Date(); break;
12095                 case 'float' : da[c.name] = 0.0; break;
12096                 case 'boolean' : da[c.name] = false; break;
12097                 default : da[c.name] = ""; break;
12098             }
12099             
12100         });
12101         return new this.recordType(Roo.apply(da, d));
12102     }
12103     
12104 };/*
12105  * Based on:
12106  * Ext JS Library 1.1.1
12107  * Copyright(c) 2006-2007, Ext JS, LLC.
12108  *
12109  * Originally Released Under LGPL - original licence link has changed is not relivant.
12110  *
12111  * Fork - LGPL
12112  * <script type="text/javascript">
12113  */
12114
12115 /**
12116  * @class Roo.data.DataProxy
12117  * @extends Roo.data.Observable
12118  * This class is an abstract base class for implementations which provide retrieval of
12119  * unformatted data objects.<br>
12120  * <p>
12121  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12122  * (of the appropriate type which knows how to parse the data object) to provide a block of
12123  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12124  * <p>
12125  * Custom implementations must implement the load method as described in
12126  * {@link Roo.data.HttpProxy#load}.
12127  */
12128 Roo.data.DataProxy = function(){
12129     this.addEvents({
12130         /**
12131          * @event beforeload
12132          * Fires before a network request is made to retrieve a data object.
12133          * @param {Object} This DataProxy object.
12134          * @param {Object} params The params parameter to the load function.
12135          */
12136         beforeload : true,
12137         /**
12138          * @event load
12139          * Fires before the load method's callback is called.
12140          * @param {Object} This DataProxy object.
12141          * @param {Object} o The data object.
12142          * @param {Object} arg The callback argument object passed to the load function.
12143          */
12144         load : true,
12145         /**
12146          * @event loadexception
12147          * Fires if an Exception occurs during data retrieval.
12148          * @param {Object} This DataProxy object.
12149          * @param {Object} o The data object.
12150          * @param {Object} arg The callback argument object passed to the load function.
12151          * @param {Object} e The Exception.
12152          */
12153         loadexception : true
12154     });
12155     Roo.data.DataProxy.superclass.constructor.call(this);
12156 };
12157
12158 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12159
12160     /**
12161      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12162      */
12163 /*
12164  * Based on:
12165  * Ext JS Library 1.1.1
12166  * Copyright(c) 2006-2007, Ext JS, LLC.
12167  *
12168  * Originally Released Under LGPL - original licence link has changed is not relivant.
12169  *
12170  * Fork - LGPL
12171  * <script type="text/javascript">
12172  */
12173 /**
12174  * @class Roo.data.MemoryProxy
12175  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12176  * to the Reader when its load method is called.
12177  * @constructor
12178  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12179  */
12180 Roo.data.MemoryProxy = function(data){
12181     if (data.data) {
12182         data = data.data;
12183     }
12184     Roo.data.MemoryProxy.superclass.constructor.call(this);
12185     this.data = data;
12186 };
12187
12188 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12189     
12190     /**
12191      * Load data from the requested source (in this case an in-memory
12192      * data object passed to the constructor), read the data object into
12193      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12194      * process that block using the passed callback.
12195      * @param {Object} params This parameter is not used by the MemoryProxy class.
12196      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12197      * object into a block of Roo.data.Records.
12198      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12199      * The function must be passed <ul>
12200      * <li>The Record block object</li>
12201      * <li>The "arg" argument from the load function</li>
12202      * <li>A boolean success indicator</li>
12203      * </ul>
12204      * @param {Object} scope The scope in which to call the callback
12205      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12206      */
12207     load : function(params, reader, callback, scope, arg){
12208         params = params || {};
12209         var result;
12210         try {
12211             result = reader.readRecords(this.data);
12212         }catch(e){
12213             this.fireEvent("loadexception", this, arg, null, e);
12214             callback.call(scope, null, arg, false);
12215             return;
12216         }
12217         callback.call(scope, result, arg, true);
12218     },
12219     
12220     // private
12221     update : function(params, records){
12222         
12223     }
12224 });/*
12225  * Based on:
12226  * Ext JS Library 1.1.1
12227  * Copyright(c) 2006-2007, Ext JS, LLC.
12228  *
12229  * Originally Released Under LGPL - original licence link has changed is not relivant.
12230  *
12231  * Fork - LGPL
12232  * <script type="text/javascript">
12233  */
12234 /**
12235  * @class Roo.data.HttpProxy
12236  * @extends Roo.data.DataProxy
12237  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12238  * configured to reference a certain URL.<br><br>
12239  * <p>
12240  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12241  * from which the running page was served.<br><br>
12242  * <p>
12243  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12244  * <p>
12245  * Be aware that to enable the browser to parse an XML document, the server must set
12246  * the Content-Type header in the HTTP response to "text/xml".
12247  * @constructor
12248  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12249  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12250  * will be used to make the request.
12251  */
12252 Roo.data.HttpProxy = function(conn){
12253     Roo.data.HttpProxy.superclass.constructor.call(this);
12254     // is conn a conn config or a real conn?
12255     this.conn = conn;
12256     this.useAjax = !conn || !conn.events;
12257   
12258 };
12259
12260 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12261     // thse are take from connection...
12262     
12263     /**
12264      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12265      */
12266     /**
12267      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12268      * extra parameters to each request made by this object. (defaults to undefined)
12269      */
12270     /**
12271      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12272      *  to each request made by this object. (defaults to undefined)
12273      */
12274     /**
12275      * @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)
12276      */
12277     /**
12278      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12279      */
12280      /**
12281      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12282      * @type Boolean
12283      */
12284   
12285
12286     /**
12287      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12288      * @type Boolean
12289      */
12290     /**
12291      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12292      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12293      * a finer-grained basis than the DataProxy events.
12294      */
12295     getConnection : function(){
12296         return this.useAjax ? Roo.Ajax : this.conn;
12297     },
12298
12299     /**
12300      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12301      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12302      * process that block using the passed callback.
12303      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12304      * for the request to the remote server.
12305      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12306      * object into a block of Roo.data.Records.
12307      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12308      * The function must be passed <ul>
12309      * <li>The Record block object</li>
12310      * <li>The "arg" argument from the load function</li>
12311      * <li>A boolean success indicator</li>
12312      * </ul>
12313      * @param {Object} scope The scope in which to call the callback
12314      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12315      */
12316     load : function(params, reader, callback, scope, arg){
12317         if(this.fireEvent("beforeload", this, params) !== false){
12318             var  o = {
12319                 params : params || {},
12320                 request: {
12321                     callback : callback,
12322                     scope : scope,
12323                     arg : arg
12324                 },
12325                 reader: reader,
12326                 callback : this.loadResponse,
12327                 scope: this
12328             };
12329             if(this.useAjax){
12330                 Roo.applyIf(o, this.conn);
12331                 if(this.activeRequest){
12332                     Roo.Ajax.abort(this.activeRequest);
12333                 }
12334                 this.activeRequest = Roo.Ajax.request(o);
12335             }else{
12336                 this.conn.request(o);
12337             }
12338         }else{
12339             callback.call(scope||this, null, arg, false);
12340         }
12341     },
12342
12343     // private
12344     loadResponse : function(o, success, response){
12345         delete this.activeRequest;
12346         if(!success){
12347             this.fireEvent("loadexception", this, o, response);
12348             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12349             return;
12350         }
12351         var result;
12352         try {
12353             result = o.reader.read(response);
12354         }catch(e){
12355             this.fireEvent("loadexception", this, o, response, e);
12356             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12357             return;
12358         }
12359         
12360         this.fireEvent("load", this, o, o.request.arg);
12361         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12362     },
12363
12364     // private
12365     update : function(dataSet){
12366
12367     },
12368
12369     // private
12370     updateResponse : function(dataSet){
12371
12372     }
12373 });/*
12374  * Based on:
12375  * Ext JS Library 1.1.1
12376  * Copyright(c) 2006-2007, Ext JS, LLC.
12377  *
12378  * Originally Released Under LGPL - original licence link has changed is not relivant.
12379  *
12380  * Fork - LGPL
12381  * <script type="text/javascript">
12382  */
12383
12384 /**
12385  * @class Roo.data.ScriptTagProxy
12386  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12387  * other than the originating domain of the running page.<br><br>
12388  * <p>
12389  * <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
12390  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12391  * <p>
12392  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12393  * source code that is used as the source inside a &lt;script> tag.<br><br>
12394  * <p>
12395  * In order for the browser to process the returned data, the server must wrap the data object
12396  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12397  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12398  * depending on whether the callback name was passed:
12399  * <p>
12400  * <pre><code>
12401 boolean scriptTag = false;
12402 String cb = request.getParameter("callback");
12403 if (cb != null) {
12404     scriptTag = true;
12405     response.setContentType("text/javascript");
12406 } else {
12407     response.setContentType("application/x-json");
12408 }
12409 Writer out = response.getWriter();
12410 if (scriptTag) {
12411     out.write(cb + "(");
12412 }
12413 out.print(dataBlock.toJsonString());
12414 if (scriptTag) {
12415     out.write(");");
12416 }
12417 </pre></code>
12418  *
12419  * @constructor
12420  * @param {Object} config A configuration object.
12421  */
12422 Roo.data.ScriptTagProxy = function(config){
12423     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12424     Roo.apply(this, config);
12425     this.head = document.getElementsByTagName("head")[0];
12426 };
12427
12428 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12429
12430 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12431     /**
12432      * @cfg {String} url The URL from which to request the data object.
12433      */
12434     /**
12435      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12436      */
12437     timeout : 30000,
12438     /**
12439      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12440      * the server the name of the callback function set up by the load call to process the returned data object.
12441      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12442      * javascript output which calls this named function passing the data object as its only parameter.
12443      */
12444     callbackParam : "callback",
12445     /**
12446      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12447      * name to the request.
12448      */
12449     nocache : true,
12450
12451     /**
12452      * Load data from the configured URL, read the data object into
12453      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12454      * process that block using the passed callback.
12455      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12456      * for the request to the remote server.
12457      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12458      * object into a block of Roo.data.Records.
12459      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12460      * The function must be passed <ul>
12461      * <li>The Record block object</li>
12462      * <li>The "arg" argument from the load function</li>
12463      * <li>A boolean success indicator</li>
12464      * </ul>
12465      * @param {Object} scope The scope in which to call the callback
12466      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12467      */
12468     load : function(params, reader, callback, scope, arg){
12469         if(this.fireEvent("beforeload", this, params) !== false){
12470
12471             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12472
12473             var url = this.url;
12474             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12475             if(this.nocache){
12476                 url += "&_dc=" + (new Date().getTime());
12477             }
12478             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12479             var trans = {
12480                 id : transId,
12481                 cb : "stcCallback"+transId,
12482                 scriptId : "stcScript"+transId,
12483                 params : params,
12484                 arg : arg,
12485                 url : url,
12486                 callback : callback,
12487                 scope : scope,
12488                 reader : reader
12489             };
12490             var conn = this;
12491
12492             window[trans.cb] = function(o){
12493                 conn.handleResponse(o, trans);
12494             };
12495
12496             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12497
12498             if(this.autoAbort !== false){
12499                 this.abort();
12500             }
12501
12502             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12503
12504             var script = document.createElement("script");
12505             script.setAttribute("src", url);
12506             script.setAttribute("type", "text/javascript");
12507             script.setAttribute("id", trans.scriptId);
12508             this.head.appendChild(script);
12509
12510             this.trans = trans;
12511         }else{
12512             callback.call(scope||this, null, arg, false);
12513         }
12514     },
12515
12516     // private
12517     isLoading : function(){
12518         return this.trans ? true : false;
12519     },
12520
12521     /**
12522      * Abort the current server request.
12523      */
12524     abort : function(){
12525         if(this.isLoading()){
12526             this.destroyTrans(this.trans);
12527         }
12528     },
12529
12530     // private
12531     destroyTrans : function(trans, isLoaded){
12532         this.head.removeChild(document.getElementById(trans.scriptId));
12533         clearTimeout(trans.timeoutId);
12534         if(isLoaded){
12535             window[trans.cb] = undefined;
12536             try{
12537                 delete window[trans.cb];
12538             }catch(e){}
12539         }else{
12540             // if hasn't been loaded, wait for load to remove it to prevent script error
12541             window[trans.cb] = function(){
12542                 window[trans.cb] = undefined;
12543                 try{
12544                     delete window[trans.cb];
12545                 }catch(e){}
12546             };
12547         }
12548     },
12549
12550     // private
12551     handleResponse : function(o, trans){
12552         this.trans = false;
12553         this.destroyTrans(trans, true);
12554         var result;
12555         try {
12556             result = trans.reader.readRecords(o);
12557         }catch(e){
12558             this.fireEvent("loadexception", this, o, trans.arg, e);
12559             trans.callback.call(trans.scope||window, null, trans.arg, false);
12560             return;
12561         }
12562         this.fireEvent("load", this, o, trans.arg);
12563         trans.callback.call(trans.scope||window, result, trans.arg, true);
12564     },
12565
12566     // private
12567     handleFailure : function(trans){
12568         this.trans = false;
12569         this.destroyTrans(trans, false);
12570         this.fireEvent("loadexception", this, null, trans.arg);
12571         trans.callback.call(trans.scope||window, null, trans.arg, false);
12572     }
12573 });/*
12574  * Based on:
12575  * Ext JS Library 1.1.1
12576  * Copyright(c) 2006-2007, Ext JS, LLC.
12577  *
12578  * Originally Released Under LGPL - original licence link has changed is not relivant.
12579  *
12580  * Fork - LGPL
12581  * <script type="text/javascript">
12582  */
12583
12584 /**
12585  * @class Roo.data.JsonReader
12586  * @extends Roo.data.DataReader
12587  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12588  * based on mappings in a provided Roo.data.Record constructor.
12589  * 
12590  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12591  * in the reply previously. 
12592  * 
12593  * <p>
12594  * Example code:
12595  * <pre><code>
12596 var RecordDef = Roo.data.Record.create([
12597     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12598     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12599 ]);
12600 var myReader = new Roo.data.JsonReader({
12601     totalProperty: "results",    // The property which contains the total dataset size (optional)
12602     root: "rows",                // The property which contains an Array of row objects
12603     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12604 }, RecordDef);
12605 </code></pre>
12606  * <p>
12607  * This would consume a JSON file like this:
12608  * <pre><code>
12609 { 'results': 2, 'rows': [
12610     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12611     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12612 }
12613 </code></pre>
12614  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12615  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12616  * paged from the remote server.
12617  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12618  * @cfg {String} root name of the property which contains the Array of row objects.
12619  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12620  * @cfg {Array} fields Array of field definition objects
12621  * @constructor
12622  * Create a new JsonReader
12623  * @param {Object} meta Metadata configuration options
12624  * @param {Object} recordType Either an Array of field definition objects,
12625  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12626  */
12627 Roo.data.JsonReader = function(meta, recordType){
12628     
12629     meta = meta || {};
12630     // set some defaults:
12631     Roo.applyIf(meta, {
12632         totalProperty: 'total',
12633         successProperty : 'success',
12634         root : 'data',
12635         id : 'id'
12636     });
12637     
12638     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12639 };
12640 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12641     
12642     /**
12643      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12644      * Used by Store query builder to append _requestMeta to params.
12645      * 
12646      */
12647     metaFromRemote : false,
12648     /**
12649      * This method is only used by a DataProxy which has retrieved data from a remote server.
12650      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12651      * @return {Object} data A data block which is used by an Roo.data.Store object as
12652      * a cache of Roo.data.Records.
12653      */
12654     read : function(response){
12655         var json = response.responseText;
12656        
12657         var o = /* eval:var:o */ eval("("+json+")");
12658         if(!o) {
12659             throw {message: "JsonReader.read: Json object not found"};
12660         }
12661         
12662         if(o.metaData){
12663             
12664             delete this.ef;
12665             this.metaFromRemote = true;
12666             this.meta = o.metaData;
12667             this.recordType = Roo.data.Record.create(o.metaData.fields);
12668             this.onMetaChange(this.meta, this.recordType, o);
12669         }
12670         return this.readRecords(o);
12671     },
12672
12673     // private function a store will implement
12674     onMetaChange : function(meta, recordType, o){
12675
12676     },
12677
12678     /**
12679          * @ignore
12680          */
12681     simpleAccess: function(obj, subsc) {
12682         return obj[subsc];
12683     },
12684
12685         /**
12686          * @ignore
12687          */
12688     getJsonAccessor: function(){
12689         var re = /[\[\.]/;
12690         return function(expr) {
12691             try {
12692                 return(re.test(expr))
12693                     ? new Function("obj", "return obj." + expr)
12694                     : function(obj){
12695                         return obj[expr];
12696                     };
12697             } catch(e){}
12698             return Roo.emptyFn;
12699         };
12700     }(),
12701
12702     /**
12703      * Create a data block containing Roo.data.Records from an XML document.
12704      * @param {Object} o An object which contains an Array of row objects in the property specified
12705      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12706      * which contains the total size of the dataset.
12707      * @return {Object} data A data block which is used by an Roo.data.Store object as
12708      * a cache of Roo.data.Records.
12709      */
12710     readRecords : function(o){
12711         /**
12712          * After any data loads, the raw JSON data is available for further custom processing.
12713          * @type Object
12714          */
12715         this.o = o;
12716         var s = this.meta, Record = this.recordType,
12717             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12718
12719 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12720         if (!this.ef) {
12721             if(s.totalProperty) {
12722                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12723                 }
12724                 if(s.successProperty) {
12725                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12726                 }
12727                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12728                 if (s.id) {
12729                         var g = this.getJsonAccessor(s.id);
12730                         this.getId = function(rec) {
12731                                 var r = g(rec);  
12732                                 return (r === undefined || r === "") ? null : r;
12733                         };
12734                 } else {
12735                         this.getId = function(){return null;};
12736                 }
12737             this.ef = [];
12738             for(var jj = 0; jj < fl; jj++){
12739                 f = fi[jj];
12740                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12741                 this.ef[jj] = this.getJsonAccessor(map);
12742             }
12743         }
12744
12745         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12746         if(s.totalProperty){
12747             var vt = parseInt(this.getTotal(o), 10);
12748             if(!isNaN(vt)){
12749                 totalRecords = vt;
12750             }
12751         }
12752         if(s.successProperty){
12753             var vs = this.getSuccess(o);
12754             if(vs === false || vs === 'false'){
12755                 success = false;
12756             }
12757         }
12758         var records = [];
12759         for(var i = 0; i < c; i++){
12760                 var n = root[i];
12761             var values = {};
12762             var id = this.getId(n);
12763             for(var j = 0; j < fl; j++){
12764                 f = fi[j];
12765             var v = this.ef[j](n);
12766             if (!f.convert) {
12767                 Roo.log('missing convert for ' + f.name);
12768                 Roo.log(f);
12769                 continue;
12770             }
12771             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12772             }
12773             var record = new Record(values, id);
12774             record.json = n;
12775             records[i] = record;
12776         }
12777         return {
12778             raw : o,
12779             success : success,
12780             records : records,
12781             totalRecords : totalRecords
12782         };
12783     }
12784 });/*
12785  * Based on:
12786  * Ext JS Library 1.1.1
12787  * Copyright(c) 2006-2007, Ext JS, LLC.
12788  *
12789  * Originally Released Under LGPL - original licence link has changed is not relivant.
12790  *
12791  * Fork - LGPL
12792  * <script type="text/javascript">
12793  */
12794
12795 /**
12796  * @class Roo.data.ArrayReader
12797  * @extends Roo.data.DataReader
12798  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12799  * Each element of that Array represents a row of data fields. The
12800  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12801  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12802  * <p>
12803  * Example code:.
12804  * <pre><code>
12805 var RecordDef = Roo.data.Record.create([
12806     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12807     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12808 ]);
12809 var myReader = new Roo.data.ArrayReader({
12810     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12811 }, RecordDef);
12812 </code></pre>
12813  * <p>
12814  * This would consume an Array like this:
12815  * <pre><code>
12816 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12817   </code></pre>
12818  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12819  * @constructor
12820  * Create a new JsonReader
12821  * @param {Object} meta Metadata configuration options.
12822  * @param {Object} recordType Either an Array of field definition objects
12823  * as specified to {@link Roo.data.Record#create},
12824  * or an {@link Roo.data.Record} object
12825  * created using {@link Roo.data.Record#create}.
12826  */
12827 Roo.data.ArrayReader = function(meta, recordType){
12828     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12829 };
12830
12831 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12832     /**
12833      * Create a data block containing Roo.data.Records from an XML document.
12834      * @param {Object} o An Array of row objects which represents the dataset.
12835      * @return {Object} data A data block which is used by an Roo.data.Store object as
12836      * a cache of Roo.data.Records.
12837      */
12838     readRecords : function(o){
12839         var sid = this.meta ? this.meta.id : null;
12840         var recordType = this.recordType, fields = recordType.prototype.fields;
12841         var records = [];
12842         var root = o;
12843             for(var i = 0; i < root.length; i++){
12844                     var n = root[i];
12845                 var values = {};
12846                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12847                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12848                 var f = fields.items[j];
12849                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12850                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12851                 v = f.convert(v);
12852                 values[f.name] = v;
12853             }
12854                 var record = new recordType(values, id);
12855                 record.json = n;
12856                 records[records.length] = record;
12857             }
12858             return {
12859                 records : records,
12860                 totalRecords : records.length
12861             };
12862     }
12863 });/*
12864  * - LGPL
12865  * * 
12866  */
12867
12868 /**
12869  * @class Roo.bootstrap.ComboBox
12870  * @extends Roo.bootstrap.TriggerField
12871  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12872  * @cfg {Boolean} append (true|false) default false
12873  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12874  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12875  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12876  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12877  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12878  * @cfg {Boolean} animate default true
12879  * @cfg {Boolean} emptyResultText only for touch device
12880  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12881  * @cfg {String} emptyTitle default ''
12882  * @constructor
12883  * Create a new ComboBox.
12884  * @param {Object} config Configuration options
12885  */
12886 Roo.bootstrap.ComboBox = function(config){
12887     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12888     this.addEvents({
12889         /**
12890          * @event expand
12891          * Fires when the dropdown list is expanded
12892         * @param {Roo.bootstrap.ComboBox} combo This combo box
12893         */
12894         'expand' : true,
12895         /**
12896          * @event collapse
12897          * Fires when the dropdown list is collapsed
12898         * @param {Roo.bootstrap.ComboBox} combo This combo box
12899         */
12900         'collapse' : true,
12901         /**
12902          * @event beforeselect
12903          * Fires before a list item is selected. Return false to cancel the selection.
12904         * @param {Roo.bootstrap.ComboBox} combo This combo box
12905         * @param {Roo.data.Record} record The data record returned from the underlying store
12906         * @param {Number} index The index of the selected item in the dropdown list
12907         */
12908         'beforeselect' : true,
12909         /**
12910          * @event select
12911          * Fires when a list item is selected
12912         * @param {Roo.bootstrap.ComboBox} combo This combo box
12913         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12914         * @param {Number} index The index of the selected item in the dropdown list
12915         */
12916         'select' : true,
12917         /**
12918          * @event beforequery
12919          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12920          * The event object passed has these properties:
12921         * @param {Roo.bootstrap.ComboBox} combo This combo box
12922         * @param {String} query The query
12923         * @param {Boolean} forceAll true to force "all" query
12924         * @param {Boolean} cancel true to cancel the query
12925         * @param {Object} e The query event object
12926         */
12927         'beforequery': true,
12928          /**
12929          * @event add
12930          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12931         * @param {Roo.bootstrap.ComboBox} combo This combo box
12932         */
12933         'add' : true,
12934         /**
12935          * @event edit
12936          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12937         * @param {Roo.bootstrap.ComboBox} combo This combo box
12938         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12939         */
12940         'edit' : true,
12941         /**
12942          * @event remove
12943          * Fires when the remove value from the combobox array
12944         * @param {Roo.bootstrap.ComboBox} combo This combo box
12945         */
12946         'remove' : true,
12947         /**
12948          * @event afterremove
12949          * Fires when the remove value from the combobox array
12950         * @param {Roo.bootstrap.ComboBox} combo This combo box
12951         */
12952         'afterremove' : true,
12953         /**
12954          * @event specialfilter
12955          * Fires when specialfilter
12956             * @param {Roo.bootstrap.ComboBox} combo This combo box
12957             */
12958         'specialfilter' : true,
12959         /**
12960          * @event tick
12961          * Fires when tick the element
12962             * @param {Roo.bootstrap.ComboBox} combo This combo box
12963             */
12964         'tick' : true,
12965         /**
12966          * @event touchviewdisplay
12967          * Fires when touch view require special display (default is using displayField)
12968             * @param {Roo.bootstrap.ComboBox} combo This combo box
12969             * @param {Object} cfg set html .
12970             */
12971         'touchviewdisplay' : true
12972         
12973     });
12974     
12975     this.item = [];
12976     this.tickItems = [];
12977     
12978     this.selectedIndex = -1;
12979     if(this.mode == 'local'){
12980         if(config.queryDelay === undefined){
12981             this.queryDelay = 10;
12982         }
12983         if(config.minChars === undefined){
12984             this.minChars = 0;
12985         }
12986     }
12987 };
12988
12989 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12990      
12991     /**
12992      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12993      * rendering into an Roo.Editor, defaults to false)
12994      */
12995     /**
12996      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12997      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12998      */
12999     /**
13000      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13001      */
13002     /**
13003      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13004      * the dropdown list (defaults to undefined, with no header element)
13005      */
13006
13007      /**
13008      * @cfg {String/Roo.Template} tpl The template to use to render the output
13009      */
13010      
13011      /**
13012      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13013      */
13014     listWidth: undefined,
13015     /**
13016      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13017      * mode = 'remote' or 'text' if mode = 'local')
13018      */
13019     displayField: undefined,
13020     
13021     /**
13022      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13023      * mode = 'remote' or 'value' if mode = 'local'). 
13024      * Note: use of a valueField requires the user make a selection
13025      * in order for a value to be mapped.
13026      */
13027     valueField: undefined,
13028     /**
13029      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13030      */
13031     modalTitle : '',
13032     
13033     /**
13034      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13035      * field's data value (defaults to the underlying DOM element's name)
13036      */
13037     hiddenName: undefined,
13038     /**
13039      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13040      */
13041     listClass: '',
13042     /**
13043      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13044      */
13045     selectedClass: 'active',
13046     
13047     /**
13048      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13049      */
13050     shadow:'sides',
13051     /**
13052      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13053      * anchor positions (defaults to 'tl-bl')
13054      */
13055     listAlign: 'tl-bl?',
13056     /**
13057      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13058      */
13059     maxHeight: 300,
13060     /**
13061      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13062      * query specified by the allQuery config option (defaults to 'query')
13063      */
13064     triggerAction: 'query',
13065     /**
13066      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13067      * (defaults to 4, does not apply if editable = false)
13068      */
13069     minChars : 4,
13070     /**
13071      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13072      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13073      */
13074     typeAhead: false,
13075     /**
13076      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13077      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13078      */
13079     queryDelay: 500,
13080     /**
13081      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13082      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13083      */
13084     pageSize: 0,
13085     /**
13086      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13087      * when editable = true (defaults to false)
13088      */
13089     selectOnFocus:false,
13090     /**
13091      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13092      */
13093     queryParam: 'query',
13094     /**
13095      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13096      * when mode = 'remote' (defaults to 'Loading...')
13097      */
13098     loadingText: 'Loading...',
13099     /**
13100      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13101      */
13102     resizable: false,
13103     /**
13104      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13105      */
13106     handleHeight : 8,
13107     /**
13108      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13109      * traditional select (defaults to true)
13110      */
13111     editable: true,
13112     /**
13113      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13114      */
13115     allQuery: '',
13116     /**
13117      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13118      */
13119     mode: 'remote',
13120     /**
13121      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13122      * listWidth has a higher value)
13123      */
13124     minListWidth : 70,
13125     /**
13126      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13127      * allow the user to set arbitrary text into the field (defaults to false)
13128      */
13129     forceSelection:false,
13130     /**
13131      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13132      * if typeAhead = true (defaults to 250)
13133      */
13134     typeAheadDelay : 250,
13135     /**
13136      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13137      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13138      */
13139     valueNotFoundText : undefined,
13140     /**
13141      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13142      */
13143     blockFocus : false,
13144     
13145     /**
13146      * @cfg {Boolean} disableClear Disable showing of clear button.
13147      */
13148     disableClear : false,
13149     /**
13150      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13151      */
13152     alwaysQuery : false,
13153     
13154     /**
13155      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13156      */
13157     multiple : false,
13158     
13159     /**
13160      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13161      */
13162     invalidClass : "has-warning",
13163     
13164     /**
13165      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13166      */
13167     validClass : "has-success",
13168     
13169     /**
13170      * @cfg {Boolean} specialFilter (true|false) special filter default false
13171      */
13172     specialFilter : false,
13173     
13174     /**
13175      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13176      */
13177     mobileTouchView : true,
13178     
13179     /**
13180      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13181      */
13182     useNativeIOS : false,
13183     
13184     /**
13185      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13186      */
13187     mobile_restrict_height : false,
13188     
13189     ios_options : false,
13190     
13191     //private
13192     addicon : false,
13193     editicon: false,
13194     
13195     page: 0,
13196     hasQuery: false,
13197     append: false,
13198     loadNext: false,
13199     autoFocus : true,
13200     tickable : false,
13201     btnPosition : 'right',
13202     triggerList : true,
13203     showToggleBtn : true,
13204     animate : true,
13205     emptyResultText: 'Empty',
13206     triggerText : 'Select',
13207     emptyTitle : '',
13208     
13209     // element that contains real text value.. (when hidden is used..)
13210     
13211     getAutoCreate : function()
13212     {   
13213         var cfg = false;
13214         //render
13215         /*
13216          * Render classic select for iso
13217          */
13218         
13219         if(Roo.isIOS && this.useNativeIOS){
13220             cfg = this.getAutoCreateNativeIOS();
13221             return cfg;
13222         }
13223         
13224         /*
13225          * Touch Devices
13226          */
13227         
13228         if(Roo.isTouch && this.mobileTouchView){
13229             cfg = this.getAutoCreateTouchView();
13230             return cfg;;
13231         }
13232         
13233         /*
13234          *  Normal ComboBox
13235          */
13236         if(!this.tickable){
13237             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13238             return cfg;
13239         }
13240         
13241         /*
13242          *  ComboBox with tickable selections
13243          */
13244              
13245         var align = this.labelAlign || this.parentLabelAlign();
13246         
13247         cfg = {
13248             cls : 'form-group roo-combobox-tickable' //input-group
13249         };
13250         
13251         var btn_text_select = '';
13252         var btn_text_done = '';
13253         var btn_text_cancel = '';
13254         
13255         if (this.btn_text_show) {
13256             btn_text_select = 'Select';
13257             btn_text_done = 'Done';
13258             btn_text_cancel = 'Cancel'; 
13259         }
13260         
13261         var buttons = {
13262             tag : 'div',
13263             cls : 'tickable-buttons',
13264             cn : [
13265                 {
13266                     tag : 'button',
13267                     type : 'button',
13268                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13269                     //html : this.triggerText
13270                     html: btn_text_select
13271                 },
13272                 {
13273                     tag : 'button',
13274                     type : 'button',
13275                     name : 'ok',
13276                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13277                     //html : 'Done'
13278                     html: btn_text_done
13279                 },
13280                 {
13281                     tag : 'button',
13282                     type : 'button',
13283                     name : 'cancel',
13284                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13285                     //html : 'Cancel'
13286                     html: btn_text_cancel
13287                 }
13288             ]
13289         };
13290         
13291         if(this.editable){
13292             buttons.cn.unshift({
13293                 tag: 'input',
13294                 cls: 'roo-select2-search-field-input'
13295             });
13296         }
13297         
13298         var _this = this;
13299         
13300         Roo.each(buttons.cn, function(c){
13301             if (_this.size) {
13302                 c.cls += ' btn-' + _this.size;
13303             }
13304
13305             if (_this.disabled) {
13306                 c.disabled = true;
13307             }
13308         });
13309         
13310         var box = {
13311             tag: 'div',
13312             cn: [
13313                 {
13314                     tag: 'input',
13315                     type : 'hidden',
13316                     cls: 'form-hidden-field'
13317                 },
13318                 {
13319                     tag: 'ul',
13320                     cls: 'roo-select2-choices',
13321                     cn:[
13322                         {
13323                             tag: 'li',
13324                             cls: 'roo-select2-search-field',
13325                             cn: [
13326                                 buttons
13327                             ]
13328                         }
13329                     ]
13330                 }
13331             ]
13332         };
13333         
13334         var combobox = {
13335             cls: 'roo-select2-container input-group roo-select2-container-multi',
13336             cn: [
13337                 
13338                 box
13339 //                {
13340 //                    tag: 'ul',
13341 //                    cls: 'typeahead typeahead-long dropdown-menu',
13342 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13343 //                }
13344             ]
13345         };
13346         
13347         if(this.hasFeedback && !this.allowBlank){
13348             
13349             var feedback = {
13350                 tag: 'span',
13351                 cls: 'glyphicon form-control-feedback'
13352             };
13353
13354             combobox.cn.push(feedback);
13355         }
13356         
13357         var indicator = {
13358             tag : 'i',
13359             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13360             tooltip : 'This field is required'
13361         };
13362         if (Roo.bootstrap.version == 4) {
13363             indicator = {
13364                 tag : 'i',
13365                 style : 'display:none'
13366             };
13367         }
13368         if (align ==='left' && this.fieldLabel.length) {
13369             
13370             cfg.cls += ' roo-form-group-label-left row';
13371             
13372             cfg.cn = [
13373                 indicator,
13374                 {
13375                     tag: 'label',
13376                     'for' :  id,
13377                     cls : 'control-label col-form-label',
13378                     html : this.fieldLabel
13379
13380                 },
13381                 {
13382                     cls : "", 
13383                     cn: [
13384                         combobox
13385                     ]
13386                 }
13387
13388             ];
13389             
13390             var labelCfg = cfg.cn[1];
13391             var contentCfg = cfg.cn[2];
13392             
13393
13394             if(this.indicatorpos == 'right'){
13395                 
13396                 cfg.cn = [
13397                     {
13398                         tag: 'label',
13399                         'for' :  id,
13400                         cls : 'control-label col-form-label',
13401                         cn : [
13402                             {
13403                                 tag : 'span',
13404                                 html : this.fieldLabel
13405                             },
13406                             indicator
13407                         ]
13408                     },
13409                     {
13410                         cls : "",
13411                         cn: [
13412                             combobox
13413                         ]
13414                     }
13415
13416                 ];
13417                 
13418                 
13419                 
13420                 labelCfg = cfg.cn[0];
13421                 contentCfg = cfg.cn[1];
13422             
13423             }
13424             
13425             if(this.labelWidth > 12){
13426                 labelCfg.style = "width: " + this.labelWidth + 'px';
13427             }
13428             
13429             if(this.labelWidth < 13 && this.labelmd == 0){
13430                 this.labelmd = this.labelWidth;
13431             }
13432             
13433             if(this.labellg > 0){
13434                 labelCfg.cls += ' col-lg-' + this.labellg;
13435                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13436             }
13437             
13438             if(this.labelmd > 0){
13439                 labelCfg.cls += ' col-md-' + this.labelmd;
13440                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13441             }
13442             
13443             if(this.labelsm > 0){
13444                 labelCfg.cls += ' col-sm-' + this.labelsm;
13445                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13446             }
13447             
13448             if(this.labelxs > 0){
13449                 labelCfg.cls += ' col-xs-' + this.labelxs;
13450                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13451             }
13452                 
13453                 
13454         } else if ( this.fieldLabel.length) {
13455 //                Roo.log(" label");
13456                  cfg.cn = [
13457                    indicator,
13458                     {
13459                         tag: 'label',
13460                         //cls : 'input-group-addon',
13461                         html : this.fieldLabel
13462                     },
13463                     combobox
13464                 ];
13465                 
13466                 if(this.indicatorpos == 'right'){
13467                     cfg.cn = [
13468                         {
13469                             tag: 'label',
13470                             //cls : 'input-group-addon',
13471                             html : this.fieldLabel
13472                         },
13473                         indicator,
13474                         combobox
13475                     ];
13476                     
13477                 }
13478
13479         } else {
13480             
13481 //                Roo.log(" no label && no align");
13482                 cfg = combobox
13483                      
13484                 
13485         }
13486          
13487         var settings=this;
13488         ['xs','sm','md','lg'].map(function(size){
13489             if (settings[size]) {
13490                 cfg.cls += ' col-' + size + '-' + settings[size];
13491             }
13492         });
13493         
13494         return cfg;
13495         
13496     },
13497     
13498     _initEventsCalled : false,
13499     
13500     // private
13501     initEvents: function()
13502     {   
13503         if (this._initEventsCalled) { // as we call render... prevent looping...
13504             return;
13505         }
13506         this._initEventsCalled = true;
13507         
13508         if (!this.store) {
13509             throw "can not find store for combo";
13510         }
13511         
13512         this.indicator = this.indicatorEl();
13513         
13514         this.store = Roo.factory(this.store, Roo.data);
13515         this.store.parent = this;
13516         
13517         // if we are building from html. then this element is so complex, that we can not really
13518         // use the rendered HTML.
13519         // so we have to trash and replace the previous code.
13520         if (Roo.XComponent.build_from_html) {
13521             // remove this element....
13522             var e = this.el.dom, k=0;
13523             while (e ) { e = e.previousSibling;  ++k;}
13524
13525             this.el.remove();
13526             
13527             this.el=false;
13528             this.rendered = false;
13529             
13530             this.render(this.parent().getChildContainer(true), k);
13531         }
13532         
13533         if(Roo.isIOS && this.useNativeIOS){
13534             this.initIOSView();
13535             return;
13536         }
13537         
13538         /*
13539          * Touch Devices
13540          */
13541         
13542         if(Roo.isTouch && this.mobileTouchView){
13543             this.initTouchView();
13544             return;
13545         }
13546         
13547         if(this.tickable){
13548             this.initTickableEvents();
13549             return;
13550         }
13551         
13552         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13553         
13554         if(this.hiddenName){
13555             
13556             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13557             
13558             this.hiddenField.dom.value =
13559                 this.hiddenValue !== undefined ? this.hiddenValue :
13560                 this.value !== undefined ? this.value : '';
13561
13562             // prevent input submission
13563             this.el.dom.removeAttribute('name');
13564             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13565              
13566              
13567         }
13568         //if(Roo.isGecko){
13569         //    this.el.dom.setAttribute('autocomplete', 'off');
13570         //}
13571         
13572         var cls = 'x-combo-list';
13573         
13574         //this.list = new Roo.Layer({
13575         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13576         //});
13577         
13578         var _this = this;
13579         
13580         (function(){
13581             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13582             _this.list.setWidth(lw);
13583         }).defer(100);
13584         
13585         this.list.on('mouseover', this.onViewOver, this);
13586         this.list.on('mousemove', this.onViewMove, this);
13587         this.list.on('scroll', this.onViewScroll, this);
13588         
13589         /*
13590         this.list.swallowEvent('mousewheel');
13591         this.assetHeight = 0;
13592
13593         if(this.title){
13594             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13595             this.assetHeight += this.header.getHeight();
13596         }
13597
13598         this.innerList = this.list.createChild({cls:cls+'-inner'});
13599         this.innerList.on('mouseover', this.onViewOver, this);
13600         this.innerList.on('mousemove', this.onViewMove, this);
13601         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13602         
13603         if(this.allowBlank && !this.pageSize && !this.disableClear){
13604             this.footer = this.list.createChild({cls:cls+'-ft'});
13605             this.pageTb = new Roo.Toolbar(this.footer);
13606            
13607         }
13608         if(this.pageSize){
13609             this.footer = this.list.createChild({cls:cls+'-ft'});
13610             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13611                     {pageSize: this.pageSize});
13612             
13613         }
13614         
13615         if (this.pageTb && this.allowBlank && !this.disableClear) {
13616             var _this = this;
13617             this.pageTb.add(new Roo.Toolbar.Fill(), {
13618                 cls: 'x-btn-icon x-btn-clear',
13619                 text: '&#160;',
13620                 handler: function()
13621                 {
13622                     _this.collapse();
13623                     _this.clearValue();
13624                     _this.onSelect(false, -1);
13625                 }
13626             });
13627         }
13628         if (this.footer) {
13629             this.assetHeight += this.footer.getHeight();
13630         }
13631         */
13632             
13633         if(!this.tpl){
13634             this.tpl = Roo.bootstrap.version == 4 ?
13635                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13636                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13637         }
13638
13639         this.view = new Roo.View(this.list, this.tpl, {
13640             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13641         });
13642         //this.view.wrapEl.setDisplayed(false);
13643         this.view.on('click', this.onViewClick, this);
13644         
13645         
13646         this.store.on('beforeload', this.onBeforeLoad, this);
13647         this.store.on('load', this.onLoad, this);
13648         this.store.on('loadexception', this.onLoadException, this);
13649         /*
13650         if(this.resizable){
13651             this.resizer = new Roo.Resizable(this.list,  {
13652                pinned:true, handles:'se'
13653             });
13654             this.resizer.on('resize', function(r, w, h){
13655                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13656                 this.listWidth = w;
13657                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13658                 this.restrictHeight();
13659             }, this);
13660             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13661         }
13662         */
13663         if(!this.editable){
13664             this.editable = true;
13665             this.setEditable(false);
13666         }
13667         
13668         /*
13669         
13670         if (typeof(this.events.add.listeners) != 'undefined') {
13671             
13672             this.addicon = this.wrap.createChild(
13673                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13674        
13675             this.addicon.on('click', function(e) {
13676                 this.fireEvent('add', this);
13677             }, this);
13678         }
13679         if (typeof(this.events.edit.listeners) != 'undefined') {
13680             
13681             this.editicon = this.wrap.createChild(
13682                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13683             if (this.addicon) {
13684                 this.editicon.setStyle('margin-left', '40px');
13685             }
13686             this.editicon.on('click', function(e) {
13687                 
13688                 // we fire even  if inothing is selected..
13689                 this.fireEvent('edit', this, this.lastData );
13690                 
13691             }, this);
13692         }
13693         */
13694         
13695         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13696             "up" : function(e){
13697                 this.inKeyMode = true;
13698                 this.selectPrev();
13699             },
13700
13701             "down" : function(e){
13702                 if(!this.isExpanded()){
13703                     this.onTriggerClick();
13704                 }else{
13705                     this.inKeyMode = true;
13706                     this.selectNext();
13707                 }
13708             },
13709
13710             "enter" : function(e){
13711 //                this.onViewClick();
13712                 //return true;
13713                 this.collapse();
13714                 
13715                 if(this.fireEvent("specialkey", this, e)){
13716                     this.onViewClick(false);
13717                 }
13718                 
13719                 return true;
13720             },
13721
13722             "esc" : function(e){
13723                 this.collapse();
13724             },
13725
13726             "tab" : function(e){
13727                 this.collapse();
13728                 
13729                 if(this.fireEvent("specialkey", this, e)){
13730                     this.onViewClick(false);
13731                 }
13732                 
13733                 return true;
13734             },
13735
13736             scope : this,
13737
13738             doRelay : function(foo, bar, hname){
13739                 if(hname == 'down' || this.scope.isExpanded()){
13740                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13741                 }
13742                 return true;
13743             },
13744
13745             forceKeyDown: true
13746         });
13747         
13748         
13749         this.queryDelay = Math.max(this.queryDelay || 10,
13750                 this.mode == 'local' ? 10 : 250);
13751         
13752         
13753         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13754         
13755         if(this.typeAhead){
13756             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13757         }
13758         if(this.editable !== false){
13759             this.inputEl().on("keyup", this.onKeyUp, this);
13760         }
13761         if(this.forceSelection){
13762             this.inputEl().on('blur', this.doForce, this);
13763         }
13764         
13765         if(this.multiple){
13766             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13767             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13768         }
13769     },
13770     
13771     initTickableEvents: function()
13772     {   
13773         this.createList();
13774         
13775         if(this.hiddenName){
13776             
13777             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13778             
13779             this.hiddenField.dom.value =
13780                 this.hiddenValue !== undefined ? this.hiddenValue :
13781                 this.value !== undefined ? this.value : '';
13782
13783             // prevent input submission
13784             this.el.dom.removeAttribute('name');
13785             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13786              
13787              
13788         }
13789         
13790 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13791         
13792         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13793         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13794         if(this.triggerList){
13795             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13796         }
13797          
13798         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13799         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13800         
13801         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13802         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13803         
13804         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13805         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13806         
13807         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13808         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13809         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13810         
13811         this.okBtn.hide();
13812         this.cancelBtn.hide();
13813         
13814         var _this = this;
13815         
13816         (function(){
13817             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13818             _this.list.setWidth(lw);
13819         }).defer(100);
13820         
13821         this.list.on('mouseover', this.onViewOver, this);
13822         this.list.on('mousemove', this.onViewMove, this);
13823         
13824         this.list.on('scroll', this.onViewScroll, this);
13825         
13826         if(!this.tpl){
13827             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13828                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13829         }
13830
13831         this.view = new Roo.View(this.list, this.tpl, {
13832             singleSelect:true,
13833             tickable:true,
13834             parent:this,
13835             store: this.store,
13836             selectedClass: this.selectedClass
13837         });
13838         
13839         //this.view.wrapEl.setDisplayed(false);
13840         this.view.on('click', this.onViewClick, this);
13841         
13842         
13843         
13844         this.store.on('beforeload', this.onBeforeLoad, this);
13845         this.store.on('load', this.onLoad, this);
13846         this.store.on('loadexception', this.onLoadException, this);
13847         
13848         if(this.editable){
13849             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13850                 "up" : function(e){
13851                     this.inKeyMode = true;
13852                     this.selectPrev();
13853                 },
13854
13855                 "down" : function(e){
13856                     this.inKeyMode = true;
13857                     this.selectNext();
13858                 },
13859
13860                 "enter" : function(e){
13861                     if(this.fireEvent("specialkey", this, e)){
13862                         this.onViewClick(false);
13863                     }
13864                     
13865                     return true;
13866                 },
13867
13868                 "esc" : function(e){
13869                     this.onTickableFooterButtonClick(e, false, false);
13870                 },
13871
13872                 "tab" : function(e){
13873                     this.fireEvent("specialkey", this, e);
13874                     
13875                     this.onTickableFooterButtonClick(e, false, false);
13876                     
13877                     return true;
13878                 },
13879
13880                 scope : this,
13881
13882                 doRelay : function(e, fn, key){
13883                     if(this.scope.isExpanded()){
13884                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13885                     }
13886                     return true;
13887                 },
13888
13889                 forceKeyDown: true
13890             });
13891         }
13892         
13893         this.queryDelay = Math.max(this.queryDelay || 10,
13894                 this.mode == 'local' ? 10 : 250);
13895         
13896         
13897         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13898         
13899         if(this.typeAhead){
13900             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13901         }
13902         
13903         if(this.editable !== false){
13904             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13905         }
13906         
13907         this.indicator = this.indicatorEl();
13908         
13909         if(this.indicator){
13910             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13911             this.indicator.hide();
13912         }
13913         
13914     },
13915
13916     onDestroy : function(){
13917         if(this.view){
13918             this.view.setStore(null);
13919             this.view.el.removeAllListeners();
13920             this.view.el.remove();
13921             this.view.purgeListeners();
13922         }
13923         if(this.list){
13924             this.list.dom.innerHTML  = '';
13925         }
13926         
13927         if(this.store){
13928             this.store.un('beforeload', this.onBeforeLoad, this);
13929             this.store.un('load', this.onLoad, this);
13930             this.store.un('loadexception', this.onLoadException, this);
13931         }
13932         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13933     },
13934
13935     // private
13936     fireKey : function(e){
13937         if(e.isNavKeyPress() && !this.list.isVisible()){
13938             this.fireEvent("specialkey", this, e);
13939         }
13940     },
13941
13942     // private
13943     onResize: function(w, h){
13944 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13945 //        
13946 //        if(typeof w != 'number'){
13947 //            // we do not handle it!?!?
13948 //            return;
13949 //        }
13950 //        var tw = this.trigger.getWidth();
13951 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13952 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13953 //        var x = w - tw;
13954 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13955 //            
13956 //        //this.trigger.setStyle('left', x+'px');
13957 //        
13958 //        if(this.list && this.listWidth === undefined){
13959 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13960 //            this.list.setWidth(lw);
13961 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13962 //        }
13963         
13964     
13965         
13966     },
13967
13968     /**
13969      * Allow or prevent the user from directly editing the field text.  If false is passed,
13970      * the user will only be able to select from the items defined in the dropdown list.  This method
13971      * is the runtime equivalent of setting the 'editable' config option at config time.
13972      * @param {Boolean} value True to allow the user to directly edit the field text
13973      */
13974     setEditable : function(value){
13975         if(value == this.editable){
13976             return;
13977         }
13978         this.editable = value;
13979         if(!value){
13980             this.inputEl().dom.setAttribute('readOnly', true);
13981             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13982             this.inputEl().addClass('x-combo-noedit');
13983         }else{
13984             this.inputEl().dom.setAttribute('readOnly', false);
13985             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13986             this.inputEl().removeClass('x-combo-noedit');
13987         }
13988     },
13989
13990     // private
13991     
13992     onBeforeLoad : function(combo,opts){
13993         if(!this.hasFocus){
13994             return;
13995         }
13996          if (!opts.add) {
13997             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13998          }
13999         this.restrictHeight();
14000         this.selectedIndex = -1;
14001     },
14002
14003     // private
14004     onLoad : function(){
14005         
14006         this.hasQuery = false;
14007         
14008         if(!this.hasFocus){
14009             return;
14010         }
14011         
14012         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14013             this.loading.hide();
14014         }
14015         
14016         if(this.store.getCount() > 0){
14017             
14018             this.expand();
14019             this.restrictHeight();
14020             if(this.lastQuery == this.allQuery){
14021                 if(this.editable && !this.tickable){
14022                     this.inputEl().dom.select();
14023                 }
14024                 
14025                 if(
14026                     !this.selectByValue(this.value, true) &&
14027                     this.autoFocus && 
14028                     (
14029                         !this.store.lastOptions ||
14030                         typeof(this.store.lastOptions.add) == 'undefined' || 
14031                         this.store.lastOptions.add != true
14032                     )
14033                 ){
14034                     this.select(0, true);
14035                 }
14036             }else{
14037                 if(this.autoFocus){
14038                     this.selectNext();
14039                 }
14040                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14041                     this.taTask.delay(this.typeAheadDelay);
14042                 }
14043             }
14044         }else{
14045             this.onEmptyResults();
14046         }
14047         
14048         //this.el.focus();
14049     },
14050     // private
14051     onLoadException : function()
14052     {
14053         this.hasQuery = false;
14054         
14055         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14056             this.loading.hide();
14057         }
14058         
14059         if(this.tickable && this.editable){
14060             return;
14061         }
14062         
14063         this.collapse();
14064         // only causes errors at present
14065         //Roo.log(this.store.reader.jsonData);
14066         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14067             // fixme
14068             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14069         //}
14070         
14071         
14072     },
14073     // private
14074     onTypeAhead : function(){
14075         if(this.store.getCount() > 0){
14076             var r = this.store.getAt(0);
14077             var newValue = r.data[this.displayField];
14078             var len = newValue.length;
14079             var selStart = this.getRawValue().length;
14080             
14081             if(selStart != len){
14082                 this.setRawValue(newValue);
14083                 this.selectText(selStart, newValue.length);
14084             }
14085         }
14086     },
14087
14088     // private
14089     onSelect : function(record, index){
14090         
14091         if(this.fireEvent('beforeselect', this, record, index) !== false){
14092         
14093             this.setFromData(index > -1 ? record.data : false);
14094             
14095             this.collapse();
14096             this.fireEvent('select', this, record, index);
14097         }
14098     },
14099
14100     /**
14101      * Returns the currently selected field value or empty string if no value is set.
14102      * @return {String} value The selected value
14103      */
14104     getValue : function()
14105     {
14106         if(Roo.isIOS && this.useNativeIOS){
14107             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14108         }
14109         
14110         if(this.multiple){
14111             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14112         }
14113         
14114         if(this.valueField){
14115             return typeof this.value != 'undefined' ? this.value : '';
14116         }else{
14117             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14118         }
14119     },
14120     
14121     getRawValue : function()
14122     {
14123         if(Roo.isIOS && this.useNativeIOS){
14124             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14125         }
14126         
14127         var v = this.inputEl().getValue();
14128         
14129         return v;
14130     },
14131
14132     /**
14133      * Clears any text/value currently set in the field
14134      */
14135     clearValue : function(){
14136         
14137         if(this.hiddenField){
14138             this.hiddenField.dom.value = '';
14139         }
14140         this.value = '';
14141         this.setRawValue('');
14142         this.lastSelectionText = '';
14143         this.lastData = false;
14144         
14145         var close = this.closeTriggerEl();
14146         
14147         if(close){
14148             close.hide();
14149         }
14150         
14151         this.validate();
14152         
14153     },
14154
14155     /**
14156      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14157      * will be displayed in the field.  If the value does not match the data value of an existing item,
14158      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14159      * Otherwise the field will be blank (although the value will still be set).
14160      * @param {String} value The value to match
14161      */
14162     setValue : function(v)
14163     {
14164         if(Roo.isIOS && this.useNativeIOS){
14165             this.setIOSValue(v);
14166             return;
14167         }
14168         
14169         if(this.multiple){
14170             this.syncValue();
14171             return;
14172         }
14173         
14174         var text = v;
14175         if(this.valueField){
14176             var r = this.findRecord(this.valueField, v);
14177             if(r){
14178                 text = r.data[this.displayField];
14179             }else if(this.valueNotFoundText !== undefined){
14180                 text = this.valueNotFoundText;
14181             }
14182         }
14183         this.lastSelectionText = text;
14184         if(this.hiddenField){
14185             this.hiddenField.dom.value = v;
14186         }
14187         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14188         this.value = v;
14189         
14190         var close = this.closeTriggerEl();
14191         
14192         if(close){
14193             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14194         }
14195         
14196         this.validate();
14197     },
14198     /**
14199      * @property {Object} the last set data for the element
14200      */
14201     
14202     lastData : false,
14203     /**
14204      * Sets the value of the field based on a object which is related to the record format for the store.
14205      * @param {Object} value the value to set as. or false on reset?
14206      */
14207     setFromData : function(o){
14208         
14209         if(this.multiple){
14210             this.addItem(o);
14211             return;
14212         }
14213             
14214         var dv = ''; // display value
14215         var vv = ''; // value value..
14216         this.lastData = o;
14217         if (this.displayField) {
14218             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14219         } else {
14220             // this is an error condition!!!
14221             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14222         }
14223         
14224         if(this.valueField){
14225             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14226         }
14227         
14228         var close = this.closeTriggerEl();
14229         
14230         if(close){
14231             if(dv.length || vv * 1 > 0){
14232                 close.show() ;
14233                 this.blockFocus=true;
14234             } else {
14235                 close.hide();
14236             }             
14237         }
14238         
14239         if(this.hiddenField){
14240             this.hiddenField.dom.value = vv;
14241             
14242             this.lastSelectionText = dv;
14243             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14244             this.value = vv;
14245             return;
14246         }
14247         // no hidden field.. - we store the value in 'value', but still display
14248         // display field!!!!
14249         this.lastSelectionText = dv;
14250         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14251         this.value = vv;
14252         
14253         
14254         
14255     },
14256     // private
14257     reset : function(){
14258         // overridden so that last data is reset..
14259         
14260         if(this.multiple){
14261             this.clearItem();
14262             return;
14263         }
14264         
14265         this.setValue(this.originalValue);
14266         //this.clearInvalid();
14267         this.lastData = false;
14268         if (this.view) {
14269             this.view.clearSelections();
14270         }
14271         
14272         this.validate();
14273     },
14274     // private
14275     findRecord : function(prop, value){
14276         var record;
14277         if(this.store.getCount() > 0){
14278             this.store.each(function(r){
14279                 if(r.data[prop] == value){
14280                     record = r;
14281                     return false;
14282                 }
14283                 return true;
14284             });
14285         }
14286         return record;
14287     },
14288     
14289     getName: function()
14290     {
14291         // returns hidden if it's set..
14292         if (!this.rendered) {return ''};
14293         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14294         
14295     },
14296     // private
14297     onViewMove : function(e, t){
14298         this.inKeyMode = false;
14299     },
14300
14301     // private
14302     onViewOver : function(e, t){
14303         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14304             return;
14305         }
14306         var item = this.view.findItemFromChild(t);
14307         
14308         if(item){
14309             var index = this.view.indexOf(item);
14310             this.select(index, false);
14311         }
14312     },
14313
14314     // private
14315     onViewClick : function(view, doFocus, el, e)
14316     {
14317         var index = this.view.getSelectedIndexes()[0];
14318         
14319         var r = this.store.getAt(index);
14320         
14321         if(this.tickable){
14322             
14323             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14324                 return;
14325             }
14326             
14327             var rm = false;
14328             var _this = this;
14329             
14330             Roo.each(this.tickItems, function(v,k){
14331                 
14332                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14333                     Roo.log(v);
14334                     _this.tickItems.splice(k, 1);
14335                     
14336                     if(typeof(e) == 'undefined' && view == false){
14337                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14338                     }
14339                     
14340                     rm = true;
14341                     return;
14342                 }
14343             });
14344             
14345             if(rm){
14346                 return;
14347             }
14348             
14349             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14350                 this.tickItems.push(r.data);
14351             }
14352             
14353             if(typeof(e) == 'undefined' && view == false){
14354                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14355             }
14356                     
14357             return;
14358         }
14359         
14360         if(r){
14361             this.onSelect(r, index);
14362         }
14363         if(doFocus !== false && !this.blockFocus){
14364             this.inputEl().focus();
14365         }
14366     },
14367
14368     // private
14369     restrictHeight : function(){
14370         //this.innerList.dom.style.height = '';
14371         //var inner = this.innerList.dom;
14372         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14373         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14374         //this.list.beginUpdate();
14375         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14376         this.list.alignTo(this.inputEl(), this.listAlign);
14377         this.list.alignTo(this.inputEl(), this.listAlign);
14378         //this.list.endUpdate();
14379     },
14380
14381     // private
14382     onEmptyResults : function(){
14383         
14384         if(this.tickable && this.editable){
14385             this.hasFocus = false;
14386             this.restrictHeight();
14387             return;
14388         }
14389         
14390         this.collapse();
14391     },
14392
14393     /**
14394      * Returns true if the dropdown list is expanded, else false.
14395      */
14396     isExpanded : function(){
14397         return this.list.isVisible();
14398     },
14399
14400     /**
14401      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14402      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14403      * @param {String} value The data value of the item to select
14404      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14405      * selected item if it is not currently in view (defaults to true)
14406      * @return {Boolean} True if the value matched an item in the list, else false
14407      */
14408     selectByValue : function(v, scrollIntoView){
14409         if(v !== undefined && v !== null){
14410             var r = this.findRecord(this.valueField || this.displayField, v);
14411             if(r){
14412                 this.select(this.store.indexOf(r), scrollIntoView);
14413                 return true;
14414             }
14415         }
14416         return false;
14417     },
14418
14419     /**
14420      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14421      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14422      * @param {Number} index The zero-based index of the list item to select
14423      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14424      * selected item if it is not currently in view (defaults to true)
14425      */
14426     select : function(index, scrollIntoView){
14427         this.selectedIndex = index;
14428         this.view.select(index);
14429         if(scrollIntoView !== false){
14430             var el = this.view.getNode(index);
14431             /*
14432              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14433              */
14434             if(el){
14435                 this.list.scrollChildIntoView(el, false);
14436             }
14437         }
14438     },
14439
14440     // private
14441     selectNext : function(){
14442         var ct = this.store.getCount();
14443         if(ct > 0){
14444             if(this.selectedIndex == -1){
14445                 this.select(0);
14446             }else if(this.selectedIndex < ct-1){
14447                 this.select(this.selectedIndex+1);
14448             }
14449         }
14450     },
14451
14452     // private
14453     selectPrev : function(){
14454         var ct = this.store.getCount();
14455         if(ct > 0){
14456             if(this.selectedIndex == -1){
14457                 this.select(0);
14458             }else if(this.selectedIndex != 0){
14459                 this.select(this.selectedIndex-1);
14460             }
14461         }
14462     },
14463
14464     // private
14465     onKeyUp : function(e){
14466         if(this.editable !== false && !e.isSpecialKey()){
14467             this.lastKey = e.getKey();
14468             this.dqTask.delay(this.queryDelay);
14469         }
14470     },
14471
14472     // private
14473     validateBlur : function(){
14474         return !this.list || !this.list.isVisible();   
14475     },
14476
14477     // private
14478     initQuery : function(){
14479         
14480         var v = this.getRawValue();
14481         
14482         if(this.tickable && this.editable){
14483             v = this.tickableInputEl().getValue();
14484         }
14485         
14486         this.doQuery(v);
14487     },
14488
14489     // private
14490     doForce : function(){
14491         if(this.inputEl().dom.value.length > 0){
14492             this.inputEl().dom.value =
14493                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14494              
14495         }
14496     },
14497
14498     /**
14499      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14500      * query allowing the query action to be canceled if needed.
14501      * @param {String} query The SQL query to execute
14502      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14503      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14504      * saved in the current store (defaults to false)
14505      */
14506     doQuery : function(q, forceAll){
14507         
14508         if(q === undefined || q === null){
14509             q = '';
14510         }
14511         var qe = {
14512             query: q,
14513             forceAll: forceAll,
14514             combo: this,
14515             cancel:false
14516         };
14517         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14518             return false;
14519         }
14520         q = qe.query;
14521         
14522         forceAll = qe.forceAll;
14523         if(forceAll === true || (q.length >= this.minChars)){
14524             
14525             this.hasQuery = true;
14526             
14527             if(this.lastQuery != q || this.alwaysQuery){
14528                 this.lastQuery = q;
14529                 if(this.mode == 'local'){
14530                     this.selectedIndex = -1;
14531                     if(forceAll){
14532                         this.store.clearFilter();
14533                     }else{
14534                         
14535                         if(this.specialFilter){
14536                             this.fireEvent('specialfilter', this);
14537                             this.onLoad();
14538                             return;
14539                         }
14540                         
14541                         this.store.filter(this.displayField, q);
14542                     }
14543                     
14544                     this.store.fireEvent("datachanged", this.store);
14545                     
14546                     this.onLoad();
14547                     
14548                     
14549                 }else{
14550                     
14551                     this.store.baseParams[this.queryParam] = q;
14552                     
14553                     var options = {params : this.getParams(q)};
14554                     
14555                     if(this.loadNext){
14556                         options.add = true;
14557                         options.params.start = this.page * this.pageSize;
14558                     }
14559                     
14560                     this.store.load(options);
14561                     
14562                     /*
14563                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14564                      *  we should expand the list on onLoad
14565                      *  so command out it
14566                      */
14567 //                    this.expand();
14568                 }
14569             }else{
14570                 this.selectedIndex = -1;
14571                 this.onLoad();   
14572             }
14573         }
14574         
14575         this.loadNext = false;
14576     },
14577     
14578     // private
14579     getParams : function(q){
14580         var p = {};
14581         //p[this.queryParam] = q;
14582         
14583         if(this.pageSize){
14584             p.start = 0;
14585             p.limit = this.pageSize;
14586         }
14587         return p;
14588     },
14589
14590     /**
14591      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14592      */
14593     collapse : function(){
14594         if(!this.isExpanded()){
14595             return;
14596         }
14597         
14598         this.list.hide();
14599         
14600         this.hasFocus = false;
14601         
14602         if(this.tickable){
14603             this.okBtn.hide();
14604             this.cancelBtn.hide();
14605             this.trigger.show();
14606             
14607             if(this.editable){
14608                 this.tickableInputEl().dom.value = '';
14609                 this.tickableInputEl().blur();
14610             }
14611             
14612         }
14613         
14614         Roo.get(document).un('mousedown', this.collapseIf, this);
14615         Roo.get(document).un('mousewheel', this.collapseIf, this);
14616         if (!this.editable) {
14617             Roo.get(document).un('keydown', this.listKeyPress, this);
14618         }
14619         this.fireEvent('collapse', this);
14620         
14621         this.validate();
14622     },
14623
14624     // private
14625     collapseIf : function(e){
14626         var in_combo  = e.within(this.el);
14627         var in_list =  e.within(this.list);
14628         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14629         
14630         if (in_combo || in_list || is_list) {
14631             //e.stopPropagation();
14632             return;
14633         }
14634         
14635         if(this.tickable){
14636             this.onTickableFooterButtonClick(e, false, false);
14637         }
14638
14639         this.collapse();
14640         
14641     },
14642
14643     /**
14644      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14645      */
14646     expand : function(){
14647        
14648         if(this.isExpanded() || !this.hasFocus){
14649             return;
14650         }
14651         
14652         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14653         this.list.setWidth(lw);
14654         
14655         Roo.log('expand');
14656         
14657         this.list.show();
14658         
14659         this.restrictHeight();
14660         
14661         if(this.tickable){
14662             
14663             this.tickItems = Roo.apply([], this.item);
14664             
14665             this.okBtn.show();
14666             this.cancelBtn.show();
14667             this.trigger.hide();
14668             
14669             if(this.editable){
14670                 this.tickableInputEl().focus();
14671             }
14672             
14673         }
14674         
14675         Roo.get(document).on('mousedown', this.collapseIf, this);
14676         Roo.get(document).on('mousewheel', this.collapseIf, this);
14677         if (!this.editable) {
14678             Roo.get(document).on('keydown', this.listKeyPress, this);
14679         }
14680         
14681         this.fireEvent('expand', this);
14682     },
14683
14684     // private
14685     // Implements the default empty TriggerField.onTriggerClick function
14686     onTriggerClick : function(e)
14687     {
14688         Roo.log('trigger click');
14689         
14690         if(this.disabled || !this.triggerList){
14691             return;
14692         }
14693         
14694         this.page = 0;
14695         this.loadNext = false;
14696         
14697         if(this.isExpanded()){
14698             this.collapse();
14699             if (!this.blockFocus) {
14700                 this.inputEl().focus();
14701             }
14702             
14703         }else {
14704             this.hasFocus = true;
14705             if(this.triggerAction == 'all') {
14706                 this.doQuery(this.allQuery, true);
14707             } else {
14708                 this.doQuery(this.getRawValue());
14709             }
14710             if (!this.blockFocus) {
14711                 this.inputEl().focus();
14712             }
14713         }
14714     },
14715     
14716     onTickableTriggerClick : function(e)
14717     {
14718         if(this.disabled){
14719             return;
14720         }
14721         
14722         this.page = 0;
14723         this.loadNext = false;
14724         this.hasFocus = true;
14725         
14726         if(this.triggerAction == 'all') {
14727             this.doQuery(this.allQuery, true);
14728         } else {
14729             this.doQuery(this.getRawValue());
14730         }
14731     },
14732     
14733     onSearchFieldClick : function(e)
14734     {
14735         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14736             this.onTickableFooterButtonClick(e, false, false);
14737             return;
14738         }
14739         
14740         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
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     listKeyPress : function(e)
14756     {
14757         //Roo.log('listkeypress');
14758         // scroll to first matching element based on key pres..
14759         if (e.isSpecialKey()) {
14760             return false;
14761         }
14762         var k = String.fromCharCode(e.getKey()).toUpperCase();
14763         //Roo.log(k);
14764         var match  = false;
14765         var csel = this.view.getSelectedNodes();
14766         var cselitem = false;
14767         if (csel.length) {
14768             var ix = this.view.indexOf(csel[0]);
14769             cselitem  = this.store.getAt(ix);
14770             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14771                 cselitem = false;
14772             }
14773             
14774         }
14775         
14776         this.store.each(function(v) { 
14777             if (cselitem) {
14778                 // start at existing selection.
14779                 if (cselitem.id == v.id) {
14780                     cselitem = false;
14781                 }
14782                 return true;
14783             }
14784                 
14785             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14786                 match = this.store.indexOf(v);
14787                 return false;
14788             }
14789             return true;
14790         }, this);
14791         
14792         if (match === false) {
14793             return true; // no more action?
14794         }
14795         // scroll to?
14796         this.view.select(match);
14797         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14798         sn.scrollIntoView(sn.dom.parentNode, false);
14799     },
14800     
14801     onViewScroll : function(e, t){
14802         
14803         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){
14804             return;
14805         }
14806         
14807         this.hasQuery = true;
14808         
14809         this.loading = this.list.select('.loading', true).first();
14810         
14811         if(this.loading === null){
14812             this.list.createChild({
14813                 tag: 'div',
14814                 cls: 'loading roo-select2-more-results roo-select2-active',
14815                 html: 'Loading more results...'
14816             });
14817             
14818             this.loading = this.list.select('.loading', true).first();
14819             
14820             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14821             
14822             this.loading.hide();
14823         }
14824         
14825         this.loading.show();
14826         
14827         var _combo = this;
14828         
14829         this.page++;
14830         this.loadNext = true;
14831         
14832         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14833         
14834         return;
14835     },
14836     
14837     addItem : function(o)
14838     {   
14839         var dv = ''; // display value
14840         
14841         if (this.displayField) {
14842             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14843         } else {
14844             // this is an error condition!!!
14845             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14846         }
14847         
14848         if(!dv.length){
14849             return;
14850         }
14851         
14852         var choice = this.choices.createChild({
14853             tag: 'li',
14854             cls: 'roo-select2-search-choice',
14855             cn: [
14856                 {
14857                     tag: 'div',
14858                     html: dv
14859                 },
14860                 {
14861                     tag: 'a',
14862                     href: '#',
14863                     cls: 'roo-select2-search-choice-close fa fa-times',
14864                     tabindex: '-1'
14865                 }
14866             ]
14867             
14868         }, this.searchField);
14869         
14870         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14871         
14872         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14873         
14874         this.item.push(o);
14875         
14876         this.lastData = o;
14877         
14878         this.syncValue();
14879         
14880         this.inputEl().dom.value = '';
14881         
14882         this.validate();
14883     },
14884     
14885     onRemoveItem : function(e, _self, o)
14886     {
14887         e.preventDefault();
14888         
14889         this.lastItem = Roo.apply([], this.item);
14890         
14891         var index = this.item.indexOf(o.data) * 1;
14892         
14893         if( index < 0){
14894             Roo.log('not this item?!');
14895             return;
14896         }
14897         
14898         this.item.splice(index, 1);
14899         o.item.remove();
14900         
14901         this.syncValue();
14902         
14903         this.fireEvent('remove', this, e);
14904         
14905         this.validate();
14906         
14907     },
14908     
14909     syncValue : function()
14910     {
14911         if(!this.item.length){
14912             this.clearValue();
14913             return;
14914         }
14915             
14916         var value = [];
14917         var _this = this;
14918         Roo.each(this.item, function(i){
14919             if(_this.valueField){
14920                 value.push(i[_this.valueField]);
14921                 return;
14922             }
14923
14924             value.push(i);
14925         });
14926
14927         this.value = value.join(',');
14928
14929         if(this.hiddenField){
14930             this.hiddenField.dom.value = this.value;
14931         }
14932         
14933         this.store.fireEvent("datachanged", this.store);
14934         
14935         this.validate();
14936     },
14937     
14938     clearItem : function()
14939     {
14940         if(!this.multiple){
14941             return;
14942         }
14943         
14944         this.item = [];
14945         
14946         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14947            c.remove();
14948         });
14949         
14950         this.syncValue();
14951         
14952         this.validate();
14953         
14954         if(this.tickable && !Roo.isTouch){
14955             this.view.refresh();
14956         }
14957     },
14958     
14959     inputEl: function ()
14960     {
14961         if(Roo.isIOS && this.useNativeIOS){
14962             return this.el.select('select.roo-ios-select', true).first();
14963         }
14964         
14965         if(Roo.isTouch && this.mobileTouchView){
14966             return this.el.select('input.form-control',true).first();
14967         }
14968         
14969         if(this.tickable){
14970             return this.searchField;
14971         }
14972         
14973         return this.el.select('input.form-control',true).first();
14974     },
14975     
14976     onTickableFooterButtonClick : function(e, btn, el)
14977     {
14978         e.preventDefault();
14979         
14980         this.lastItem = Roo.apply([], this.item);
14981         
14982         if(btn && btn.name == 'cancel'){
14983             this.tickItems = Roo.apply([], this.item);
14984             this.collapse();
14985             return;
14986         }
14987         
14988         this.clearItem();
14989         
14990         var _this = this;
14991         
14992         Roo.each(this.tickItems, function(o){
14993             _this.addItem(o);
14994         });
14995         
14996         this.collapse();
14997         
14998     },
14999     
15000     validate : function()
15001     {
15002         if(this.getVisibilityEl().hasClass('hidden')){
15003             return true;
15004         }
15005         
15006         var v = this.getRawValue();
15007         
15008         if(this.multiple){
15009             v = this.getValue();
15010         }
15011         
15012         if(this.disabled || this.allowBlank || v.length){
15013             this.markValid();
15014             return true;
15015         }
15016         
15017         this.markInvalid();
15018         return false;
15019     },
15020     
15021     tickableInputEl : function()
15022     {
15023         if(!this.tickable || !this.editable){
15024             return this.inputEl();
15025         }
15026         
15027         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15028     },
15029     
15030     
15031     getAutoCreateTouchView : function()
15032     {
15033         var id = Roo.id();
15034         
15035         var cfg = {
15036             cls: 'form-group' //input-group
15037         };
15038         
15039         var input =  {
15040             tag: 'input',
15041             id : id,
15042             type : this.inputType,
15043             cls : 'form-control x-combo-noedit',
15044             autocomplete: 'new-password',
15045             placeholder : this.placeholder || '',
15046             readonly : true
15047         };
15048         
15049         if (this.name) {
15050             input.name = this.name;
15051         }
15052         
15053         if (this.size) {
15054             input.cls += ' input-' + this.size;
15055         }
15056         
15057         if (this.disabled) {
15058             input.disabled = true;
15059         }
15060         
15061         var inputblock = {
15062             cls : '',
15063             cn : [
15064                 input
15065             ]
15066         };
15067         
15068         if(this.before){
15069             inputblock.cls += ' input-group';
15070             
15071             inputblock.cn.unshift({
15072                 tag :'span',
15073                 cls : 'input-group-addon input-group-prepend input-group-text',
15074                 html : this.before
15075             });
15076         }
15077         
15078         if(this.removable && !this.multiple){
15079             inputblock.cls += ' roo-removable';
15080             
15081             inputblock.cn.push({
15082                 tag: 'button',
15083                 html : 'x',
15084                 cls : 'roo-combo-removable-btn close'
15085             });
15086         }
15087
15088         if(this.hasFeedback && !this.allowBlank){
15089             
15090             inputblock.cls += ' has-feedback';
15091             
15092             inputblock.cn.push({
15093                 tag: 'span',
15094                 cls: 'glyphicon form-control-feedback'
15095             });
15096             
15097         }
15098         
15099         if (this.after) {
15100             
15101             inputblock.cls += (this.before) ? '' : ' input-group';
15102             
15103             inputblock.cn.push({
15104                 tag :'span',
15105                 cls : 'input-group-addon input-group-append input-group-text',
15106                 html : this.after
15107             });
15108         }
15109
15110         
15111         var ibwrap = inputblock;
15112         
15113         if(this.multiple){
15114             ibwrap = {
15115                 tag: 'ul',
15116                 cls: 'roo-select2-choices',
15117                 cn:[
15118                     {
15119                         tag: 'li',
15120                         cls: 'roo-select2-search-field',
15121                         cn: [
15122
15123                             inputblock
15124                         ]
15125                     }
15126                 ]
15127             };
15128         
15129             
15130         }
15131         
15132         var combobox = {
15133             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15134             cn: [
15135                 {
15136                     tag: 'input',
15137                     type : 'hidden',
15138                     cls: 'form-hidden-field'
15139                 },
15140                 ibwrap
15141             ]
15142         };
15143         
15144         if(!this.multiple && this.showToggleBtn){
15145             
15146             var caret = {
15147                         tag: 'span',
15148                         cls: 'caret'
15149             };
15150             
15151             if (this.caret != false) {
15152                 caret = {
15153                      tag: 'i',
15154                      cls: 'fa fa-' + this.caret
15155                 };
15156                 
15157             }
15158             
15159             combobox.cn.push({
15160                 tag :'span',
15161                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15162                 cn : [
15163                     caret,
15164                     {
15165                         tag: 'span',
15166                         cls: 'combobox-clear',
15167                         cn  : [
15168                             {
15169                                 tag : 'i',
15170                                 cls: 'icon-remove'
15171                             }
15172                         ]
15173                     }
15174                 ]
15175
15176             })
15177         }
15178         
15179         if(this.multiple){
15180             combobox.cls += ' roo-select2-container-multi';
15181         }
15182         
15183         var align = this.labelAlign || this.parentLabelAlign();
15184         
15185         if (align ==='left' && this.fieldLabel.length) {
15186
15187             cfg.cn = [
15188                 {
15189                    tag : 'i',
15190                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15191                    tooltip : 'This field is required'
15192                 },
15193                 {
15194                     tag: 'label',
15195                     cls : 'control-label col-form-label',
15196                     html : this.fieldLabel
15197
15198                 },
15199                 {
15200                     cls : '', 
15201                     cn: [
15202                         combobox
15203                     ]
15204                 }
15205             ];
15206             
15207             var labelCfg = cfg.cn[1];
15208             var contentCfg = cfg.cn[2];
15209             
15210
15211             if(this.indicatorpos == 'right'){
15212                 cfg.cn = [
15213                     {
15214                         tag: 'label',
15215                         'for' :  id,
15216                         cls : 'control-label col-form-label',
15217                         cn : [
15218                             {
15219                                 tag : 'span',
15220                                 html : this.fieldLabel
15221                             },
15222                             {
15223                                 tag : 'i',
15224                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15225                                 tooltip : 'This field is required'
15226                             }
15227                         ]
15228                     },
15229                     {
15230                         cls : "",
15231                         cn: [
15232                             combobox
15233                         ]
15234                     }
15235
15236                 ];
15237                 
15238                 labelCfg = cfg.cn[0];
15239                 contentCfg = cfg.cn[1];
15240             }
15241             
15242            
15243             
15244             if(this.labelWidth > 12){
15245                 labelCfg.style = "width: " + this.labelWidth + 'px';
15246             }
15247             
15248             if(this.labelWidth < 13 && this.labelmd == 0){
15249                 this.labelmd = this.labelWidth;
15250             }
15251             
15252             if(this.labellg > 0){
15253                 labelCfg.cls += ' col-lg-' + this.labellg;
15254                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15255             }
15256             
15257             if(this.labelmd > 0){
15258                 labelCfg.cls += ' col-md-' + this.labelmd;
15259                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15260             }
15261             
15262             if(this.labelsm > 0){
15263                 labelCfg.cls += ' col-sm-' + this.labelsm;
15264                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15265             }
15266             
15267             if(this.labelxs > 0){
15268                 labelCfg.cls += ' col-xs-' + this.labelxs;
15269                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15270             }
15271                 
15272                 
15273         } else if ( this.fieldLabel.length) {
15274             cfg.cn = [
15275                 {
15276                    tag : 'i',
15277                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15278                    tooltip : 'This field is required'
15279                 },
15280                 {
15281                     tag: 'label',
15282                     cls : 'control-label',
15283                     html : this.fieldLabel
15284
15285                 },
15286                 {
15287                     cls : '', 
15288                     cn: [
15289                         combobox
15290                     ]
15291                 }
15292             ];
15293             
15294             if(this.indicatorpos == 'right'){
15295                 cfg.cn = [
15296                     {
15297                         tag: 'label',
15298                         cls : 'control-label',
15299                         html : this.fieldLabel,
15300                         cn : [
15301                             {
15302                                tag : 'i',
15303                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15304                                tooltip : 'This field is required'
15305                             }
15306                         ]
15307                     },
15308                     {
15309                         cls : '', 
15310                         cn: [
15311                             combobox
15312                         ]
15313                     }
15314                 ];
15315             }
15316         } else {
15317             cfg.cn = combobox;    
15318         }
15319         
15320         
15321         var settings = this;
15322         
15323         ['xs','sm','md','lg'].map(function(size){
15324             if (settings[size]) {
15325                 cfg.cls += ' col-' + size + '-' + settings[size];
15326             }
15327         });
15328         
15329         return cfg;
15330     },
15331     
15332     initTouchView : function()
15333     {
15334         this.renderTouchView();
15335         
15336         this.touchViewEl.on('scroll', function(){
15337             this.el.dom.scrollTop = 0;
15338         }, this);
15339         
15340         this.originalValue = this.getValue();
15341         
15342         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15343         
15344         this.inputEl().on("click", this.showTouchView, this);
15345         if (this.triggerEl) {
15346             this.triggerEl.on("click", this.showTouchView, this);
15347         }
15348         
15349         
15350         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15351         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15352         
15353         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15354         
15355         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15356         this.store.on('load', this.onTouchViewLoad, this);
15357         this.store.on('loadexception', this.onTouchViewLoadException, this);
15358         
15359         if(this.hiddenName){
15360             
15361             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15362             
15363             this.hiddenField.dom.value =
15364                 this.hiddenValue !== undefined ? this.hiddenValue :
15365                 this.value !== undefined ? this.value : '';
15366         
15367             this.el.dom.removeAttribute('name');
15368             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15369         }
15370         
15371         if(this.multiple){
15372             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15373             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15374         }
15375         
15376         if(this.removable && !this.multiple){
15377             var close = this.closeTriggerEl();
15378             if(close){
15379                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15380                 close.on('click', this.removeBtnClick, this, close);
15381             }
15382         }
15383         /*
15384          * fix the bug in Safari iOS8
15385          */
15386         this.inputEl().on("focus", function(e){
15387             document.activeElement.blur();
15388         }, this);
15389         
15390         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15391         
15392         return;
15393         
15394         
15395     },
15396     
15397     renderTouchView : function()
15398     {
15399         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15400         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15401         
15402         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15403         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15404         
15405         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15406         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15407         this.touchViewBodyEl.setStyle('overflow', 'auto');
15408         
15409         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15410         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15411         
15412         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15413         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15414         
15415     },
15416     
15417     showTouchView : function()
15418     {
15419         if(this.disabled){
15420             return;
15421         }
15422         
15423         this.touchViewHeaderEl.hide();
15424
15425         if(this.modalTitle.length){
15426             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15427             this.touchViewHeaderEl.show();
15428         }
15429
15430         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15431         this.touchViewEl.show();
15432
15433         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15434         
15435         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15436         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15437
15438         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15439
15440         if(this.modalTitle.length){
15441             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15442         }
15443         
15444         this.touchViewBodyEl.setHeight(bodyHeight);
15445
15446         if(this.animate){
15447             var _this = this;
15448             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15449         }else{
15450             this.touchViewEl.addClass('in');
15451         }
15452         
15453         if(this._touchViewMask){
15454             Roo.get(document.body).addClass("x-body-masked");
15455             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15456             this._touchViewMask.setStyle('z-index', 10000);
15457             this._touchViewMask.addClass('show');
15458         }
15459         
15460         this.doTouchViewQuery();
15461         
15462     },
15463     
15464     hideTouchView : function()
15465     {
15466         this.touchViewEl.removeClass('in');
15467
15468         if(this.animate){
15469             var _this = this;
15470             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15471         }else{
15472             this.touchViewEl.setStyle('display', 'none');
15473         }
15474         
15475         if(this._touchViewMask){
15476             this._touchViewMask.removeClass('show');
15477             Roo.get(document.body).removeClass("x-body-masked");
15478         }
15479     },
15480     
15481     setTouchViewValue : function()
15482     {
15483         if(this.multiple){
15484             this.clearItem();
15485         
15486             var _this = this;
15487
15488             Roo.each(this.tickItems, function(o){
15489                 this.addItem(o);
15490             }, this);
15491         }
15492         
15493         this.hideTouchView();
15494     },
15495     
15496     doTouchViewQuery : function()
15497     {
15498         var qe = {
15499             query: '',
15500             forceAll: true,
15501             combo: this,
15502             cancel:false
15503         };
15504         
15505         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15506             return false;
15507         }
15508         
15509         if(!this.alwaysQuery || this.mode == 'local'){
15510             this.onTouchViewLoad();
15511             return;
15512         }
15513         
15514         this.store.load();
15515     },
15516     
15517     onTouchViewBeforeLoad : function(combo,opts)
15518     {
15519         return;
15520     },
15521
15522     // private
15523     onTouchViewLoad : function()
15524     {
15525         if(this.store.getCount() < 1){
15526             this.onTouchViewEmptyResults();
15527             return;
15528         }
15529         
15530         this.clearTouchView();
15531         
15532         var rawValue = this.getRawValue();
15533         
15534         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15535         
15536         this.tickItems = [];
15537         
15538         this.store.data.each(function(d, rowIndex){
15539             var row = this.touchViewListGroup.createChild(template);
15540             
15541             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15542                 row.addClass(d.data.cls);
15543             }
15544             
15545             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15546                 var cfg = {
15547                     data : d.data,
15548                     html : d.data[this.displayField]
15549                 };
15550                 
15551                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15552                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15553                 }
15554             }
15555             row.removeClass('selected');
15556             if(!this.multiple && this.valueField &&
15557                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15558             {
15559                 // radio buttons..
15560                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15561                 row.addClass('selected');
15562             }
15563             
15564             if(this.multiple && this.valueField &&
15565                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15566             {
15567                 
15568                 // checkboxes...
15569                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15570                 this.tickItems.push(d.data);
15571             }
15572             
15573             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15574             
15575         }, this);
15576         
15577         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15578         
15579         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15580
15581         if(this.modalTitle.length){
15582             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15583         }
15584
15585         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15586         
15587         if(this.mobile_restrict_height && listHeight < bodyHeight){
15588             this.touchViewBodyEl.setHeight(listHeight);
15589         }
15590         
15591         var _this = this;
15592         
15593         if(firstChecked && listHeight > bodyHeight){
15594             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15595         }
15596         
15597     },
15598     
15599     onTouchViewLoadException : function()
15600     {
15601         this.hideTouchView();
15602     },
15603     
15604     onTouchViewEmptyResults : function()
15605     {
15606         this.clearTouchView();
15607         
15608         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15609         
15610         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15611         
15612     },
15613     
15614     clearTouchView : function()
15615     {
15616         this.touchViewListGroup.dom.innerHTML = '';
15617     },
15618     
15619     onTouchViewClick : function(e, el, o)
15620     {
15621         e.preventDefault();
15622         
15623         var row = o.row;
15624         var rowIndex = o.rowIndex;
15625         
15626         var r = this.store.getAt(rowIndex);
15627         
15628         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15629             
15630             if(!this.multiple){
15631                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15632                     c.dom.removeAttribute('checked');
15633                 }, this);
15634
15635                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15636
15637                 this.setFromData(r.data);
15638
15639                 var close = this.closeTriggerEl();
15640
15641                 if(close){
15642                     close.show();
15643                 }
15644
15645                 this.hideTouchView();
15646
15647                 this.fireEvent('select', this, r, rowIndex);
15648
15649                 return;
15650             }
15651
15652             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15653                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15654                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15655                 return;
15656             }
15657
15658             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15659             this.addItem(r.data);
15660             this.tickItems.push(r.data);
15661         }
15662     },
15663     
15664     getAutoCreateNativeIOS : function()
15665     {
15666         var cfg = {
15667             cls: 'form-group' //input-group,
15668         };
15669         
15670         var combobox =  {
15671             tag: 'select',
15672             cls : 'roo-ios-select'
15673         };
15674         
15675         if (this.name) {
15676             combobox.name = this.name;
15677         }
15678         
15679         if (this.disabled) {
15680             combobox.disabled = true;
15681         }
15682         
15683         var settings = this;
15684         
15685         ['xs','sm','md','lg'].map(function(size){
15686             if (settings[size]) {
15687                 cfg.cls += ' col-' + size + '-' + settings[size];
15688             }
15689         });
15690         
15691         cfg.cn = combobox;
15692         
15693         return cfg;
15694         
15695     },
15696     
15697     initIOSView : function()
15698     {
15699         this.store.on('load', this.onIOSViewLoad, this);
15700         
15701         return;
15702     },
15703     
15704     onIOSViewLoad : function()
15705     {
15706         if(this.store.getCount() < 1){
15707             return;
15708         }
15709         
15710         this.clearIOSView();
15711         
15712         if(this.allowBlank) {
15713             
15714             var default_text = '-- SELECT --';
15715             
15716             if(this.placeholder.length){
15717                 default_text = this.placeholder;
15718             }
15719             
15720             if(this.emptyTitle.length){
15721                 default_text += ' - ' + this.emptyTitle + ' -';
15722             }
15723             
15724             var opt = this.inputEl().createChild({
15725                 tag: 'option',
15726                 value : 0,
15727                 html : default_text
15728             });
15729             
15730             var o = {};
15731             o[this.valueField] = 0;
15732             o[this.displayField] = default_text;
15733             
15734             this.ios_options.push({
15735                 data : o,
15736                 el : opt
15737             });
15738             
15739         }
15740         
15741         this.store.data.each(function(d, rowIndex){
15742             
15743             var html = '';
15744             
15745             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15746                 html = d.data[this.displayField];
15747             }
15748             
15749             var value = '';
15750             
15751             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15752                 value = d.data[this.valueField];
15753             }
15754             
15755             var option = {
15756                 tag: 'option',
15757                 value : value,
15758                 html : html
15759             };
15760             
15761             if(this.value == d.data[this.valueField]){
15762                 option['selected'] = true;
15763             }
15764             
15765             var opt = this.inputEl().createChild(option);
15766             
15767             this.ios_options.push({
15768                 data : d.data,
15769                 el : opt
15770             });
15771             
15772         }, this);
15773         
15774         this.inputEl().on('change', function(){
15775            this.fireEvent('select', this);
15776         }, this);
15777         
15778     },
15779     
15780     clearIOSView: function()
15781     {
15782         this.inputEl().dom.innerHTML = '';
15783         
15784         this.ios_options = [];
15785     },
15786     
15787     setIOSValue: function(v)
15788     {
15789         this.value = v;
15790         
15791         if(!this.ios_options){
15792             return;
15793         }
15794         
15795         Roo.each(this.ios_options, function(opts){
15796            
15797            opts.el.dom.removeAttribute('selected');
15798            
15799            if(opts.data[this.valueField] != v){
15800                return;
15801            }
15802            
15803            opts.el.dom.setAttribute('selected', true);
15804            
15805         }, this);
15806     }
15807
15808     /** 
15809     * @cfg {Boolean} grow 
15810     * @hide 
15811     */
15812     /** 
15813     * @cfg {Number} growMin 
15814     * @hide 
15815     */
15816     /** 
15817     * @cfg {Number} growMax 
15818     * @hide 
15819     */
15820     /**
15821      * @hide
15822      * @method autoSize
15823      */
15824 });
15825
15826 Roo.apply(Roo.bootstrap.ComboBox,  {
15827     
15828     header : {
15829         tag: 'div',
15830         cls: 'modal-header',
15831         cn: [
15832             {
15833                 tag: 'h4',
15834                 cls: 'modal-title'
15835             }
15836         ]
15837     },
15838     
15839     body : {
15840         tag: 'div',
15841         cls: 'modal-body',
15842         cn: [
15843             {
15844                 tag: 'ul',
15845                 cls: 'list-group'
15846             }
15847         ]
15848     },
15849     
15850     listItemRadio : {
15851         tag: 'li',
15852         cls: 'list-group-item',
15853         cn: [
15854             {
15855                 tag: 'span',
15856                 cls: 'roo-combobox-list-group-item-value'
15857             },
15858             {
15859                 tag: 'div',
15860                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15861                 cn: [
15862                     {
15863                         tag: 'input',
15864                         type: 'radio'
15865                     },
15866                     {
15867                         tag: 'label'
15868                     }
15869                 ]
15870             }
15871         ]
15872     },
15873     
15874     listItemCheckbox : {
15875         tag: 'li',
15876         cls: 'list-group-item',
15877         cn: [
15878             {
15879                 tag: 'span',
15880                 cls: 'roo-combobox-list-group-item-value'
15881             },
15882             {
15883                 tag: 'div',
15884                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15885                 cn: [
15886                     {
15887                         tag: 'input',
15888                         type: 'checkbox'
15889                     },
15890                     {
15891                         tag: 'label'
15892                     }
15893                 ]
15894             }
15895         ]
15896     },
15897     
15898     emptyResult : {
15899         tag: 'div',
15900         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15901     },
15902     
15903     footer : {
15904         tag: 'div',
15905         cls: 'modal-footer',
15906         cn: [
15907             {
15908                 tag: 'div',
15909                 cls: 'row',
15910                 cn: [
15911                     {
15912                         tag: 'div',
15913                         cls: 'col-xs-6 text-left',
15914                         cn: {
15915                             tag: 'button',
15916                             cls: 'btn btn-danger roo-touch-view-cancel',
15917                             html: 'Cancel'
15918                         }
15919                     },
15920                     {
15921                         tag: 'div',
15922                         cls: 'col-xs-6 text-right',
15923                         cn: {
15924                             tag: 'button',
15925                             cls: 'btn btn-success roo-touch-view-ok',
15926                             html: 'OK'
15927                         }
15928                     }
15929                 ]
15930             }
15931         ]
15932         
15933     }
15934 });
15935
15936 Roo.apply(Roo.bootstrap.ComboBox,  {
15937     
15938     touchViewTemplate : {
15939         tag: 'div',
15940         cls: 'modal fade roo-combobox-touch-view',
15941         cn: [
15942             {
15943                 tag: 'div',
15944                 cls: 'modal-dialog',
15945                 style : 'position:fixed', // we have to fix position....
15946                 cn: [
15947                     {
15948                         tag: 'div',
15949                         cls: 'modal-content',
15950                         cn: [
15951                             Roo.bootstrap.ComboBox.header,
15952                             Roo.bootstrap.ComboBox.body,
15953                             Roo.bootstrap.ComboBox.footer
15954                         ]
15955                     }
15956                 ]
15957             }
15958         ]
15959     }
15960 });/*
15961  * Based on:
15962  * Ext JS Library 1.1.1
15963  * Copyright(c) 2006-2007, Ext JS, LLC.
15964  *
15965  * Originally Released Under LGPL - original licence link has changed is not relivant.
15966  *
15967  * Fork - LGPL
15968  * <script type="text/javascript">
15969  */
15970
15971 /**
15972  * @class Roo.View
15973  * @extends Roo.util.Observable
15974  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15975  * This class also supports single and multi selection modes. <br>
15976  * Create a data model bound view:
15977  <pre><code>
15978  var store = new Roo.data.Store(...);
15979
15980  var view = new Roo.View({
15981     el : "my-element",
15982     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15983  
15984     singleSelect: true,
15985     selectedClass: "ydataview-selected",
15986     store: store
15987  });
15988
15989  // listen for node click?
15990  view.on("click", function(vw, index, node, e){
15991  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15992  });
15993
15994  // load XML data
15995  dataModel.load("foobar.xml");
15996  </code></pre>
15997  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15998  * <br><br>
15999  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16000  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16001  * 
16002  * Note: old style constructor is still suported (container, template, config)
16003  * 
16004  * @constructor
16005  * Create a new View
16006  * @param {Object} config The config object
16007  * 
16008  */
16009 Roo.View = function(config, depreciated_tpl, depreciated_config){
16010     
16011     this.parent = false;
16012     
16013     if (typeof(depreciated_tpl) == 'undefined') {
16014         // new way.. - universal constructor.
16015         Roo.apply(this, config);
16016         this.el  = Roo.get(this.el);
16017     } else {
16018         // old format..
16019         this.el  = Roo.get(config);
16020         this.tpl = depreciated_tpl;
16021         Roo.apply(this, depreciated_config);
16022     }
16023     this.wrapEl  = this.el.wrap().wrap();
16024     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16025     
16026     
16027     if(typeof(this.tpl) == "string"){
16028         this.tpl = new Roo.Template(this.tpl);
16029     } else {
16030         // support xtype ctors..
16031         this.tpl = new Roo.factory(this.tpl, Roo);
16032     }
16033     
16034     
16035     this.tpl.compile();
16036     
16037     /** @private */
16038     this.addEvents({
16039         /**
16040          * @event beforeclick
16041          * Fires before a click is processed. Returns false to cancel the default action.
16042          * @param {Roo.View} this
16043          * @param {Number} index The index of the target node
16044          * @param {HTMLElement} node The target node
16045          * @param {Roo.EventObject} e The raw event object
16046          */
16047             "beforeclick" : true,
16048         /**
16049          * @event click
16050          * Fires when a template node is clicked.
16051          * @param {Roo.View} this
16052          * @param {Number} index The index of the target node
16053          * @param {HTMLElement} node The target node
16054          * @param {Roo.EventObject} e The raw event object
16055          */
16056             "click" : true,
16057         /**
16058          * @event dblclick
16059          * Fires when a template node is double clicked.
16060          * @param {Roo.View} this
16061          * @param {Number} index The index of the target node
16062          * @param {HTMLElement} node The target node
16063          * @param {Roo.EventObject} e The raw event object
16064          */
16065             "dblclick" : true,
16066         /**
16067          * @event contextmenu
16068          * Fires when a template node is right clicked.
16069          * @param {Roo.View} this
16070          * @param {Number} index The index of the target node
16071          * @param {HTMLElement} node The target node
16072          * @param {Roo.EventObject} e The raw event object
16073          */
16074             "contextmenu" : true,
16075         /**
16076          * @event selectionchange
16077          * Fires when the selected nodes change.
16078          * @param {Roo.View} this
16079          * @param {Array} selections Array of the selected nodes
16080          */
16081             "selectionchange" : true,
16082     
16083         /**
16084          * @event beforeselect
16085          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16086          * @param {Roo.View} this
16087          * @param {HTMLElement} node The node to be selected
16088          * @param {Array} selections Array of currently selected nodes
16089          */
16090             "beforeselect" : true,
16091         /**
16092          * @event preparedata
16093          * Fires on every row to render, to allow you to change the data.
16094          * @param {Roo.View} this
16095          * @param {Object} data to be rendered (change this)
16096          */
16097           "preparedata" : true
16098           
16099           
16100         });
16101
16102
16103
16104     this.el.on({
16105         "click": this.onClick,
16106         "dblclick": this.onDblClick,
16107         "contextmenu": this.onContextMenu,
16108         scope:this
16109     });
16110
16111     this.selections = [];
16112     this.nodes = [];
16113     this.cmp = new Roo.CompositeElementLite([]);
16114     if(this.store){
16115         this.store = Roo.factory(this.store, Roo.data);
16116         this.setStore(this.store, true);
16117     }
16118     
16119     if ( this.footer && this.footer.xtype) {
16120            
16121          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16122         
16123         this.footer.dataSource = this.store;
16124         this.footer.container = fctr;
16125         this.footer = Roo.factory(this.footer, Roo);
16126         fctr.insertFirst(this.el);
16127         
16128         // this is a bit insane - as the paging toolbar seems to detach the el..
16129 //        dom.parentNode.parentNode.parentNode
16130          // they get detached?
16131     }
16132     
16133     
16134     Roo.View.superclass.constructor.call(this);
16135     
16136     
16137 };
16138
16139 Roo.extend(Roo.View, Roo.util.Observable, {
16140     
16141      /**
16142      * @cfg {Roo.data.Store} store Data store to load data from.
16143      */
16144     store : false,
16145     
16146     /**
16147      * @cfg {String|Roo.Element} el The container element.
16148      */
16149     el : '',
16150     
16151     /**
16152      * @cfg {String|Roo.Template} tpl The template used by this View 
16153      */
16154     tpl : false,
16155     /**
16156      * @cfg {String} dataName the named area of the template to use as the data area
16157      *                          Works with domtemplates roo-name="name"
16158      */
16159     dataName: false,
16160     /**
16161      * @cfg {String} selectedClass The css class to add to selected nodes
16162      */
16163     selectedClass : "x-view-selected",
16164      /**
16165      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16166      */
16167     emptyText : "",
16168     
16169     /**
16170      * @cfg {String} text to display on mask (default Loading)
16171      */
16172     mask : false,
16173     /**
16174      * @cfg {Boolean} multiSelect Allow multiple selection
16175      */
16176     multiSelect : false,
16177     /**
16178      * @cfg {Boolean} singleSelect Allow single selection
16179      */
16180     singleSelect:  false,
16181     
16182     /**
16183      * @cfg {Boolean} toggleSelect - selecting 
16184      */
16185     toggleSelect : false,
16186     
16187     /**
16188      * @cfg {Boolean} tickable - selecting 
16189      */
16190     tickable : false,
16191     
16192     /**
16193      * Returns the element this view is bound to.
16194      * @return {Roo.Element}
16195      */
16196     getEl : function(){
16197         return this.wrapEl;
16198     },
16199     
16200     
16201
16202     /**
16203      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16204      */
16205     refresh : function(){
16206         //Roo.log('refresh');
16207         var t = this.tpl;
16208         
16209         // if we are using something like 'domtemplate', then
16210         // the what gets used is:
16211         // t.applySubtemplate(NAME, data, wrapping data..)
16212         // the outer template then get' applied with
16213         //     the store 'extra data'
16214         // and the body get's added to the
16215         //      roo-name="data" node?
16216         //      <span class='roo-tpl-{name}'></span> ?????
16217         
16218         
16219         
16220         this.clearSelections();
16221         this.el.update("");
16222         var html = [];
16223         var records = this.store.getRange();
16224         if(records.length < 1) {
16225             
16226             // is this valid??  = should it render a template??
16227             
16228             this.el.update(this.emptyText);
16229             return;
16230         }
16231         var el = this.el;
16232         if (this.dataName) {
16233             this.el.update(t.apply(this.store.meta)); //????
16234             el = this.el.child('.roo-tpl-' + this.dataName);
16235         }
16236         
16237         for(var i = 0, len = records.length; i < len; i++){
16238             var data = this.prepareData(records[i].data, i, records[i]);
16239             this.fireEvent("preparedata", this, data, i, records[i]);
16240             
16241             var d = Roo.apply({}, data);
16242             
16243             if(this.tickable){
16244                 Roo.apply(d, {'roo-id' : Roo.id()});
16245                 
16246                 var _this = this;
16247             
16248                 Roo.each(this.parent.item, function(item){
16249                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16250                         return;
16251                     }
16252                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16253                 });
16254             }
16255             
16256             html[html.length] = Roo.util.Format.trim(
16257                 this.dataName ?
16258                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16259                     t.apply(d)
16260             );
16261         }
16262         
16263         
16264         
16265         el.update(html.join(""));
16266         this.nodes = el.dom.childNodes;
16267         this.updateIndexes(0);
16268     },
16269     
16270
16271     /**
16272      * Function to override to reformat the data that is sent to
16273      * the template for each node.
16274      * DEPRICATED - use the preparedata event handler.
16275      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16276      * a JSON object for an UpdateManager bound view).
16277      */
16278     prepareData : function(data, index, record)
16279     {
16280         this.fireEvent("preparedata", this, data, index, record);
16281         return data;
16282     },
16283
16284     onUpdate : function(ds, record){
16285         // Roo.log('on update');   
16286         this.clearSelections();
16287         var index = this.store.indexOf(record);
16288         var n = this.nodes[index];
16289         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16290         n.parentNode.removeChild(n);
16291         this.updateIndexes(index, index);
16292     },
16293
16294     
16295     
16296 // --------- FIXME     
16297     onAdd : function(ds, records, index)
16298     {
16299         //Roo.log(['on Add', ds, records, index] );        
16300         this.clearSelections();
16301         if(this.nodes.length == 0){
16302             this.refresh();
16303             return;
16304         }
16305         var n = this.nodes[index];
16306         for(var i = 0, len = records.length; i < len; i++){
16307             var d = this.prepareData(records[i].data, i, records[i]);
16308             if(n){
16309                 this.tpl.insertBefore(n, d);
16310             }else{
16311                 
16312                 this.tpl.append(this.el, d);
16313             }
16314         }
16315         this.updateIndexes(index);
16316     },
16317
16318     onRemove : function(ds, record, index){
16319        // Roo.log('onRemove');
16320         this.clearSelections();
16321         var el = this.dataName  ?
16322             this.el.child('.roo-tpl-' + this.dataName) :
16323             this.el; 
16324         
16325         el.dom.removeChild(this.nodes[index]);
16326         this.updateIndexes(index);
16327     },
16328
16329     /**
16330      * Refresh an individual node.
16331      * @param {Number} index
16332      */
16333     refreshNode : function(index){
16334         this.onUpdate(this.store, this.store.getAt(index));
16335     },
16336
16337     updateIndexes : function(startIndex, endIndex){
16338         var ns = this.nodes;
16339         startIndex = startIndex || 0;
16340         endIndex = endIndex || ns.length - 1;
16341         for(var i = startIndex; i <= endIndex; i++){
16342             ns[i].nodeIndex = i;
16343         }
16344     },
16345
16346     /**
16347      * Changes the data store this view uses and refresh the view.
16348      * @param {Store} store
16349      */
16350     setStore : function(store, initial){
16351         if(!initial && this.store){
16352             this.store.un("datachanged", this.refresh);
16353             this.store.un("add", this.onAdd);
16354             this.store.un("remove", this.onRemove);
16355             this.store.un("update", this.onUpdate);
16356             this.store.un("clear", this.refresh);
16357             this.store.un("beforeload", this.onBeforeLoad);
16358             this.store.un("load", this.onLoad);
16359             this.store.un("loadexception", this.onLoad);
16360         }
16361         if(store){
16362           
16363             store.on("datachanged", this.refresh, this);
16364             store.on("add", this.onAdd, this);
16365             store.on("remove", this.onRemove, this);
16366             store.on("update", this.onUpdate, this);
16367             store.on("clear", this.refresh, this);
16368             store.on("beforeload", this.onBeforeLoad, this);
16369             store.on("load", this.onLoad, this);
16370             store.on("loadexception", this.onLoad, this);
16371         }
16372         
16373         if(store){
16374             this.refresh();
16375         }
16376     },
16377     /**
16378      * onbeforeLoad - masks the loading area.
16379      *
16380      */
16381     onBeforeLoad : function(store,opts)
16382     {
16383          //Roo.log('onBeforeLoad');   
16384         if (!opts.add) {
16385             this.el.update("");
16386         }
16387         this.el.mask(this.mask ? this.mask : "Loading" ); 
16388     },
16389     onLoad : function ()
16390     {
16391         this.el.unmask();
16392     },
16393     
16394
16395     /**
16396      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16397      * @param {HTMLElement} node
16398      * @return {HTMLElement} The template node
16399      */
16400     findItemFromChild : function(node){
16401         var el = this.dataName  ?
16402             this.el.child('.roo-tpl-' + this.dataName,true) :
16403             this.el.dom; 
16404         
16405         if(!node || node.parentNode == el){
16406                     return node;
16407             }
16408             var p = node.parentNode;
16409             while(p && p != el){
16410             if(p.parentNode == el){
16411                 return p;
16412             }
16413             p = p.parentNode;
16414         }
16415             return null;
16416     },
16417
16418     /** @ignore */
16419     onClick : function(e){
16420         var item = this.findItemFromChild(e.getTarget());
16421         if(item){
16422             var index = this.indexOf(item);
16423             if(this.onItemClick(item, index, e) !== false){
16424                 this.fireEvent("click", this, index, item, e);
16425             }
16426         }else{
16427             this.clearSelections();
16428         }
16429     },
16430
16431     /** @ignore */
16432     onContextMenu : function(e){
16433         var item = this.findItemFromChild(e.getTarget());
16434         if(item){
16435             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16436         }
16437     },
16438
16439     /** @ignore */
16440     onDblClick : function(e){
16441         var item = this.findItemFromChild(e.getTarget());
16442         if(item){
16443             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16444         }
16445     },
16446
16447     onItemClick : function(item, index, e)
16448     {
16449         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16450             return false;
16451         }
16452         if (this.toggleSelect) {
16453             var m = this.isSelected(item) ? 'unselect' : 'select';
16454             //Roo.log(m);
16455             var _t = this;
16456             _t[m](item, true, false);
16457             return true;
16458         }
16459         if(this.multiSelect || this.singleSelect){
16460             if(this.multiSelect && e.shiftKey && this.lastSelection){
16461                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16462             }else{
16463                 this.select(item, this.multiSelect && e.ctrlKey);
16464                 this.lastSelection = item;
16465             }
16466             
16467             if(!this.tickable){
16468                 e.preventDefault();
16469             }
16470             
16471         }
16472         return true;
16473     },
16474
16475     /**
16476      * Get the number of selected nodes.
16477      * @return {Number}
16478      */
16479     getSelectionCount : function(){
16480         return this.selections.length;
16481     },
16482
16483     /**
16484      * Get the currently selected nodes.
16485      * @return {Array} An array of HTMLElements
16486      */
16487     getSelectedNodes : function(){
16488         return this.selections;
16489     },
16490
16491     /**
16492      * Get the indexes of the selected nodes.
16493      * @return {Array}
16494      */
16495     getSelectedIndexes : function(){
16496         var indexes = [], s = this.selections;
16497         for(var i = 0, len = s.length; i < len; i++){
16498             indexes.push(s[i].nodeIndex);
16499         }
16500         return indexes;
16501     },
16502
16503     /**
16504      * Clear all selections
16505      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16506      */
16507     clearSelections : function(suppressEvent){
16508         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16509             this.cmp.elements = this.selections;
16510             this.cmp.removeClass(this.selectedClass);
16511             this.selections = [];
16512             if(!suppressEvent){
16513                 this.fireEvent("selectionchange", this, this.selections);
16514             }
16515         }
16516     },
16517
16518     /**
16519      * Returns true if the passed node is selected
16520      * @param {HTMLElement/Number} node The node or node index
16521      * @return {Boolean}
16522      */
16523     isSelected : function(node){
16524         var s = this.selections;
16525         if(s.length < 1){
16526             return false;
16527         }
16528         node = this.getNode(node);
16529         return s.indexOf(node) !== -1;
16530     },
16531
16532     /**
16533      * Selects nodes.
16534      * @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
16535      * @param {Boolean} keepExisting (optional) true to keep existing selections
16536      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16537      */
16538     select : function(nodeInfo, keepExisting, suppressEvent){
16539         if(nodeInfo instanceof Array){
16540             if(!keepExisting){
16541                 this.clearSelections(true);
16542             }
16543             for(var i = 0, len = nodeInfo.length; i < len; i++){
16544                 this.select(nodeInfo[i], true, true);
16545             }
16546             return;
16547         } 
16548         var node = this.getNode(nodeInfo);
16549         if(!node || this.isSelected(node)){
16550             return; // already selected.
16551         }
16552         if(!keepExisting){
16553             this.clearSelections(true);
16554         }
16555         
16556         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16557             Roo.fly(node).addClass(this.selectedClass);
16558             this.selections.push(node);
16559             if(!suppressEvent){
16560                 this.fireEvent("selectionchange", this, this.selections);
16561             }
16562         }
16563         
16564         
16565     },
16566       /**
16567      * Unselects nodes.
16568      * @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
16569      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16570      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16571      */
16572     unselect : function(nodeInfo, keepExisting, suppressEvent)
16573     {
16574         if(nodeInfo instanceof Array){
16575             Roo.each(this.selections, function(s) {
16576                 this.unselect(s, nodeInfo);
16577             }, this);
16578             return;
16579         }
16580         var node = this.getNode(nodeInfo);
16581         if(!node || !this.isSelected(node)){
16582             //Roo.log("not selected");
16583             return; // not selected.
16584         }
16585         // fireevent???
16586         var ns = [];
16587         Roo.each(this.selections, function(s) {
16588             if (s == node ) {
16589                 Roo.fly(node).removeClass(this.selectedClass);
16590
16591                 return;
16592             }
16593             ns.push(s);
16594         },this);
16595         
16596         this.selections= ns;
16597         this.fireEvent("selectionchange", this, this.selections);
16598     },
16599
16600     /**
16601      * Gets a template node.
16602      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16603      * @return {HTMLElement} The node or null if it wasn't found
16604      */
16605     getNode : function(nodeInfo){
16606         if(typeof nodeInfo == "string"){
16607             return document.getElementById(nodeInfo);
16608         }else if(typeof nodeInfo == "number"){
16609             return this.nodes[nodeInfo];
16610         }
16611         return nodeInfo;
16612     },
16613
16614     /**
16615      * Gets a range template nodes.
16616      * @param {Number} startIndex
16617      * @param {Number} endIndex
16618      * @return {Array} An array of nodes
16619      */
16620     getNodes : function(start, end){
16621         var ns = this.nodes;
16622         start = start || 0;
16623         end = typeof end == "undefined" ? ns.length - 1 : end;
16624         var nodes = [];
16625         if(start <= end){
16626             for(var i = start; i <= end; i++){
16627                 nodes.push(ns[i]);
16628             }
16629         } else{
16630             for(var i = start; i >= end; i--){
16631                 nodes.push(ns[i]);
16632             }
16633         }
16634         return nodes;
16635     },
16636
16637     /**
16638      * Finds the index of the passed node
16639      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16640      * @return {Number} The index of the node or -1
16641      */
16642     indexOf : function(node){
16643         node = this.getNode(node);
16644         if(typeof node.nodeIndex == "number"){
16645             return node.nodeIndex;
16646         }
16647         var ns = this.nodes;
16648         for(var i = 0, len = ns.length; i < len; i++){
16649             if(ns[i] == node){
16650                 return i;
16651             }
16652         }
16653         return -1;
16654     }
16655 });
16656 /*
16657  * - LGPL
16658  *
16659  * based on jquery fullcalendar
16660  * 
16661  */
16662
16663 Roo.bootstrap = Roo.bootstrap || {};
16664 /**
16665  * @class Roo.bootstrap.Calendar
16666  * @extends Roo.bootstrap.Component
16667  * Bootstrap Calendar class
16668  * @cfg {Boolean} loadMask (true|false) default false
16669  * @cfg {Object} header generate the user specific header of the calendar, default false
16670
16671  * @constructor
16672  * Create a new Container
16673  * @param {Object} config The config object
16674  */
16675
16676
16677
16678 Roo.bootstrap.Calendar = function(config){
16679     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16680      this.addEvents({
16681         /**
16682              * @event select
16683              * Fires when a date is selected
16684              * @param {DatePicker} this
16685              * @param {Date} date The selected date
16686              */
16687         'select': true,
16688         /**
16689              * @event monthchange
16690              * Fires when the displayed month changes 
16691              * @param {DatePicker} this
16692              * @param {Date} date The selected month
16693              */
16694         'monthchange': true,
16695         /**
16696              * @event evententer
16697              * Fires when mouse over an event
16698              * @param {Calendar} this
16699              * @param {event} Event
16700              */
16701         'evententer': true,
16702         /**
16703              * @event eventleave
16704              * Fires when the mouse leaves an
16705              * @param {Calendar} this
16706              * @param {event}
16707              */
16708         'eventleave': true,
16709         /**
16710              * @event eventclick
16711              * Fires when the mouse click an
16712              * @param {Calendar} this
16713              * @param {event}
16714              */
16715         'eventclick': true
16716         
16717     });
16718
16719 };
16720
16721 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16722     
16723      /**
16724      * @cfg {Number} startDay
16725      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16726      */
16727     startDay : 0,
16728     
16729     loadMask : false,
16730     
16731     header : false,
16732       
16733     getAutoCreate : function(){
16734         
16735         
16736         var fc_button = function(name, corner, style, content ) {
16737             return Roo.apply({},{
16738                 tag : 'span',
16739                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16740                          (corner.length ?
16741                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16742                             ''
16743                         ),
16744                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16745                 unselectable: 'on'
16746             });
16747         };
16748         
16749         var header = {};
16750         
16751         if(!this.header){
16752             header = {
16753                 tag : 'table',
16754                 cls : 'fc-header',
16755                 style : 'width:100%',
16756                 cn : [
16757                     {
16758                         tag: 'tr',
16759                         cn : [
16760                             {
16761                                 tag : 'td',
16762                                 cls : 'fc-header-left',
16763                                 cn : [
16764                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16765                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16766                                     { tag: 'span', cls: 'fc-header-space' },
16767                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16768
16769
16770                                 ]
16771                             },
16772
16773                             {
16774                                 tag : 'td',
16775                                 cls : 'fc-header-center',
16776                                 cn : [
16777                                     {
16778                                         tag: 'span',
16779                                         cls: 'fc-header-title',
16780                                         cn : {
16781                                             tag: 'H2',
16782                                             html : 'month / year'
16783                                         }
16784                                     }
16785
16786                                 ]
16787                             },
16788                             {
16789                                 tag : 'td',
16790                                 cls : 'fc-header-right',
16791                                 cn : [
16792                               /*      fc_button('month', 'left', '', 'month' ),
16793                                     fc_button('week', '', '', 'week' ),
16794                                     fc_button('day', 'right', '', 'day' )
16795                                 */    
16796
16797                                 ]
16798                             }
16799
16800                         ]
16801                     }
16802                 ]
16803             };
16804         }
16805         
16806         header = this.header;
16807         
16808        
16809         var cal_heads = function() {
16810             var ret = [];
16811             // fixme - handle this.
16812             
16813             for (var i =0; i < Date.dayNames.length; i++) {
16814                 var d = Date.dayNames[i];
16815                 ret.push({
16816                     tag: 'th',
16817                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16818                     html : d.substring(0,3)
16819                 });
16820                 
16821             }
16822             ret[0].cls += ' fc-first';
16823             ret[6].cls += ' fc-last';
16824             return ret;
16825         };
16826         var cal_cell = function(n) {
16827             return  {
16828                 tag: 'td',
16829                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16830                 cn : [
16831                     {
16832                         cn : [
16833                             {
16834                                 cls: 'fc-day-number',
16835                                 html: 'D'
16836                             },
16837                             {
16838                                 cls: 'fc-day-content',
16839                              
16840                                 cn : [
16841                                      {
16842                                         style: 'position: relative;' // height: 17px;
16843                                     }
16844                                 ]
16845                             }
16846                             
16847                             
16848                         ]
16849                     }
16850                 ]
16851                 
16852             }
16853         };
16854         var cal_rows = function() {
16855             
16856             var ret = [];
16857             for (var r = 0; r < 6; r++) {
16858                 var row= {
16859                     tag : 'tr',
16860                     cls : 'fc-week',
16861                     cn : []
16862                 };
16863                 
16864                 for (var i =0; i < Date.dayNames.length; i++) {
16865                     var d = Date.dayNames[i];
16866                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16867
16868                 }
16869                 row.cn[0].cls+=' fc-first';
16870                 row.cn[0].cn[0].style = 'min-height:90px';
16871                 row.cn[6].cls+=' fc-last';
16872                 ret.push(row);
16873                 
16874             }
16875             ret[0].cls += ' fc-first';
16876             ret[4].cls += ' fc-prev-last';
16877             ret[5].cls += ' fc-last';
16878             return ret;
16879             
16880         };
16881         
16882         var cal_table = {
16883             tag: 'table',
16884             cls: 'fc-border-separate',
16885             style : 'width:100%',
16886             cellspacing  : 0,
16887             cn : [
16888                 { 
16889                     tag: 'thead',
16890                     cn : [
16891                         { 
16892                             tag: 'tr',
16893                             cls : 'fc-first fc-last',
16894                             cn : cal_heads()
16895                         }
16896                     ]
16897                 },
16898                 { 
16899                     tag: 'tbody',
16900                     cn : cal_rows()
16901                 }
16902                   
16903             ]
16904         };
16905          
16906          var cfg = {
16907             cls : 'fc fc-ltr',
16908             cn : [
16909                 header,
16910                 {
16911                     cls : 'fc-content',
16912                     style : "position: relative;",
16913                     cn : [
16914                         {
16915                             cls : 'fc-view fc-view-month fc-grid',
16916                             style : 'position: relative',
16917                             unselectable : 'on',
16918                             cn : [
16919                                 {
16920                                     cls : 'fc-event-container',
16921                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16922                                 },
16923                                 cal_table
16924                             ]
16925                         }
16926                     ]
16927     
16928                 }
16929            ] 
16930             
16931         };
16932         
16933          
16934         
16935         return cfg;
16936     },
16937     
16938     
16939     initEvents : function()
16940     {
16941         if(!this.store){
16942             throw "can not find store for calendar";
16943         }
16944         
16945         var mark = {
16946             tag: "div",
16947             cls:"x-dlg-mask",
16948             style: "text-align:center",
16949             cn: [
16950                 {
16951                     tag: "div",
16952                     style: "background-color:white;width:50%;margin:250 auto",
16953                     cn: [
16954                         {
16955                             tag: "img",
16956                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16957                         },
16958                         {
16959                             tag: "span",
16960                             html: "Loading"
16961                         }
16962                         
16963                     ]
16964                 }
16965             ]
16966         };
16967         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16968         
16969         var size = this.el.select('.fc-content', true).first().getSize();
16970         this.maskEl.setSize(size.width, size.height);
16971         this.maskEl.enableDisplayMode("block");
16972         if(!this.loadMask){
16973             this.maskEl.hide();
16974         }
16975         
16976         this.store = Roo.factory(this.store, Roo.data);
16977         this.store.on('load', this.onLoad, this);
16978         this.store.on('beforeload', this.onBeforeLoad, this);
16979         
16980         this.resize();
16981         
16982         this.cells = this.el.select('.fc-day',true);
16983         //Roo.log(this.cells);
16984         this.textNodes = this.el.query('.fc-day-number');
16985         this.cells.addClassOnOver('fc-state-hover');
16986         
16987         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16988         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16989         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16990         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16991         
16992         this.on('monthchange', this.onMonthChange, this);
16993         
16994         this.update(new Date().clearTime());
16995     },
16996     
16997     resize : function() {
16998         var sz  = this.el.getSize();
16999         
17000         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17001         this.el.select('.fc-day-content div',true).setHeight(34);
17002     },
17003     
17004     
17005     // private
17006     showPrevMonth : function(e){
17007         this.update(this.activeDate.add("mo", -1));
17008     },
17009     showToday : function(e){
17010         this.update(new Date().clearTime());
17011     },
17012     // private
17013     showNextMonth : function(e){
17014         this.update(this.activeDate.add("mo", 1));
17015     },
17016
17017     // private
17018     showPrevYear : function(){
17019         this.update(this.activeDate.add("y", -1));
17020     },
17021
17022     // private
17023     showNextYear : function(){
17024         this.update(this.activeDate.add("y", 1));
17025     },
17026
17027     
17028    // private
17029     update : function(date)
17030     {
17031         var vd = this.activeDate;
17032         this.activeDate = date;
17033 //        if(vd && this.el){
17034 //            var t = date.getTime();
17035 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17036 //                Roo.log('using add remove');
17037 //                
17038 //                this.fireEvent('monthchange', this, date);
17039 //                
17040 //                this.cells.removeClass("fc-state-highlight");
17041 //                this.cells.each(function(c){
17042 //                   if(c.dateValue == t){
17043 //                       c.addClass("fc-state-highlight");
17044 //                       setTimeout(function(){
17045 //                            try{c.dom.firstChild.focus();}catch(e){}
17046 //                       }, 50);
17047 //                       return false;
17048 //                   }
17049 //                   return true;
17050 //                });
17051 //                return;
17052 //            }
17053 //        }
17054         
17055         var days = date.getDaysInMonth();
17056         
17057         var firstOfMonth = date.getFirstDateOfMonth();
17058         var startingPos = firstOfMonth.getDay()-this.startDay;
17059         
17060         if(startingPos < this.startDay){
17061             startingPos += 7;
17062         }
17063         
17064         var pm = date.add(Date.MONTH, -1);
17065         var prevStart = pm.getDaysInMonth()-startingPos;
17066 //        
17067         this.cells = this.el.select('.fc-day',true);
17068         this.textNodes = this.el.query('.fc-day-number');
17069         this.cells.addClassOnOver('fc-state-hover');
17070         
17071         var cells = this.cells.elements;
17072         var textEls = this.textNodes;
17073         
17074         Roo.each(cells, function(cell){
17075             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17076         });
17077         
17078         days += startingPos;
17079
17080         // convert everything to numbers so it's fast
17081         var day = 86400000;
17082         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17083         //Roo.log(d);
17084         //Roo.log(pm);
17085         //Roo.log(prevStart);
17086         
17087         var today = new Date().clearTime().getTime();
17088         var sel = date.clearTime().getTime();
17089         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17090         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17091         var ddMatch = this.disabledDatesRE;
17092         var ddText = this.disabledDatesText;
17093         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17094         var ddaysText = this.disabledDaysText;
17095         var format = this.format;
17096         
17097         var setCellClass = function(cal, cell){
17098             cell.row = 0;
17099             cell.events = [];
17100             cell.more = [];
17101             //Roo.log('set Cell Class');
17102             cell.title = "";
17103             var t = d.getTime();
17104             
17105             //Roo.log(d);
17106             
17107             cell.dateValue = t;
17108             if(t == today){
17109                 cell.className += " fc-today";
17110                 cell.className += " fc-state-highlight";
17111                 cell.title = cal.todayText;
17112             }
17113             if(t == sel){
17114                 // disable highlight in other month..
17115                 //cell.className += " fc-state-highlight";
17116                 
17117             }
17118             // disabling
17119             if(t < min) {
17120                 cell.className = " fc-state-disabled";
17121                 cell.title = cal.minText;
17122                 return;
17123             }
17124             if(t > max) {
17125                 cell.className = " fc-state-disabled";
17126                 cell.title = cal.maxText;
17127                 return;
17128             }
17129             if(ddays){
17130                 if(ddays.indexOf(d.getDay()) != -1){
17131                     cell.title = ddaysText;
17132                     cell.className = " fc-state-disabled";
17133                 }
17134             }
17135             if(ddMatch && format){
17136                 var fvalue = d.dateFormat(format);
17137                 if(ddMatch.test(fvalue)){
17138                     cell.title = ddText.replace("%0", fvalue);
17139                     cell.className = " fc-state-disabled";
17140                 }
17141             }
17142             
17143             if (!cell.initialClassName) {
17144                 cell.initialClassName = cell.dom.className;
17145             }
17146             
17147             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17148         };
17149
17150         var i = 0;
17151         
17152         for(; i < startingPos; i++) {
17153             textEls[i].innerHTML = (++prevStart);
17154             d.setDate(d.getDate()+1);
17155             
17156             cells[i].className = "fc-past fc-other-month";
17157             setCellClass(this, cells[i]);
17158         }
17159         
17160         var intDay = 0;
17161         
17162         for(; i < days; i++){
17163             intDay = i - startingPos + 1;
17164             textEls[i].innerHTML = (intDay);
17165             d.setDate(d.getDate()+1);
17166             
17167             cells[i].className = ''; // "x-date-active";
17168             setCellClass(this, cells[i]);
17169         }
17170         var extraDays = 0;
17171         
17172         for(; i < 42; i++) {
17173             textEls[i].innerHTML = (++extraDays);
17174             d.setDate(d.getDate()+1);
17175             
17176             cells[i].className = "fc-future fc-other-month";
17177             setCellClass(this, cells[i]);
17178         }
17179         
17180         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17181         
17182         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17183         
17184         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17185         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17186         
17187         if(totalRows != 6){
17188             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17189             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17190         }
17191         
17192         this.fireEvent('monthchange', this, date);
17193         
17194         
17195         /*
17196         if(!this.internalRender){
17197             var main = this.el.dom.firstChild;
17198             var w = main.offsetWidth;
17199             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17200             Roo.fly(main).setWidth(w);
17201             this.internalRender = true;
17202             // opera does not respect the auto grow header center column
17203             // then, after it gets a width opera refuses to recalculate
17204             // without a second pass
17205             if(Roo.isOpera && !this.secondPass){
17206                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17207                 this.secondPass = true;
17208                 this.update.defer(10, this, [date]);
17209             }
17210         }
17211         */
17212         
17213     },
17214     
17215     findCell : function(dt) {
17216         dt = dt.clearTime().getTime();
17217         var ret = false;
17218         this.cells.each(function(c){
17219             //Roo.log("check " +c.dateValue + '?=' + dt);
17220             if(c.dateValue == dt){
17221                 ret = c;
17222                 return false;
17223             }
17224             return true;
17225         });
17226         
17227         return ret;
17228     },
17229     
17230     findCells : function(ev) {
17231         var s = ev.start.clone().clearTime().getTime();
17232        // Roo.log(s);
17233         var e= ev.end.clone().clearTime().getTime();
17234        // Roo.log(e);
17235         var ret = [];
17236         this.cells.each(function(c){
17237              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17238             
17239             if(c.dateValue > e){
17240                 return ;
17241             }
17242             if(c.dateValue < s){
17243                 return ;
17244             }
17245             ret.push(c);
17246         });
17247         
17248         return ret;    
17249     },
17250     
17251 //    findBestRow: function(cells)
17252 //    {
17253 //        var ret = 0;
17254 //        
17255 //        for (var i =0 ; i < cells.length;i++) {
17256 //            ret  = Math.max(cells[i].rows || 0,ret);
17257 //        }
17258 //        return ret;
17259 //        
17260 //    },
17261     
17262     
17263     addItem : function(ev)
17264     {
17265         // look for vertical location slot in
17266         var cells = this.findCells(ev);
17267         
17268 //        ev.row = this.findBestRow(cells);
17269         
17270         // work out the location.
17271         
17272         var crow = false;
17273         var rows = [];
17274         for(var i =0; i < cells.length; i++) {
17275             
17276             cells[i].row = cells[0].row;
17277             
17278             if(i == 0){
17279                 cells[i].row = cells[i].row + 1;
17280             }
17281             
17282             if (!crow) {
17283                 crow = {
17284                     start : cells[i],
17285                     end :  cells[i]
17286                 };
17287                 continue;
17288             }
17289             if (crow.start.getY() == cells[i].getY()) {
17290                 // on same row.
17291                 crow.end = cells[i];
17292                 continue;
17293             }
17294             // different row.
17295             rows.push(crow);
17296             crow = {
17297                 start: cells[i],
17298                 end : cells[i]
17299             };
17300             
17301         }
17302         
17303         rows.push(crow);
17304         ev.els = [];
17305         ev.rows = rows;
17306         ev.cells = cells;
17307         
17308         cells[0].events.push(ev);
17309         
17310         this.calevents.push(ev);
17311     },
17312     
17313     clearEvents: function() {
17314         
17315         if(!this.calevents){
17316             return;
17317         }
17318         
17319         Roo.each(this.cells.elements, function(c){
17320             c.row = 0;
17321             c.events = [];
17322             c.more = [];
17323         });
17324         
17325         Roo.each(this.calevents, function(e) {
17326             Roo.each(e.els, function(el) {
17327                 el.un('mouseenter' ,this.onEventEnter, this);
17328                 el.un('mouseleave' ,this.onEventLeave, this);
17329                 el.remove();
17330             },this);
17331         },this);
17332         
17333         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17334             e.remove();
17335         });
17336         
17337     },
17338     
17339     renderEvents: function()
17340     {   
17341         var _this = this;
17342         
17343         this.cells.each(function(c) {
17344             
17345             if(c.row < 5){
17346                 return;
17347             }
17348             
17349             var ev = c.events;
17350             
17351             var r = 4;
17352             if(c.row != c.events.length){
17353                 r = 4 - (4 - (c.row - c.events.length));
17354             }
17355             
17356             c.events = ev.slice(0, r);
17357             c.more = ev.slice(r);
17358             
17359             if(c.more.length && c.more.length == 1){
17360                 c.events.push(c.more.pop());
17361             }
17362             
17363             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17364             
17365         });
17366             
17367         this.cells.each(function(c) {
17368             
17369             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17370             
17371             
17372             for (var e = 0; e < c.events.length; e++){
17373                 var ev = c.events[e];
17374                 var rows = ev.rows;
17375                 
17376                 for(var i = 0; i < rows.length; i++) {
17377                 
17378                     // how many rows should it span..
17379
17380                     var  cfg = {
17381                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17382                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17383
17384                         unselectable : "on",
17385                         cn : [
17386                             {
17387                                 cls: 'fc-event-inner',
17388                                 cn : [
17389     //                                {
17390     //                                  tag:'span',
17391     //                                  cls: 'fc-event-time',
17392     //                                  html : cells.length > 1 ? '' : ev.time
17393     //                                },
17394                                     {
17395                                       tag:'span',
17396                                       cls: 'fc-event-title',
17397                                       html : String.format('{0}', ev.title)
17398                                     }
17399
17400
17401                                 ]
17402                             },
17403                             {
17404                                 cls: 'ui-resizable-handle ui-resizable-e',
17405                                 html : '&nbsp;&nbsp;&nbsp'
17406                             }
17407
17408                         ]
17409                     };
17410
17411                     if (i == 0) {
17412                         cfg.cls += ' fc-event-start';
17413                     }
17414                     if ((i+1) == rows.length) {
17415                         cfg.cls += ' fc-event-end';
17416                     }
17417
17418                     var ctr = _this.el.select('.fc-event-container',true).first();
17419                     var cg = ctr.createChild(cfg);
17420
17421                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17422                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17423
17424                     var r = (c.more.length) ? 1 : 0;
17425                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17426                     cg.setWidth(ebox.right - sbox.x -2);
17427
17428                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17429                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17430                     cg.on('click', _this.onEventClick, _this, ev);
17431
17432                     ev.els.push(cg);
17433                     
17434                 }
17435                 
17436             }
17437             
17438             
17439             if(c.more.length){
17440                 var  cfg = {
17441                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17442                     style : 'position: absolute',
17443                     unselectable : "on",
17444                     cn : [
17445                         {
17446                             cls: 'fc-event-inner',
17447                             cn : [
17448                                 {
17449                                   tag:'span',
17450                                   cls: 'fc-event-title',
17451                                   html : 'More'
17452                                 }
17453
17454
17455                             ]
17456                         },
17457                         {
17458                             cls: 'ui-resizable-handle ui-resizable-e',
17459                             html : '&nbsp;&nbsp;&nbsp'
17460                         }
17461
17462                     ]
17463                 };
17464
17465                 var ctr = _this.el.select('.fc-event-container',true).first();
17466                 var cg = ctr.createChild(cfg);
17467
17468                 var sbox = c.select('.fc-day-content',true).first().getBox();
17469                 var ebox = c.select('.fc-day-content',true).first().getBox();
17470                 //Roo.log(cg);
17471                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17472                 cg.setWidth(ebox.right - sbox.x -2);
17473
17474                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17475                 
17476             }
17477             
17478         });
17479         
17480         
17481         
17482     },
17483     
17484     onEventEnter: function (e, el,event,d) {
17485         this.fireEvent('evententer', this, el, event);
17486     },
17487     
17488     onEventLeave: function (e, el,event,d) {
17489         this.fireEvent('eventleave', this, el, event);
17490     },
17491     
17492     onEventClick: function (e, el,event,d) {
17493         this.fireEvent('eventclick', this, el, event);
17494     },
17495     
17496     onMonthChange: function () {
17497         this.store.load();
17498     },
17499     
17500     onMoreEventClick: function(e, el, more)
17501     {
17502         var _this = this;
17503         
17504         this.calpopover.placement = 'right';
17505         this.calpopover.setTitle('More');
17506         
17507         this.calpopover.setContent('');
17508         
17509         var ctr = this.calpopover.el.select('.popover-content', true).first();
17510         
17511         Roo.each(more, function(m){
17512             var cfg = {
17513                 cls : 'fc-event-hori fc-event-draggable',
17514                 html : m.title
17515             };
17516             var cg = ctr.createChild(cfg);
17517             
17518             cg.on('click', _this.onEventClick, _this, m);
17519         });
17520         
17521         this.calpopover.show(el);
17522         
17523         
17524     },
17525     
17526     onLoad: function () 
17527     {   
17528         this.calevents = [];
17529         var cal = this;
17530         
17531         if(this.store.getCount() > 0){
17532             this.store.data.each(function(d){
17533                cal.addItem({
17534                     id : d.data.id,
17535                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17536                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17537                     time : d.data.start_time,
17538                     title : d.data.title,
17539                     description : d.data.description,
17540                     venue : d.data.venue
17541                 });
17542             });
17543         }
17544         
17545         this.renderEvents();
17546         
17547         if(this.calevents.length && this.loadMask){
17548             this.maskEl.hide();
17549         }
17550     },
17551     
17552     onBeforeLoad: function()
17553     {
17554         this.clearEvents();
17555         if(this.loadMask){
17556             this.maskEl.show();
17557         }
17558     }
17559 });
17560
17561  
17562  /*
17563  * - LGPL
17564  *
17565  * element
17566  * 
17567  */
17568
17569 /**
17570  * @class Roo.bootstrap.Popover
17571  * @extends Roo.bootstrap.Component
17572  * Bootstrap Popover class
17573  * @cfg {String} html contents of the popover   (or false to use children..)
17574  * @cfg {String} title of popover (or false to hide)
17575  * @cfg {String} placement how it is placed
17576  * @cfg {String} trigger click || hover (or false to trigger manually)
17577  * @cfg {String} over what (parent or false to trigger manually.)
17578  * @cfg {Number} delay - delay before showing
17579  
17580  * @constructor
17581  * Create a new Popover
17582  * @param {Object} config The config object
17583  */
17584
17585 Roo.bootstrap.Popover = function(config){
17586     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17587     
17588     this.addEvents({
17589         // raw events
17590          /**
17591          * @event show
17592          * After the popover show
17593          * 
17594          * @param {Roo.bootstrap.Popover} this
17595          */
17596         "show" : true,
17597         /**
17598          * @event hide
17599          * After the popover hide
17600          * 
17601          * @param {Roo.bootstrap.Popover} this
17602          */
17603         "hide" : true
17604     });
17605 };
17606
17607 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17608     
17609     title: 'Fill in a title',
17610     html: false,
17611     
17612     placement : 'right',
17613     trigger : 'hover', // hover
17614     
17615     delay : 0,
17616     
17617     over: 'parent',
17618     
17619     can_build_overlaid : false,
17620     
17621     getChildContainer : function()
17622     {
17623         return this.el.select('.popover-content',true).first();
17624     },
17625     
17626     getAutoCreate : function(){
17627          
17628         var cfg = {
17629            cls : 'popover roo-dynamic',
17630            style: 'display:block',
17631            cn : [
17632                 {
17633                     cls : 'arrow'
17634                 },
17635                 {
17636                     cls : 'popover-inner',
17637                     cn : [
17638                         {
17639                             tag: 'h3',
17640                             cls: 'popover-title popover-header',
17641                             html : this.title
17642                         },
17643                         {
17644                             cls : 'popover-content popover-body',
17645                             html : this.html
17646                         }
17647                     ]
17648                     
17649                 }
17650            ]
17651         };
17652         
17653         return cfg;
17654     },
17655     setTitle: function(str)
17656     {
17657         this.title = str;
17658         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17659     },
17660     setContent: function(str)
17661     {
17662         this.html = str;
17663         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17664     },
17665     // as it get's added to the bottom of the page.
17666     onRender : function(ct, position)
17667     {
17668         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17669         if(!this.el){
17670             var cfg = Roo.apply({},  this.getAutoCreate());
17671             cfg.id = Roo.id();
17672             
17673             if (this.cls) {
17674                 cfg.cls += ' ' + this.cls;
17675             }
17676             if (this.style) {
17677                 cfg.style = this.style;
17678             }
17679             //Roo.log("adding to ");
17680             this.el = Roo.get(document.body).createChild(cfg, position);
17681 //            Roo.log(this.el);
17682         }
17683         this.initEvents();
17684     },
17685     
17686     initEvents : function()
17687     {
17688         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17689         this.el.enableDisplayMode('block');
17690         this.el.hide();
17691         if (this.over === false) {
17692             return; 
17693         }
17694         if (this.triggers === false) {
17695             return;
17696         }
17697         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17698         var triggers = this.trigger ? this.trigger.split(' ') : [];
17699         Roo.each(triggers, function(trigger) {
17700         
17701             if (trigger == 'click') {
17702                 on_el.on('click', this.toggle, this);
17703             } else if (trigger != 'manual') {
17704                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17705                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17706       
17707                 on_el.on(eventIn  ,this.enter, this);
17708                 on_el.on(eventOut, this.leave, this);
17709             }
17710         }, this);
17711         
17712     },
17713     
17714     
17715     // private
17716     timeout : null,
17717     hoverState : null,
17718     
17719     toggle : function () {
17720         this.hoverState == 'in' ? this.leave() : this.enter();
17721     },
17722     
17723     enter : function () {
17724         
17725         clearTimeout(this.timeout);
17726     
17727         this.hoverState = 'in';
17728     
17729         if (!this.delay || !this.delay.show) {
17730             this.show();
17731             return;
17732         }
17733         var _t = this;
17734         this.timeout = setTimeout(function () {
17735             if (_t.hoverState == 'in') {
17736                 _t.show();
17737             }
17738         }, this.delay.show)
17739     },
17740     
17741     leave : function() {
17742         clearTimeout(this.timeout);
17743     
17744         this.hoverState = 'out';
17745     
17746         if (!this.delay || !this.delay.hide) {
17747             this.hide();
17748             return;
17749         }
17750         var _t = this;
17751         this.timeout = setTimeout(function () {
17752             if (_t.hoverState == 'out') {
17753                 _t.hide();
17754             }
17755         }, this.delay.hide)
17756     },
17757     
17758     show : function (on_el)
17759     {
17760         if (!on_el) {
17761             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17762         }
17763         
17764         // set content.
17765         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17766         if (this.html !== false) {
17767             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17768         }
17769         this.el.removeClass([
17770             'fade','top','bottom', 'left', 'right','in',
17771             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17772         ]);
17773         if (!this.title.length) {
17774             this.el.select('.popover-title',true).hide();
17775         }
17776         
17777         var placement = typeof this.placement == 'function' ?
17778             this.placement.call(this, this.el, on_el) :
17779             this.placement;
17780             
17781         var autoToken = /\s?auto?\s?/i;
17782         var autoPlace = autoToken.test(placement);
17783         if (autoPlace) {
17784             placement = placement.replace(autoToken, '') || 'top';
17785         }
17786         
17787         //this.el.detach()
17788         //this.el.setXY([0,0]);
17789         this.el.show();
17790         this.el.dom.style.display='block';
17791         this.el.addClass(placement);
17792         
17793         //this.el.appendTo(on_el);
17794         
17795         var p = this.getPosition();
17796         var box = this.el.getBox();
17797         
17798         if (autoPlace) {
17799             // fixme..
17800         }
17801         var align = Roo.bootstrap.Popover.alignment[placement];
17802         
17803 //        Roo.log(align);
17804         this.el.alignTo(on_el, align[0],align[1]);
17805         //var arrow = this.el.select('.arrow',true).first();
17806         //arrow.set(align[2], 
17807         
17808         this.el.addClass('in');
17809         
17810         
17811         if (this.el.hasClass('fade')) {
17812             // fade it?
17813         }
17814         
17815         this.hoverState = 'in';
17816         
17817         this.fireEvent('show', this);
17818         
17819     },
17820     hide : function()
17821     {
17822         this.el.setXY([0,0]);
17823         this.el.removeClass('in');
17824         this.el.hide();
17825         this.hoverState = null;
17826         
17827         this.fireEvent('hide', this);
17828     }
17829     
17830 });
17831
17832 Roo.bootstrap.Popover.alignment = {
17833     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17834     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17835     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17836     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17837 };
17838
17839  /*
17840  * - LGPL
17841  *
17842  * Progress
17843  * 
17844  */
17845
17846 /**
17847  * @class Roo.bootstrap.Progress
17848  * @extends Roo.bootstrap.Component
17849  * Bootstrap Progress class
17850  * @cfg {Boolean} striped striped of the progress bar
17851  * @cfg {Boolean} active animated of the progress bar
17852  * 
17853  * 
17854  * @constructor
17855  * Create a new Progress
17856  * @param {Object} config The config object
17857  */
17858
17859 Roo.bootstrap.Progress = function(config){
17860     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17861 };
17862
17863 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17864     
17865     striped : false,
17866     active: false,
17867     
17868     getAutoCreate : function(){
17869         var cfg = {
17870             tag: 'div',
17871             cls: 'progress'
17872         };
17873         
17874         
17875         if(this.striped){
17876             cfg.cls += ' progress-striped';
17877         }
17878       
17879         if(this.active){
17880             cfg.cls += ' active';
17881         }
17882         
17883         
17884         return cfg;
17885     }
17886    
17887 });
17888
17889  
17890
17891  /*
17892  * - LGPL
17893  *
17894  * ProgressBar
17895  * 
17896  */
17897
17898 /**
17899  * @class Roo.bootstrap.ProgressBar
17900  * @extends Roo.bootstrap.Component
17901  * Bootstrap ProgressBar class
17902  * @cfg {Number} aria_valuenow aria-value now
17903  * @cfg {Number} aria_valuemin aria-value min
17904  * @cfg {Number} aria_valuemax aria-value max
17905  * @cfg {String} label label for the progress bar
17906  * @cfg {String} panel (success | info | warning | danger )
17907  * @cfg {String} role role of the progress bar
17908  * @cfg {String} sr_only text
17909  * 
17910  * 
17911  * @constructor
17912  * Create a new ProgressBar
17913  * @param {Object} config The config object
17914  */
17915
17916 Roo.bootstrap.ProgressBar = function(config){
17917     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17918 };
17919
17920 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17921     
17922     aria_valuenow : 0,
17923     aria_valuemin : 0,
17924     aria_valuemax : 100,
17925     label : false,
17926     panel : false,
17927     role : false,
17928     sr_only: false,
17929     
17930     getAutoCreate : function()
17931     {
17932         
17933         var cfg = {
17934             tag: 'div',
17935             cls: 'progress-bar',
17936             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17937         };
17938         
17939         if(this.sr_only){
17940             cfg.cn = {
17941                 tag: 'span',
17942                 cls: 'sr-only',
17943                 html: this.sr_only
17944             }
17945         }
17946         
17947         if(this.role){
17948             cfg.role = this.role;
17949         }
17950         
17951         if(this.aria_valuenow){
17952             cfg['aria-valuenow'] = this.aria_valuenow;
17953         }
17954         
17955         if(this.aria_valuemin){
17956             cfg['aria-valuemin'] = this.aria_valuemin;
17957         }
17958         
17959         if(this.aria_valuemax){
17960             cfg['aria-valuemax'] = this.aria_valuemax;
17961         }
17962         
17963         if(this.label && !this.sr_only){
17964             cfg.html = this.label;
17965         }
17966         
17967         if(this.panel){
17968             cfg.cls += ' progress-bar-' + this.panel;
17969         }
17970         
17971         return cfg;
17972     },
17973     
17974     update : function(aria_valuenow)
17975     {
17976         this.aria_valuenow = aria_valuenow;
17977         
17978         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17979     }
17980    
17981 });
17982
17983  
17984
17985  /*
17986  * - LGPL
17987  *
17988  * column
17989  * 
17990  */
17991
17992 /**
17993  * @class Roo.bootstrap.TabGroup
17994  * @extends Roo.bootstrap.Column
17995  * Bootstrap Column class
17996  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17997  * @cfg {Boolean} carousel true to make the group behave like a carousel
17998  * @cfg {Boolean} bullets show bullets for the panels
17999  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18000  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18001  * @cfg {Boolean} showarrow (true|false) show arrow default true
18002  * 
18003  * @constructor
18004  * Create a new TabGroup
18005  * @param {Object} config The config object
18006  */
18007
18008 Roo.bootstrap.TabGroup = function(config){
18009     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18010     if (!this.navId) {
18011         this.navId = Roo.id();
18012     }
18013     this.tabs = [];
18014     Roo.bootstrap.TabGroup.register(this);
18015     
18016 };
18017
18018 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18019     
18020     carousel : false,
18021     transition : false,
18022     bullets : 0,
18023     timer : 0,
18024     autoslide : false,
18025     slideFn : false,
18026     slideOnTouch : false,
18027     showarrow : true,
18028     
18029     getAutoCreate : function()
18030     {
18031         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18032         
18033         cfg.cls += ' tab-content';
18034         
18035         if (this.carousel) {
18036             cfg.cls += ' carousel slide';
18037             
18038             cfg.cn = [{
18039                cls : 'carousel-inner',
18040                cn : []
18041             }];
18042         
18043             if(this.bullets  && !Roo.isTouch){
18044                 
18045                 var bullets = {
18046                     cls : 'carousel-bullets',
18047                     cn : []
18048                 };
18049                
18050                 if(this.bullets_cls){
18051                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18052                 }
18053                 
18054                 bullets.cn.push({
18055                     cls : 'clear'
18056                 });
18057                 
18058                 cfg.cn[0].cn.push(bullets);
18059             }
18060             
18061             if(this.showarrow){
18062                 cfg.cn[0].cn.push({
18063                     tag : 'div',
18064                     class : 'carousel-arrow',
18065                     cn : [
18066                         {
18067                             tag : 'div',
18068                             class : 'carousel-prev',
18069                             cn : [
18070                                 {
18071                                     tag : 'i',
18072                                     class : 'fa fa-chevron-left'
18073                                 }
18074                             ]
18075                         },
18076                         {
18077                             tag : 'div',
18078                             class : 'carousel-next',
18079                             cn : [
18080                                 {
18081                                     tag : 'i',
18082                                     class : 'fa fa-chevron-right'
18083                                 }
18084                             ]
18085                         }
18086                     ]
18087                 });
18088             }
18089             
18090         }
18091         
18092         return cfg;
18093     },
18094     
18095     initEvents:  function()
18096     {
18097 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18098 //            this.el.on("touchstart", this.onTouchStart, this);
18099 //        }
18100         
18101         if(this.autoslide){
18102             var _this = this;
18103             
18104             this.slideFn = window.setInterval(function() {
18105                 _this.showPanelNext();
18106             }, this.timer);
18107         }
18108         
18109         if(this.showarrow){
18110             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18111             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18112         }
18113         
18114         
18115     },
18116     
18117 //    onTouchStart : function(e, el, o)
18118 //    {
18119 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18120 //            return;
18121 //        }
18122 //        
18123 //        this.showPanelNext();
18124 //    },
18125     
18126     
18127     getChildContainer : function()
18128     {
18129         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18130     },
18131     
18132     /**
18133     * register a Navigation item
18134     * @param {Roo.bootstrap.NavItem} the navitem to add
18135     */
18136     register : function(item)
18137     {
18138         this.tabs.push( item);
18139         item.navId = this.navId; // not really needed..
18140         this.addBullet();
18141     
18142     },
18143     
18144     getActivePanel : function()
18145     {
18146         var r = false;
18147         Roo.each(this.tabs, function(t) {
18148             if (t.active) {
18149                 r = t;
18150                 return false;
18151             }
18152             return null;
18153         });
18154         return r;
18155         
18156     },
18157     getPanelByName : function(n)
18158     {
18159         var r = false;
18160         Roo.each(this.tabs, function(t) {
18161             if (t.tabId == n) {
18162                 r = t;
18163                 return false;
18164             }
18165             return null;
18166         });
18167         return r;
18168     },
18169     indexOfPanel : function(p)
18170     {
18171         var r = false;
18172         Roo.each(this.tabs, function(t,i) {
18173             if (t.tabId == p.tabId) {
18174                 r = i;
18175                 return false;
18176             }
18177             return null;
18178         });
18179         return r;
18180     },
18181     /**
18182      * show a specific panel
18183      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18184      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18185      */
18186     showPanel : function (pan)
18187     {
18188         if(this.transition || typeof(pan) == 'undefined'){
18189             Roo.log("waiting for the transitionend");
18190             return;
18191         }
18192         
18193         if (typeof(pan) == 'number') {
18194             pan = this.tabs[pan];
18195         }
18196         
18197         if (typeof(pan) == 'string') {
18198             pan = this.getPanelByName(pan);
18199         }
18200         
18201         var cur = this.getActivePanel();
18202         
18203         if(!pan || !cur){
18204             Roo.log('pan or acitve pan is undefined');
18205             return false;
18206         }
18207         
18208         if (pan.tabId == this.getActivePanel().tabId) {
18209             return true;
18210         }
18211         
18212         if (false === cur.fireEvent('beforedeactivate')) {
18213             return false;
18214         }
18215         
18216         if(this.bullets > 0 && !Roo.isTouch){
18217             this.setActiveBullet(this.indexOfPanel(pan));
18218         }
18219         
18220         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18221             
18222             this.transition = true;
18223             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18224             var lr = dir == 'next' ? 'left' : 'right';
18225             pan.el.addClass(dir); // or prev
18226             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18227             cur.el.addClass(lr); // or right
18228             pan.el.addClass(lr);
18229             
18230             var _this = this;
18231             cur.el.on('transitionend', function() {
18232                 Roo.log("trans end?");
18233                 
18234                 pan.el.removeClass([lr,dir]);
18235                 pan.setActive(true);
18236                 
18237                 cur.el.removeClass([lr]);
18238                 cur.setActive(false);
18239                 
18240                 _this.transition = false;
18241                 
18242             }, this, { single:  true } );
18243             
18244             return true;
18245         }
18246         
18247         cur.setActive(false);
18248         pan.setActive(true);
18249         
18250         return true;
18251         
18252     },
18253     showPanelNext : function()
18254     {
18255         var i = this.indexOfPanel(this.getActivePanel());
18256         
18257         if (i >= this.tabs.length - 1 && !this.autoslide) {
18258             return;
18259         }
18260         
18261         if (i >= this.tabs.length - 1 && this.autoslide) {
18262             i = -1;
18263         }
18264         
18265         this.showPanel(this.tabs[i+1]);
18266     },
18267     
18268     showPanelPrev : function()
18269     {
18270         var i = this.indexOfPanel(this.getActivePanel());
18271         
18272         if (i  < 1 && !this.autoslide) {
18273             return;
18274         }
18275         
18276         if (i < 1 && this.autoslide) {
18277             i = this.tabs.length;
18278         }
18279         
18280         this.showPanel(this.tabs[i-1]);
18281     },
18282     
18283     
18284     addBullet: function()
18285     {
18286         if(!this.bullets || Roo.isTouch){
18287             return;
18288         }
18289         var ctr = this.el.select('.carousel-bullets',true).first();
18290         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18291         var bullet = ctr.createChild({
18292             cls : 'bullet bullet-' + i
18293         },ctr.dom.lastChild);
18294         
18295         
18296         var _this = this;
18297         
18298         bullet.on('click', (function(e, el, o, ii, t){
18299
18300             e.preventDefault();
18301
18302             this.showPanel(ii);
18303
18304             if(this.autoslide && this.slideFn){
18305                 clearInterval(this.slideFn);
18306                 this.slideFn = window.setInterval(function() {
18307                     _this.showPanelNext();
18308                 }, this.timer);
18309             }
18310
18311         }).createDelegate(this, [i, bullet], true));
18312                 
18313         
18314     },
18315      
18316     setActiveBullet : function(i)
18317     {
18318         if(Roo.isTouch){
18319             return;
18320         }
18321         
18322         Roo.each(this.el.select('.bullet', true).elements, function(el){
18323             el.removeClass('selected');
18324         });
18325
18326         var bullet = this.el.select('.bullet-' + i, true).first();
18327         
18328         if(!bullet){
18329             return;
18330         }
18331         
18332         bullet.addClass('selected');
18333     }
18334     
18335     
18336   
18337 });
18338
18339  
18340
18341  
18342  
18343 Roo.apply(Roo.bootstrap.TabGroup, {
18344     
18345     groups: {},
18346      /**
18347     * register a Navigation Group
18348     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18349     */
18350     register : function(navgrp)
18351     {
18352         this.groups[navgrp.navId] = navgrp;
18353         
18354     },
18355     /**
18356     * fetch a Navigation Group based on the navigation ID
18357     * if one does not exist , it will get created.
18358     * @param {string} the navgroup to add
18359     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18360     */
18361     get: function(navId) {
18362         if (typeof(this.groups[navId]) == 'undefined') {
18363             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18364         }
18365         return this.groups[navId] ;
18366     }
18367     
18368     
18369     
18370 });
18371
18372  /*
18373  * - LGPL
18374  *
18375  * TabPanel
18376  * 
18377  */
18378
18379 /**
18380  * @class Roo.bootstrap.TabPanel
18381  * @extends Roo.bootstrap.Component
18382  * Bootstrap TabPanel class
18383  * @cfg {Boolean} active panel active
18384  * @cfg {String} html panel content
18385  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18386  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18387  * @cfg {String} href click to link..
18388  * 
18389  * 
18390  * @constructor
18391  * Create a new TabPanel
18392  * @param {Object} config The config object
18393  */
18394
18395 Roo.bootstrap.TabPanel = function(config){
18396     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18397     this.addEvents({
18398         /**
18399              * @event changed
18400              * Fires when the active status changes
18401              * @param {Roo.bootstrap.TabPanel} this
18402              * @param {Boolean} state the new state
18403             
18404          */
18405         'changed': true,
18406         /**
18407              * @event beforedeactivate
18408              * Fires before a tab is de-activated - can be used to do validation on a form.
18409              * @param {Roo.bootstrap.TabPanel} this
18410              * @return {Boolean} false if there is an error
18411             
18412          */
18413         'beforedeactivate': true
18414      });
18415     
18416     this.tabId = this.tabId || Roo.id();
18417   
18418 };
18419
18420 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18421     
18422     active: false,
18423     html: false,
18424     tabId: false,
18425     navId : false,
18426     href : '',
18427     
18428     getAutoCreate : function(){
18429         var cfg = {
18430             tag: 'div',
18431             // item is needed for carousel - not sure if it has any effect otherwise
18432             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18433             html: this.html || ''
18434         };
18435         
18436         if(this.active){
18437             cfg.cls += ' active';
18438         }
18439         
18440         if(this.tabId){
18441             cfg.tabId = this.tabId;
18442         }
18443         
18444         
18445         return cfg;
18446     },
18447     
18448     initEvents:  function()
18449     {
18450         var p = this.parent();
18451         
18452         this.navId = this.navId || p.navId;
18453         
18454         if (typeof(this.navId) != 'undefined') {
18455             // not really needed.. but just in case.. parent should be a NavGroup.
18456             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18457             
18458             tg.register(this);
18459             
18460             var i = tg.tabs.length - 1;
18461             
18462             if(this.active && tg.bullets > 0 && i < tg.bullets){
18463                 tg.setActiveBullet(i);
18464             }
18465         }
18466         
18467         this.el.on('click', this.onClick, this);
18468         
18469         if(Roo.isTouch){
18470             this.el.on("touchstart", this.onTouchStart, this);
18471             this.el.on("touchmove", this.onTouchMove, this);
18472             this.el.on("touchend", this.onTouchEnd, this);
18473         }
18474         
18475     },
18476     
18477     onRender : function(ct, position)
18478     {
18479         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18480     },
18481     
18482     setActive : function(state)
18483     {
18484         Roo.log("panel - set active " + this.tabId + "=" + state);
18485         
18486         this.active = state;
18487         if (!state) {
18488             this.el.removeClass('active');
18489             
18490         } else  if (!this.el.hasClass('active')) {
18491             this.el.addClass('active');
18492         }
18493         
18494         this.fireEvent('changed', this, state);
18495     },
18496     
18497     onClick : function(e)
18498     {
18499         e.preventDefault();
18500         
18501         if(!this.href.length){
18502             return;
18503         }
18504         
18505         window.location.href = this.href;
18506     },
18507     
18508     startX : 0,
18509     startY : 0,
18510     endX : 0,
18511     endY : 0,
18512     swiping : false,
18513     
18514     onTouchStart : function(e)
18515     {
18516         this.swiping = false;
18517         
18518         this.startX = e.browserEvent.touches[0].clientX;
18519         this.startY = e.browserEvent.touches[0].clientY;
18520     },
18521     
18522     onTouchMove : function(e)
18523     {
18524         this.swiping = true;
18525         
18526         this.endX = e.browserEvent.touches[0].clientX;
18527         this.endY = e.browserEvent.touches[0].clientY;
18528     },
18529     
18530     onTouchEnd : function(e)
18531     {
18532         if(!this.swiping){
18533             this.onClick(e);
18534             return;
18535         }
18536         
18537         var tabGroup = this.parent();
18538         
18539         if(this.endX > this.startX){ // swiping right
18540             tabGroup.showPanelPrev();
18541             return;
18542         }
18543         
18544         if(this.startX > this.endX){ // swiping left
18545             tabGroup.showPanelNext();
18546             return;
18547         }
18548     }
18549     
18550     
18551 });
18552  
18553
18554  
18555
18556  /*
18557  * - LGPL
18558  *
18559  * DateField
18560  * 
18561  */
18562
18563 /**
18564  * @class Roo.bootstrap.DateField
18565  * @extends Roo.bootstrap.Input
18566  * Bootstrap DateField class
18567  * @cfg {Number} weekStart default 0
18568  * @cfg {String} viewMode default empty, (months|years)
18569  * @cfg {String} minViewMode default empty, (months|years)
18570  * @cfg {Number} startDate default -Infinity
18571  * @cfg {Number} endDate default Infinity
18572  * @cfg {Boolean} todayHighlight default false
18573  * @cfg {Boolean} todayBtn default false
18574  * @cfg {Boolean} calendarWeeks default false
18575  * @cfg {Object} daysOfWeekDisabled default empty
18576  * @cfg {Boolean} singleMode default false (true | false)
18577  * 
18578  * @cfg {Boolean} keyboardNavigation default true
18579  * @cfg {String} language default en
18580  * 
18581  * @constructor
18582  * Create a new DateField
18583  * @param {Object} config The config object
18584  */
18585
18586 Roo.bootstrap.DateField = function(config){
18587     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18588      this.addEvents({
18589             /**
18590              * @event show
18591              * Fires when this field show.
18592              * @param {Roo.bootstrap.DateField} this
18593              * @param {Mixed} date The date value
18594              */
18595             show : true,
18596             /**
18597              * @event show
18598              * Fires when this field hide.
18599              * @param {Roo.bootstrap.DateField} this
18600              * @param {Mixed} date The date value
18601              */
18602             hide : true,
18603             /**
18604              * @event select
18605              * Fires when select a date.
18606              * @param {Roo.bootstrap.DateField} this
18607              * @param {Mixed} date The date value
18608              */
18609             select : true,
18610             /**
18611              * @event beforeselect
18612              * Fires when before select a date.
18613              * @param {Roo.bootstrap.DateField} this
18614              * @param {Mixed} date The date value
18615              */
18616             beforeselect : true
18617         });
18618 };
18619
18620 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18621     
18622     /**
18623      * @cfg {String} format
18624      * The default date format string which can be overriden for localization support.  The format must be
18625      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18626      */
18627     format : "m/d/y",
18628     /**
18629      * @cfg {String} altFormats
18630      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18631      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18632      */
18633     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18634     
18635     weekStart : 0,
18636     
18637     viewMode : '',
18638     
18639     minViewMode : '',
18640     
18641     todayHighlight : false,
18642     
18643     todayBtn: false,
18644     
18645     language: 'en',
18646     
18647     keyboardNavigation: true,
18648     
18649     calendarWeeks: false,
18650     
18651     startDate: -Infinity,
18652     
18653     endDate: Infinity,
18654     
18655     daysOfWeekDisabled: [],
18656     
18657     _events: [],
18658     
18659     singleMode : false,
18660     
18661     UTCDate: function()
18662     {
18663         return new Date(Date.UTC.apply(Date, arguments));
18664     },
18665     
18666     UTCToday: function()
18667     {
18668         var today = new Date();
18669         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18670     },
18671     
18672     getDate: function() {
18673             var d = this.getUTCDate();
18674             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18675     },
18676     
18677     getUTCDate: function() {
18678             return this.date;
18679     },
18680     
18681     setDate: function(d) {
18682             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18683     },
18684     
18685     setUTCDate: function(d) {
18686             this.date = d;
18687             this.setValue(this.formatDate(this.date));
18688     },
18689         
18690     onRender: function(ct, position)
18691     {
18692         
18693         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18694         
18695         this.language = this.language || 'en';
18696         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18697         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18698         
18699         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18700         this.format = this.format || 'm/d/y';
18701         this.isInline = false;
18702         this.isInput = true;
18703         this.component = this.el.select('.add-on', true).first() || false;
18704         this.component = (this.component && this.component.length === 0) ? false : this.component;
18705         this.hasInput = this.component && this.inputEl().length;
18706         
18707         if (typeof(this.minViewMode === 'string')) {
18708             switch (this.minViewMode) {
18709                 case 'months':
18710                     this.minViewMode = 1;
18711                     break;
18712                 case 'years':
18713                     this.minViewMode = 2;
18714                     break;
18715                 default:
18716                     this.minViewMode = 0;
18717                     break;
18718             }
18719         }
18720         
18721         if (typeof(this.viewMode === 'string')) {
18722             switch (this.viewMode) {
18723                 case 'months':
18724                     this.viewMode = 1;
18725                     break;
18726                 case 'years':
18727                     this.viewMode = 2;
18728                     break;
18729                 default:
18730                     this.viewMode = 0;
18731                     break;
18732             }
18733         }
18734                 
18735         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18736         
18737 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18738         
18739         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18740         
18741         this.picker().on('mousedown', this.onMousedown, this);
18742         this.picker().on('click', this.onClick, this);
18743         
18744         this.picker().addClass('datepicker-dropdown');
18745         
18746         this.startViewMode = this.viewMode;
18747         
18748         if(this.singleMode){
18749             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18750                 v.setVisibilityMode(Roo.Element.DISPLAY);
18751                 v.hide();
18752             });
18753             
18754             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18755                 v.setStyle('width', '189px');
18756             });
18757         }
18758         
18759         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18760             if(!this.calendarWeeks){
18761                 v.remove();
18762                 return;
18763             }
18764             
18765             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18766             v.attr('colspan', function(i, val){
18767                 return parseInt(val) + 1;
18768             });
18769         });
18770                         
18771         
18772         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18773         
18774         this.setStartDate(this.startDate);
18775         this.setEndDate(this.endDate);
18776         
18777         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18778         
18779         this.fillDow();
18780         this.fillMonths();
18781         this.update();
18782         this.showMode();
18783         
18784         if(this.isInline) {
18785             this.showPopup();
18786         }
18787     },
18788     
18789     picker : function()
18790     {
18791         return this.pickerEl;
18792 //        return this.el.select('.datepicker', true).first();
18793     },
18794     
18795     fillDow: function()
18796     {
18797         var dowCnt = this.weekStart;
18798         
18799         var dow = {
18800             tag: 'tr',
18801             cn: [
18802                 
18803             ]
18804         };
18805         
18806         if(this.calendarWeeks){
18807             dow.cn.push({
18808                 tag: 'th',
18809                 cls: 'cw',
18810                 html: '&nbsp;'
18811             })
18812         }
18813         
18814         while (dowCnt < this.weekStart + 7) {
18815             dow.cn.push({
18816                 tag: 'th',
18817                 cls: 'dow',
18818                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18819             });
18820         }
18821         
18822         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18823     },
18824     
18825     fillMonths: function()
18826     {    
18827         var i = 0;
18828         var months = this.picker().select('>.datepicker-months td', true).first();
18829         
18830         months.dom.innerHTML = '';
18831         
18832         while (i < 12) {
18833             var month = {
18834                 tag: 'span',
18835                 cls: 'month',
18836                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18837             };
18838             
18839             months.createChild(month);
18840         }
18841         
18842     },
18843     
18844     update: function()
18845     {
18846         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;
18847         
18848         if (this.date < this.startDate) {
18849             this.viewDate = new Date(this.startDate);
18850         } else if (this.date > this.endDate) {
18851             this.viewDate = new Date(this.endDate);
18852         } else {
18853             this.viewDate = new Date(this.date);
18854         }
18855         
18856         this.fill();
18857     },
18858     
18859     fill: function() 
18860     {
18861         var d = new Date(this.viewDate),
18862                 year = d.getUTCFullYear(),
18863                 month = d.getUTCMonth(),
18864                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18865                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18866                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18867                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18868                 currentDate = this.date && this.date.valueOf(),
18869                 today = this.UTCToday();
18870         
18871         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18872         
18873 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18874         
18875 //        this.picker.select('>tfoot th.today').
18876 //                                              .text(dates[this.language].today)
18877 //                                              .toggle(this.todayBtn !== false);
18878     
18879         this.updateNavArrows();
18880         this.fillMonths();
18881                                                 
18882         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18883         
18884         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18885          
18886         prevMonth.setUTCDate(day);
18887         
18888         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18889         
18890         var nextMonth = new Date(prevMonth);
18891         
18892         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18893         
18894         nextMonth = nextMonth.valueOf();
18895         
18896         var fillMonths = false;
18897         
18898         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18899         
18900         while(prevMonth.valueOf() <= nextMonth) {
18901             var clsName = '';
18902             
18903             if (prevMonth.getUTCDay() === this.weekStart) {
18904                 if(fillMonths){
18905                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18906                 }
18907                     
18908                 fillMonths = {
18909                     tag: 'tr',
18910                     cn: []
18911                 };
18912                 
18913                 if(this.calendarWeeks){
18914                     // ISO 8601: First week contains first thursday.
18915                     // ISO also states week starts on Monday, but we can be more abstract here.
18916                     var
18917                     // Start of current week: based on weekstart/current date
18918                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18919                     // Thursday of this week
18920                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18921                     // First Thursday of year, year from thursday
18922                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18923                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18924                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18925                     
18926                     fillMonths.cn.push({
18927                         tag: 'td',
18928                         cls: 'cw',
18929                         html: calWeek
18930                     });
18931                 }
18932             }
18933             
18934             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18935                 clsName += ' old';
18936             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18937                 clsName += ' new';
18938             }
18939             if (this.todayHighlight &&
18940                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18941                 prevMonth.getUTCMonth() == today.getMonth() &&
18942                 prevMonth.getUTCDate() == today.getDate()) {
18943                 clsName += ' today';
18944             }
18945             
18946             if (currentDate && prevMonth.valueOf() === currentDate) {
18947                 clsName += ' active';
18948             }
18949             
18950             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18951                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18952                     clsName += ' disabled';
18953             }
18954             
18955             fillMonths.cn.push({
18956                 tag: 'td',
18957                 cls: 'day ' + clsName,
18958                 html: prevMonth.getDate()
18959             });
18960             
18961             prevMonth.setDate(prevMonth.getDate()+1);
18962         }
18963           
18964         var currentYear = this.date && this.date.getUTCFullYear();
18965         var currentMonth = this.date && this.date.getUTCMonth();
18966         
18967         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18968         
18969         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18970             v.removeClass('active');
18971             
18972             if(currentYear === year && k === currentMonth){
18973                 v.addClass('active');
18974             }
18975             
18976             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18977                 v.addClass('disabled');
18978             }
18979             
18980         });
18981         
18982         
18983         year = parseInt(year/10, 10) * 10;
18984         
18985         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18986         
18987         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18988         
18989         year -= 1;
18990         for (var i = -1; i < 11; i++) {
18991             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18992                 tag: 'span',
18993                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18994                 html: year
18995             });
18996             
18997             year += 1;
18998         }
18999     },
19000     
19001     showMode: function(dir) 
19002     {
19003         if (dir) {
19004             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19005         }
19006         
19007         Roo.each(this.picker().select('>div',true).elements, function(v){
19008             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19009             v.hide();
19010         });
19011         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19012     },
19013     
19014     place: function()
19015     {
19016         if(this.isInline) {
19017             return;
19018         }
19019         
19020         this.picker().removeClass(['bottom', 'top']);
19021         
19022         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19023             /*
19024              * place to the top of element!
19025              *
19026              */
19027             
19028             this.picker().addClass('top');
19029             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19030             
19031             return;
19032         }
19033         
19034         this.picker().addClass('bottom');
19035         
19036         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19037     },
19038     
19039     parseDate : function(value)
19040     {
19041         if(!value || value instanceof Date){
19042             return value;
19043         }
19044         var v = Date.parseDate(value, this.format);
19045         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19046             v = Date.parseDate(value, 'Y-m-d');
19047         }
19048         if(!v && this.altFormats){
19049             if(!this.altFormatsArray){
19050                 this.altFormatsArray = this.altFormats.split("|");
19051             }
19052             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19053                 v = Date.parseDate(value, this.altFormatsArray[i]);
19054             }
19055         }
19056         return v;
19057     },
19058     
19059     formatDate : function(date, fmt)
19060     {   
19061         return (!date || !(date instanceof Date)) ?
19062         date : date.dateFormat(fmt || this.format);
19063     },
19064     
19065     onFocus : function()
19066     {
19067         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19068         this.showPopup();
19069     },
19070     
19071     onBlur : function()
19072     {
19073         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19074         
19075         var d = this.inputEl().getValue();
19076         
19077         this.setValue(d);
19078                 
19079         this.hidePopup();
19080     },
19081     
19082     showPopup : function()
19083     {
19084         this.picker().show();
19085         this.update();
19086         this.place();
19087         
19088         this.fireEvent('showpopup', this, this.date);
19089     },
19090     
19091     hidePopup : function()
19092     {
19093         if(this.isInline) {
19094             return;
19095         }
19096         this.picker().hide();
19097         this.viewMode = this.startViewMode;
19098         this.showMode();
19099         
19100         this.fireEvent('hidepopup', this, this.date);
19101         
19102     },
19103     
19104     onMousedown: function(e)
19105     {
19106         e.stopPropagation();
19107         e.preventDefault();
19108     },
19109     
19110     keyup: function(e)
19111     {
19112         Roo.bootstrap.DateField.superclass.keyup.call(this);
19113         this.update();
19114     },
19115
19116     setValue: function(v)
19117     {
19118         if(this.fireEvent('beforeselect', this, v) !== false){
19119             var d = new Date(this.parseDate(v) ).clearTime();
19120         
19121             if(isNaN(d.getTime())){
19122                 this.date = this.viewDate = '';
19123                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19124                 return;
19125             }
19126
19127             v = this.formatDate(d);
19128
19129             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19130
19131             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19132
19133             this.update();
19134
19135             this.fireEvent('select', this, this.date);
19136         }
19137     },
19138     
19139     getValue: function()
19140     {
19141         return this.formatDate(this.date);
19142     },
19143     
19144     fireKey: function(e)
19145     {
19146         if (!this.picker().isVisible()){
19147             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19148                 this.showPopup();
19149             }
19150             return;
19151         }
19152         
19153         var dateChanged = false,
19154         dir, day, month,
19155         newDate, newViewDate;
19156         
19157         switch(e.keyCode){
19158             case 27: // escape
19159                 this.hidePopup();
19160                 e.preventDefault();
19161                 break;
19162             case 37: // left
19163             case 39: // right
19164                 if (!this.keyboardNavigation) {
19165                     break;
19166                 }
19167                 dir = e.keyCode == 37 ? -1 : 1;
19168                 
19169                 if (e.ctrlKey){
19170                     newDate = this.moveYear(this.date, dir);
19171                     newViewDate = this.moveYear(this.viewDate, dir);
19172                 } else if (e.shiftKey){
19173                     newDate = this.moveMonth(this.date, dir);
19174                     newViewDate = this.moveMonth(this.viewDate, dir);
19175                 } else {
19176                     newDate = new Date(this.date);
19177                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19178                     newViewDate = new Date(this.viewDate);
19179                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19180                 }
19181                 if (this.dateWithinRange(newDate)){
19182                     this.date = newDate;
19183                     this.viewDate = newViewDate;
19184                     this.setValue(this.formatDate(this.date));
19185 //                    this.update();
19186                     e.preventDefault();
19187                     dateChanged = true;
19188                 }
19189                 break;
19190             case 38: // up
19191             case 40: // down
19192                 if (!this.keyboardNavigation) {
19193                     break;
19194                 }
19195                 dir = e.keyCode == 38 ? -1 : 1;
19196                 if (e.ctrlKey){
19197                     newDate = this.moveYear(this.date, dir);
19198                     newViewDate = this.moveYear(this.viewDate, dir);
19199                 } else if (e.shiftKey){
19200                     newDate = this.moveMonth(this.date, dir);
19201                     newViewDate = this.moveMonth(this.viewDate, dir);
19202                 } else {
19203                     newDate = new Date(this.date);
19204                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19205                     newViewDate = new Date(this.viewDate);
19206                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19207                 }
19208                 if (this.dateWithinRange(newDate)){
19209                     this.date = newDate;
19210                     this.viewDate = newViewDate;
19211                     this.setValue(this.formatDate(this.date));
19212 //                    this.update();
19213                     e.preventDefault();
19214                     dateChanged = true;
19215                 }
19216                 break;
19217             case 13: // enter
19218                 this.setValue(this.formatDate(this.date));
19219                 this.hidePopup();
19220                 e.preventDefault();
19221                 break;
19222             case 9: // tab
19223                 this.setValue(this.formatDate(this.date));
19224                 this.hidePopup();
19225                 break;
19226             case 16: // shift
19227             case 17: // ctrl
19228             case 18: // alt
19229                 break;
19230             default :
19231                 this.hidePopup();
19232                 
19233         }
19234     },
19235     
19236     
19237     onClick: function(e) 
19238     {
19239         e.stopPropagation();
19240         e.preventDefault();
19241         
19242         var target = e.getTarget();
19243         
19244         if(target.nodeName.toLowerCase() === 'i'){
19245             target = Roo.get(target).dom.parentNode;
19246         }
19247         
19248         var nodeName = target.nodeName;
19249         var className = target.className;
19250         var html = target.innerHTML;
19251         //Roo.log(nodeName);
19252         
19253         switch(nodeName.toLowerCase()) {
19254             case 'th':
19255                 switch(className) {
19256                     case 'switch':
19257                         this.showMode(1);
19258                         break;
19259                     case 'prev':
19260                     case 'next':
19261                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19262                         switch(this.viewMode){
19263                                 case 0:
19264                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19265                                         break;
19266                                 case 1:
19267                                 case 2:
19268                                         this.viewDate = this.moveYear(this.viewDate, dir);
19269                                         break;
19270                         }
19271                         this.fill();
19272                         break;
19273                     case 'today':
19274                         var date = new Date();
19275                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19276 //                        this.fill()
19277                         this.setValue(this.formatDate(this.date));
19278                         
19279                         this.hidePopup();
19280                         break;
19281                 }
19282                 break;
19283             case 'span':
19284                 if (className.indexOf('disabled') < 0) {
19285                     this.viewDate.setUTCDate(1);
19286                     if (className.indexOf('month') > -1) {
19287                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19288                     } else {
19289                         var year = parseInt(html, 10) || 0;
19290                         this.viewDate.setUTCFullYear(year);
19291                         
19292                     }
19293                     
19294                     if(this.singleMode){
19295                         this.setValue(this.formatDate(this.viewDate));
19296                         this.hidePopup();
19297                         return;
19298                     }
19299                     
19300                     this.showMode(-1);
19301                     this.fill();
19302                 }
19303                 break;
19304                 
19305             case 'td':
19306                 //Roo.log(className);
19307                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19308                     var day = parseInt(html, 10) || 1;
19309                     var year = this.viewDate.getUTCFullYear(),
19310                         month = this.viewDate.getUTCMonth();
19311
19312                     if (className.indexOf('old') > -1) {
19313                         if(month === 0 ){
19314                             month = 11;
19315                             year -= 1;
19316                         }else{
19317                             month -= 1;
19318                         }
19319                     } else if (className.indexOf('new') > -1) {
19320                         if (month == 11) {
19321                             month = 0;
19322                             year += 1;
19323                         } else {
19324                             month += 1;
19325                         }
19326                     }
19327                     //Roo.log([year,month,day]);
19328                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19329                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19330 //                    this.fill();
19331                     //Roo.log(this.formatDate(this.date));
19332                     this.setValue(this.formatDate(this.date));
19333                     this.hidePopup();
19334                 }
19335                 break;
19336         }
19337     },
19338     
19339     setStartDate: function(startDate)
19340     {
19341         this.startDate = startDate || -Infinity;
19342         if (this.startDate !== -Infinity) {
19343             this.startDate = this.parseDate(this.startDate);
19344         }
19345         this.update();
19346         this.updateNavArrows();
19347     },
19348
19349     setEndDate: function(endDate)
19350     {
19351         this.endDate = endDate || Infinity;
19352         if (this.endDate !== Infinity) {
19353             this.endDate = this.parseDate(this.endDate);
19354         }
19355         this.update();
19356         this.updateNavArrows();
19357     },
19358     
19359     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19360     {
19361         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19362         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19363             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19364         }
19365         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19366             return parseInt(d, 10);
19367         });
19368         this.update();
19369         this.updateNavArrows();
19370     },
19371     
19372     updateNavArrows: function() 
19373     {
19374         if(this.singleMode){
19375             return;
19376         }
19377         
19378         var d = new Date(this.viewDate),
19379         year = d.getUTCFullYear(),
19380         month = d.getUTCMonth();
19381         
19382         Roo.each(this.picker().select('.prev', true).elements, function(v){
19383             v.show();
19384             switch (this.viewMode) {
19385                 case 0:
19386
19387                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19388                         v.hide();
19389                     }
19390                     break;
19391                 case 1:
19392                 case 2:
19393                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19394                         v.hide();
19395                     }
19396                     break;
19397             }
19398         });
19399         
19400         Roo.each(this.picker().select('.next', true).elements, function(v){
19401             v.show();
19402             switch (this.viewMode) {
19403                 case 0:
19404
19405                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19406                         v.hide();
19407                     }
19408                     break;
19409                 case 1:
19410                 case 2:
19411                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19412                         v.hide();
19413                     }
19414                     break;
19415             }
19416         })
19417     },
19418     
19419     moveMonth: function(date, dir)
19420     {
19421         if (!dir) {
19422             return date;
19423         }
19424         var new_date = new Date(date.valueOf()),
19425         day = new_date.getUTCDate(),
19426         month = new_date.getUTCMonth(),
19427         mag = Math.abs(dir),
19428         new_month, test;
19429         dir = dir > 0 ? 1 : -1;
19430         if (mag == 1){
19431             test = dir == -1
19432             // If going back one month, make sure month is not current month
19433             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19434             ? function(){
19435                 return new_date.getUTCMonth() == month;
19436             }
19437             // If going forward one month, make sure month is as expected
19438             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19439             : function(){
19440                 return new_date.getUTCMonth() != new_month;
19441             };
19442             new_month = month + dir;
19443             new_date.setUTCMonth(new_month);
19444             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19445             if (new_month < 0 || new_month > 11) {
19446                 new_month = (new_month + 12) % 12;
19447             }
19448         } else {
19449             // For magnitudes >1, move one month at a time...
19450             for (var i=0; i<mag; i++) {
19451                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19452                 new_date = this.moveMonth(new_date, dir);
19453             }
19454             // ...then reset the day, keeping it in the new month
19455             new_month = new_date.getUTCMonth();
19456             new_date.setUTCDate(day);
19457             test = function(){
19458                 return new_month != new_date.getUTCMonth();
19459             };
19460         }
19461         // Common date-resetting loop -- if date is beyond end of month, make it
19462         // end of month
19463         while (test()){
19464             new_date.setUTCDate(--day);
19465             new_date.setUTCMonth(new_month);
19466         }
19467         return new_date;
19468     },
19469
19470     moveYear: function(date, dir)
19471     {
19472         return this.moveMonth(date, dir*12);
19473     },
19474
19475     dateWithinRange: function(date)
19476     {
19477         return date >= this.startDate && date <= this.endDate;
19478     },
19479
19480     
19481     remove: function() 
19482     {
19483         this.picker().remove();
19484     },
19485     
19486     validateValue : function(value)
19487     {
19488         if(this.getVisibilityEl().hasClass('hidden')){
19489             return true;
19490         }
19491         
19492         if(value.length < 1)  {
19493             if(this.allowBlank){
19494                 return true;
19495             }
19496             return false;
19497         }
19498         
19499         if(value.length < this.minLength){
19500             return false;
19501         }
19502         if(value.length > this.maxLength){
19503             return false;
19504         }
19505         if(this.vtype){
19506             var vt = Roo.form.VTypes;
19507             if(!vt[this.vtype](value, this)){
19508                 return false;
19509             }
19510         }
19511         if(typeof this.validator == "function"){
19512             var msg = this.validator(value);
19513             if(msg !== true){
19514                 return false;
19515             }
19516         }
19517         
19518         if(this.regex && !this.regex.test(value)){
19519             return false;
19520         }
19521         
19522         if(typeof(this.parseDate(value)) == 'undefined'){
19523             return false;
19524         }
19525         
19526         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19527             return false;
19528         }      
19529         
19530         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19531             return false;
19532         } 
19533         
19534         
19535         return true;
19536     },
19537     
19538     reset : function()
19539     {
19540         this.date = this.viewDate = '';
19541         
19542         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19543     }
19544    
19545 });
19546
19547 Roo.apply(Roo.bootstrap.DateField,  {
19548     
19549     head : {
19550         tag: 'thead',
19551         cn: [
19552         {
19553             tag: 'tr',
19554             cn: [
19555             {
19556                 tag: 'th',
19557                 cls: 'prev',
19558                 html: '<i class="fa fa-arrow-left"/>'
19559             },
19560             {
19561                 tag: 'th',
19562                 cls: 'switch',
19563                 colspan: '5'
19564             },
19565             {
19566                 tag: 'th',
19567                 cls: 'next',
19568                 html: '<i class="fa fa-arrow-right"/>'
19569             }
19570
19571             ]
19572         }
19573         ]
19574     },
19575     
19576     content : {
19577         tag: 'tbody',
19578         cn: [
19579         {
19580             tag: 'tr',
19581             cn: [
19582             {
19583                 tag: 'td',
19584                 colspan: '7'
19585             }
19586             ]
19587         }
19588         ]
19589     },
19590     
19591     footer : {
19592         tag: 'tfoot',
19593         cn: [
19594         {
19595             tag: 'tr',
19596             cn: [
19597             {
19598                 tag: 'th',
19599                 colspan: '7',
19600                 cls: 'today'
19601             }
19602                     
19603             ]
19604         }
19605         ]
19606     },
19607     
19608     dates:{
19609         en: {
19610             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19611             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19612             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19613             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19614             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19615             today: "Today"
19616         }
19617     },
19618     
19619     modes: [
19620     {
19621         clsName: 'days',
19622         navFnc: 'Month',
19623         navStep: 1
19624     },
19625     {
19626         clsName: 'months',
19627         navFnc: 'FullYear',
19628         navStep: 1
19629     },
19630     {
19631         clsName: 'years',
19632         navFnc: 'FullYear',
19633         navStep: 10
19634     }]
19635 });
19636
19637 Roo.apply(Roo.bootstrap.DateField,  {
19638   
19639     template : {
19640         tag: 'div',
19641         cls: 'datepicker dropdown-menu roo-dynamic',
19642         cn: [
19643         {
19644             tag: 'div',
19645             cls: 'datepicker-days',
19646             cn: [
19647             {
19648                 tag: 'table',
19649                 cls: 'table-condensed',
19650                 cn:[
19651                 Roo.bootstrap.DateField.head,
19652                 {
19653                     tag: 'tbody'
19654                 },
19655                 Roo.bootstrap.DateField.footer
19656                 ]
19657             }
19658             ]
19659         },
19660         {
19661             tag: 'div',
19662             cls: 'datepicker-months',
19663             cn: [
19664             {
19665                 tag: 'table',
19666                 cls: 'table-condensed',
19667                 cn:[
19668                 Roo.bootstrap.DateField.head,
19669                 Roo.bootstrap.DateField.content,
19670                 Roo.bootstrap.DateField.footer
19671                 ]
19672             }
19673             ]
19674         },
19675         {
19676             tag: 'div',
19677             cls: 'datepicker-years',
19678             cn: [
19679             {
19680                 tag: 'table',
19681                 cls: 'table-condensed',
19682                 cn:[
19683                 Roo.bootstrap.DateField.head,
19684                 Roo.bootstrap.DateField.content,
19685                 Roo.bootstrap.DateField.footer
19686                 ]
19687             }
19688             ]
19689         }
19690         ]
19691     }
19692 });
19693
19694  
19695
19696  /*
19697  * - LGPL
19698  *
19699  * TimeField
19700  * 
19701  */
19702
19703 /**
19704  * @class Roo.bootstrap.TimeField
19705  * @extends Roo.bootstrap.Input
19706  * Bootstrap DateField class
19707  * 
19708  * 
19709  * @constructor
19710  * Create a new TimeField
19711  * @param {Object} config The config object
19712  */
19713
19714 Roo.bootstrap.TimeField = function(config){
19715     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19716     this.addEvents({
19717             /**
19718              * @event show
19719              * Fires when this field show.
19720              * @param {Roo.bootstrap.DateField} thisthis
19721              * @param {Mixed} date The date value
19722              */
19723             show : true,
19724             /**
19725              * @event show
19726              * Fires when this field hide.
19727              * @param {Roo.bootstrap.DateField} this
19728              * @param {Mixed} date The date value
19729              */
19730             hide : true,
19731             /**
19732              * @event select
19733              * Fires when select a date.
19734              * @param {Roo.bootstrap.DateField} this
19735              * @param {Mixed} date The date value
19736              */
19737             select : true
19738         });
19739 };
19740
19741 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19742     
19743     /**
19744      * @cfg {String} format
19745      * The default time format string which can be overriden for localization support.  The format must be
19746      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19747      */
19748     format : "H:i",
19749        
19750     onRender: function(ct, position)
19751     {
19752         
19753         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19754                 
19755         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19756         
19757         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19758         
19759         this.pop = this.picker().select('>.datepicker-time',true).first();
19760         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19761         
19762         this.picker().on('mousedown', this.onMousedown, this);
19763         this.picker().on('click', this.onClick, this);
19764         
19765         this.picker().addClass('datepicker-dropdown');
19766     
19767         this.fillTime();
19768         this.update();
19769             
19770         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19771         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19772         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19773         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19774         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19775         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19776
19777     },
19778     
19779     fireKey: function(e){
19780         if (!this.picker().isVisible()){
19781             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19782                 this.show();
19783             }
19784             return;
19785         }
19786
19787         e.preventDefault();
19788         
19789         switch(e.keyCode){
19790             case 27: // escape
19791                 this.hide();
19792                 break;
19793             case 37: // left
19794             case 39: // right
19795                 this.onTogglePeriod();
19796                 break;
19797             case 38: // up
19798                 this.onIncrementMinutes();
19799                 break;
19800             case 40: // down
19801                 this.onDecrementMinutes();
19802                 break;
19803             case 13: // enter
19804             case 9: // tab
19805                 this.setTime();
19806                 break;
19807         }
19808     },
19809     
19810     onClick: function(e) {
19811         e.stopPropagation();
19812         e.preventDefault();
19813     },
19814     
19815     picker : function()
19816     {
19817         return this.el.select('.datepicker', true).first();
19818     },
19819     
19820     fillTime: function()
19821     {    
19822         var time = this.pop.select('tbody', true).first();
19823         
19824         time.dom.innerHTML = '';
19825         
19826         time.createChild({
19827             tag: 'tr',
19828             cn: [
19829                 {
19830                     tag: 'td',
19831                     cn: [
19832                         {
19833                             tag: 'a',
19834                             href: '#',
19835                             cls: 'btn',
19836                             cn: [
19837                                 {
19838                                     tag: 'span',
19839                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19840                                 }
19841                             ]
19842                         } 
19843                     ]
19844                 },
19845                 {
19846                     tag: 'td',
19847                     cls: 'separator'
19848                 },
19849                 {
19850                     tag: 'td',
19851                     cn: [
19852                         {
19853                             tag: 'a',
19854                             href: '#',
19855                             cls: 'btn',
19856                             cn: [
19857                                 {
19858                                     tag: 'span',
19859                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19860                                 }
19861                             ]
19862                         }
19863                     ]
19864                 },
19865                 {
19866                     tag: 'td',
19867                     cls: 'separator'
19868                 }
19869             ]
19870         });
19871         
19872         time.createChild({
19873             tag: 'tr',
19874             cn: [
19875                 {
19876                     tag: 'td',
19877                     cn: [
19878                         {
19879                             tag: 'span',
19880                             cls: 'timepicker-hour',
19881                             html: '00'
19882                         }  
19883                     ]
19884                 },
19885                 {
19886                     tag: 'td',
19887                     cls: 'separator',
19888                     html: ':'
19889                 },
19890                 {
19891                     tag: 'td',
19892                     cn: [
19893                         {
19894                             tag: 'span',
19895                             cls: 'timepicker-minute',
19896                             html: '00'
19897                         }  
19898                     ]
19899                 },
19900                 {
19901                     tag: 'td',
19902                     cls: 'separator'
19903                 },
19904                 {
19905                     tag: 'td',
19906                     cn: [
19907                         {
19908                             tag: 'button',
19909                             type: 'button',
19910                             cls: 'btn btn-primary period',
19911                             html: 'AM'
19912                             
19913                         }
19914                     ]
19915                 }
19916             ]
19917         });
19918         
19919         time.createChild({
19920             tag: 'tr',
19921             cn: [
19922                 {
19923                     tag: 'td',
19924                     cn: [
19925                         {
19926                             tag: 'a',
19927                             href: '#',
19928                             cls: 'btn',
19929                             cn: [
19930                                 {
19931                                     tag: 'span',
19932                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19933                                 }
19934                             ]
19935                         }
19936                     ]
19937                 },
19938                 {
19939                     tag: 'td',
19940                     cls: 'separator'
19941                 },
19942                 {
19943                     tag: 'td',
19944                     cn: [
19945                         {
19946                             tag: 'a',
19947                             href: '#',
19948                             cls: 'btn',
19949                             cn: [
19950                                 {
19951                                     tag: 'span',
19952                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19953                                 }
19954                             ]
19955                         }
19956                     ]
19957                 },
19958                 {
19959                     tag: 'td',
19960                     cls: 'separator'
19961                 }
19962             ]
19963         });
19964         
19965     },
19966     
19967     update: function()
19968     {
19969         
19970         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19971         
19972         this.fill();
19973     },
19974     
19975     fill: function() 
19976     {
19977         var hours = this.time.getHours();
19978         var minutes = this.time.getMinutes();
19979         var period = 'AM';
19980         
19981         if(hours > 11){
19982             period = 'PM';
19983         }
19984         
19985         if(hours == 0){
19986             hours = 12;
19987         }
19988         
19989         
19990         if(hours > 12){
19991             hours = hours - 12;
19992         }
19993         
19994         if(hours < 10){
19995             hours = '0' + hours;
19996         }
19997         
19998         if(minutes < 10){
19999             minutes = '0' + minutes;
20000         }
20001         
20002         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20003         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20004         this.pop.select('button', true).first().dom.innerHTML = period;
20005         
20006     },
20007     
20008     place: function()
20009     {   
20010         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20011         
20012         var cls = ['bottom'];
20013         
20014         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20015             cls.pop();
20016             cls.push('top');
20017         }
20018         
20019         cls.push('right');
20020         
20021         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20022             cls.pop();
20023             cls.push('left');
20024         }
20025         
20026         this.picker().addClass(cls.join('-'));
20027         
20028         var _this = this;
20029         
20030         Roo.each(cls, function(c){
20031             if(c == 'bottom'){
20032                 _this.picker().setTop(_this.inputEl().getHeight());
20033                 return;
20034             }
20035             if(c == 'top'){
20036                 _this.picker().setTop(0 - _this.picker().getHeight());
20037                 return;
20038             }
20039             
20040             if(c == 'left'){
20041                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20042                 return;
20043             }
20044             if(c == 'right'){
20045                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20046                 return;
20047             }
20048         });
20049         
20050     },
20051   
20052     onFocus : function()
20053     {
20054         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20055         this.show();
20056     },
20057     
20058     onBlur : function()
20059     {
20060         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20061         this.hide();
20062     },
20063     
20064     show : function()
20065     {
20066         this.picker().show();
20067         this.pop.show();
20068         this.update();
20069         this.place();
20070         
20071         this.fireEvent('show', this, this.date);
20072     },
20073     
20074     hide : function()
20075     {
20076         this.picker().hide();
20077         this.pop.hide();
20078         
20079         this.fireEvent('hide', this, this.date);
20080     },
20081     
20082     setTime : function()
20083     {
20084         this.hide();
20085         this.setValue(this.time.format(this.format));
20086         
20087         this.fireEvent('select', this, this.date);
20088         
20089         
20090     },
20091     
20092     onMousedown: function(e){
20093         e.stopPropagation();
20094         e.preventDefault();
20095     },
20096     
20097     onIncrementHours: function()
20098     {
20099         Roo.log('onIncrementHours');
20100         this.time = this.time.add(Date.HOUR, 1);
20101         this.update();
20102         
20103     },
20104     
20105     onDecrementHours: function()
20106     {
20107         Roo.log('onDecrementHours');
20108         this.time = this.time.add(Date.HOUR, -1);
20109         this.update();
20110     },
20111     
20112     onIncrementMinutes: function()
20113     {
20114         Roo.log('onIncrementMinutes');
20115         this.time = this.time.add(Date.MINUTE, 1);
20116         this.update();
20117     },
20118     
20119     onDecrementMinutes: function()
20120     {
20121         Roo.log('onDecrementMinutes');
20122         this.time = this.time.add(Date.MINUTE, -1);
20123         this.update();
20124     },
20125     
20126     onTogglePeriod: function()
20127     {
20128         Roo.log('onTogglePeriod');
20129         this.time = this.time.add(Date.HOUR, 12);
20130         this.update();
20131     }
20132     
20133    
20134 });
20135
20136 Roo.apply(Roo.bootstrap.TimeField,  {
20137     
20138     content : {
20139         tag: 'tbody',
20140         cn: [
20141             {
20142                 tag: 'tr',
20143                 cn: [
20144                 {
20145                     tag: 'td',
20146                     colspan: '7'
20147                 }
20148                 ]
20149             }
20150         ]
20151     },
20152     
20153     footer : {
20154         tag: 'tfoot',
20155         cn: [
20156             {
20157                 tag: 'tr',
20158                 cn: [
20159                 {
20160                     tag: 'th',
20161                     colspan: '7',
20162                     cls: '',
20163                     cn: [
20164                         {
20165                             tag: 'button',
20166                             cls: 'btn btn-info ok',
20167                             html: 'OK'
20168                         }
20169                     ]
20170                 }
20171
20172                 ]
20173             }
20174         ]
20175     }
20176 });
20177
20178 Roo.apply(Roo.bootstrap.TimeField,  {
20179   
20180     template : {
20181         tag: 'div',
20182         cls: 'datepicker dropdown-menu',
20183         cn: [
20184             {
20185                 tag: 'div',
20186                 cls: 'datepicker-time',
20187                 cn: [
20188                 {
20189                     tag: 'table',
20190                     cls: 'table-condensed',
20191                     cn:[
20192                     Roo.bootstrap.TimeField.content,
20193                     Roo.bootstrap.TimeField.footer
20194                     ]
20195                 }
20196                 ]
20197             }
20198         ]
20199     }
20200 });
20201
20202  
20203
20204  /*
20205  * - LGPL
20206  *
20207  * MonthField
20208  * 
20209  */
20210
20211 /**
20212  * @class Roo.bootstrap.MonthField
20213  * @extends Roo.bootstrap.Input
20214  * Bootstrap MonthField class
20215  * 
20216  * @cfg {String} language default en
20217  * 
20218  * @constructor
20219  * Create a new MonthField
20220  * @param {Object} config The config object
20221  */
20222
20223 Roo.bootstrap.MonthField = function(config){
20224     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20225     
20226     this.addEvents({
20227         /**
20228          * @event show
20229          * Fires when this field show.
20230          * @param {Roo.bootstrap.MonthField} this
20231          * @param {Mixed} date The date value
20232          */
20233         show : true,
20234         /**
20235          * @event show
20236          * Fires when this field hide.
20237          * @param {Roo.bootstrap.MonthField} this
20238          * @param {Mixed} date The date value
20239          */
20240         hide : true,
20241         /**
20242          * @event select
20243          * Fires when select a date.
20244          * @param {Roo.bootstrap.MonthField} this
20245          * @param {String} oldvalue The old value
20246          * @param {String} newvalue The new value
20247          */
20248         select : true
20249     });
20250 };
20251
20252 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20253     
20254     onRender: function(ct, position)
20255     {
20256         
20257         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20258         
20259         this.language = this.language || 'en';
20260         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20261         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20262         
20263         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20264         this.isInline = false;
20265         this.isInput = true;
20266         this.component = this.el.select('.add-on', true).first() || false;
20267         this.component = (this.component && this.component.length === 0) ? false : this.component;
20268         this.hasInput = this.component && this.inputEL().length;
20269         
20270         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20271         
20272         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20273         
20274         this.picker().on('mousedown', this.onMousedown, this);
20275         this.picker().on('click', this.onClick, this);
20276         
20277         this.picker().addClass('datepicker-dropdown');
20278         
20279         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20280             v.setStyle('width', '189px');
20281         });
20282         
20283         this.fillMonths();
20284         
20285         this.update();
20286         
20287         if(this.isInline) {
20288             this.show();
20289         }
20290         
20291     },
20292     
20293     setValue: function(v, suppressEvent)
20294     {   
20295         var o = this.getValue();
20296         
20297         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20298         
20299         this.update();
20300
20301         if(suppressEvent !== true){
20302             this.fireEvent('select', this, o, v);
20303         }
20304         
20305     },
20306     
20307     getValue: function()
20308     {
20309         return this.value;
20310     },
20311     
20312     onClick: function(e) 
20313     {
20314         e.stopPropagation();
20315         e.preventDefault();
20316         
20317         var target = e.getTarget();
20318         
20319         if(target.nodeName.toLowerCase() === 'i'){
20320             target = Roo.get(target).dom.parentNode;
20321         }
20322         
20323         var nodeName = target.nodeName;
20324         var className = target.className;
20325         var html = target.innerHTML;
20326         
20327         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20328             return;
20329         }
20330         
20331         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20332         
20333         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20334         
20335         this.hide();
20336                         
20337     },
20338     
20339     picker : function()
20340     {
20341         return this.pickerEl;
20342     },
20343     
20344     fillMonths: function()
20345     {    
20346         var i = 0;
20347         var months = this.picker().select('>.datepicker-months td', true).first();
20348         
20349         months.dom.innerHTML = '';
20350         
20351         while (i < 12) {
20352             var month = {
20353                 tag: 'span',
20354                 cls: 'month',
20355                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20356             };
20357             
20358             months.createChild(month);
20359         }
20360         
20361     },
20362     
20363     update: function()
20364     {
20365         var _this = this;
20366         
20367         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20368             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20369         }
20370         
20371         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20372             e.removeClass('active');
20373             
20374             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20375                 e.addClass('active');
20376             }
20377         })
20378     },
20379     
20380     place: function()
20381     {
20382         if(this.isInline) {
20383             return;
20384         }
20385         
20386         this.picker().removeClass(['bottom', 'top']);
20387         
20388         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20389             /*
20390              * place to the top of element!
20391              *
20392              */
20393             
20394             this.picker().addClass('top');
20395             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20396             
20397             return;
20398         }
20399         
20400         this.picker().addClass('bottom');
20401         
20402         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20403     },
20404     
20405     onFocus : function()
20406     {
20407         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20408         this.show();
20409     },
20410     
20411     onBlur : function()
20412     {
20413         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20414         
20415         var d = this.inputEl().getValue();
20416         
20417         this.setValue(d);
20418                 
20419         this.hide();
20420     },
20421     
20422     show : function()
20423     {
20424         this.picker().show();
20425         this.picker().select('>.datepicker-months', true).first().show();
20426         this.update();
20427         this.place();
20428         
20429         this.fireEvent('show', this, this.date);
20430     },
20431     
20432     hide : function()
20433     {
20434         if(this.isInline) {
20435             return;
20436         }
20437         this.picker().hide();
20438         this.fireEvent('hide', this, this.date);
20439         
20440     },
20441     
20442     onMousedown: function(e)
20443     {
20444         e.stopPropagation();
20445         e.preventDefault();
20446     },
20447     
20448     keyup: function(e)
20449     {
20450         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20451         this.update();
20452     },
20453
20454     fireKey: function(e)
20455     {
20456         if (!this.picker().isVisible()){
20457             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20458                 this.show();
20459             }
20460             return;
20461         }
20462         
20463         var dir;
20464         
20465         switch(e.keyCode){
20466             case 27: // escape
20467                 this.hide();
20468                 e.preventDefault();
20469                 break;
20470             case 37: // left
20471             case 39: // right
20472                 dir = e.keyCode == 37 ? -1 : 1;
20473                 
20474                 this.vIndex = this.vIndex + dir;
20475                 
20476                 if(this.vIndex < 0){
20477                     this.vIndex = 0;
20478                 }
20479                 
20480                 if(this.vIndex > 11){
20481                     this.vIndex = 11;
20482                 }
20483                 
20484                 if(isNaN(this.vIndex)){
20485                     this.vIndex = 0;
20486                 }
20487                 
20488                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20489                 
20490                 break;
20491             case 38: // up
20492             case 40: // down
20493                 
20494                 dir = e.keyCode == 38 ? -1 : 1;
20495                 
20496                 this.vIndex = this.vIndex + dir * 4;
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                 break;
20512                 
20513             case 13: // enter
20514                 
20515                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20516                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20517                 }
20518                 
20519                 this.hide();
20520                 e.preventDefault();
20521                 break;
20522             case 9: // tab
20523                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20524                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20525                 }
20526                 this.hide();
20527                 break;
20528             case 16: // shift
20529             case 17: // ctrl
20530             case 18: // alt
20531                 break;
20532             default :
20533                 this.hide();
20534                 
20535         }
20536     },
20537     
20538     remove: function() 
20539     {
20540         this.picker().remove();
20541     }
20542    
20543 });
20544
20545 Roo.apply(Roo.bootstrap.MonthField,  {
20546     
20547     content : {
20548         tag: 'tbody',
20549         cn: [
20550         {
20551             tag: 'tr',
20552             cn: [
20553             {
20554                 tag: 'td',
20555                 colspan: '7'
20556             }
20557             ]
20558         }
20559         ]
20560     },
20561     
20562     dates:{
20563         en: {
20564             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20565             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20566         }
20567     }
20568 });
20569
20570 Roo.apply(Roo.bootstrap.MonthField,  {
20571   
20572     template : {
20573         tag: 'div',
20574         cls: 'datepicker dropdown-menu roo-dynamic',
20575         cn: [
20576             {
20577                 tag: 'div',
20578                 cls: 'datepicker-months',
20579                 cn: [
20580                 {
20581                     tag: 'table',
20582                     cls: 'table-condensed',
20583                     cn:[
20584                         Roo.bootstrap.DateField.content
20585                     ]
20586                 }
20587                 ]
20588             }
20589         ]
20590     }
20591 });
20592
20593  
20594
20595  
20596  /*
20597  * - LGPL
20598  *
20599  * CheckBox
20600  * 
20601  */
20602
20603 /**
20604  * @class Roo.bootstrap.CheckBox
20605  * @extends Roo.bootstrap.Input
20606  * Bootstrap CheckBox class
20607  * 
20608  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20609  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20610  * @cfg {String} boxLabel The text that appears beside the checkbox
20611  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20612  * @cfg {Boolean} checked initnal the element
20613  * @cfg {Boolean} inline inline the element (default false)
20614  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20615  * @cfg {String} tooltip label tooltip
20616  * 
20617  * @constructor
20618  * Create a new CheckBox
20619  * @param {Object} config The config object
20620  */
20621
20622 Roo.bootstrap.CheckBox = function(config){
20623     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20624    
20625     this.addEvents({
20626         /**
20627         * @event check
20628         * Fires when the element is checked or unchecked.
20629         * @param {Roo.bootstrap.CheckBox} this This input
20630         * @param {Boolean} checked The new checked value
20631         */
20632        check : true,
20633        /**
20634         * @event click
20635         * Fires when the element is click.
20636         * @param {Roo.bootstrap.CheckBox} this This input
20637         */
20638        click : true
20639     });
20640     
20641 };
20642
20643 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20644   
20645     inputType: 'checkbox',
20646     inputValue: 1,
20647     valueOff: 0,
20648     boxLabel: false,
20649     checked: false,
20650     weight : false,
20651     inline: false,
20652     tooltip : '',
20653     
20654     getAutoCreate : function()
20655     {
20656         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20657         
20658         var id = Roo.id();
20659         
20660         var cfg = {};
20661         
20662         cfg.cls = 'form-group ' + this.inputType; //input-group
20663         
20664         if(this.inline){
20665             cfg.cls += ' ' + this.inputType + '-inline';
20666         }
20667         
20668         var input =  {
20669             tag: 'input',
20670             id : id,
20671             type : this.inputType,
20672             value : this.inputValue,
20673             cls : 'roo-' + this.inputType, //'form-box',
20674             placeholder : this.placeholder || ''
20675             
20676         };
20677         
20678         if(this.inputType != 'radio'){
20679             var hidden =  {
20680                 tag: 'input',
20681                 type : 'hidden',
20682                 cls : 'roo-hidden-value',
20683                 value : this.checked ? this.inputValue : this.valueOff
20684             };
20685         }
20686         
20687             
20688         if (this.weight) { // Validity check?
20689             cfg.cls += " " + this.inputType + "-" + this.weight;
20690         }
20691         
20692         if (this.disabled) {
20693             input.disabled=true;
20694         }
20695         
20696         if(this.checked){
20697             input.checked = this.checked;
20698         }
20699         
20700         if (this.name) {
20701             
20702             input.name = this.name;
20703             
20704             if(this.inputType != 'radio'){
20705                 hidden.name = this.name;
20706                 input.name = '_hidden_' + this.name;
20707             }
20708         }
20709         
20710         if (this.size) {
20711             input.cls += ' input-' + this.size;
20712         }
20713         
20714         var settings=this;
20715         
20716         ['xs','sm','md','lg'].map(function(size){
20717             if (settings[size]) {
20718                 cfg.cls += ' col-' + size + '-' + settings[size];
20719             }
20720         });
20721         
20722         var inputblock = input;
20723          
20724         if (this.before || this.after) {
20725             
20726             inputblock = {
20727                 cls : 'input-group',
20728                 cn :  [] 
20729             };
20730             
20731             if (this.before) {
20732                 inputblock.cn.push({
20733                     tag :'span',
20734                     cls : 'input-group-addon',
20735                     html : this.before
20736                 });
20737             }
20738             
20739             inputblock.cn.push(input);
20740             
20741             if(this.inputType != 'radio'){
20742                 inputblock.cn.push(hidden);
20743             }
20744             
20745             if (this.after) {
20746                 inputblock.cn.push({
20747                     tag :'span',
20748                     cls : 'input-group-addon',
20749                     html : this.after
20750                 });
20751             }
20752             
20753         }
20754         
20755         if (align ==='left' && this.fieldLabel.length) {
20756 //                Roo.log("left and has label");
20757             cfg.cn = [
20758                 {
20759                     tag: 'label',
20760                     'for' :  id,
20761                     cls : 'control-label',
20762                     html : this.fieldLabel
20763                 },
20764                 {
20765                     cls : "", 
20766                     cn: [
20767                         inputblock
20768                     ]
20769                 }
20770             ];
20771             
20772             if(this.labelWidth > 12){
20773                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20774             }
20775             
20776             if(this.labelWidth < 13 && this.labelmd == 0){
20777                 this.labelmd = this.labelWidth;
20778             }
20779             
20780             if(this.labellg > 0){
20781                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20782                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20783             }
20784             
20785             if(this.labelmd > 0){
20786                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20787                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20788             }
20789             
20790             if(this.labelsm > 0){
20791                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20792                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20793             }
20794             
20795             if(this.labelxs > 0){
20796                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20797                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20798             }
20799             
20800         } else if ( this.fieldLabel.length) {
20801 //                Roo.log(" label");
20802                 cfg.cn = [
20803                    
20804                     {
20805                         tag: this.boxLabel ? 'span' : 'label',
20806                         'for': id,
20807                         cls: 'control-label box-input-label',
20808                         //cls : 'input-group-addon',
20809                         html : this.fieldLabel
20810                     },
20811                     
20812                     inputblock
20813                     
20814                 ];
20815
20816         } else {
20817             
20818 //                Roo.log(" no label && no align");
20819                 cfg.cn = [  inputblock ] ;
20820                 
20821                 
20822         }
20823         
20824         if(this.boxLabel){
20825              var boxLabelCfg = {
20826                 tag: 'label',
20827                 //'for': id, // box label is handled by onclick - so no for...
20828                 cls: 'box-label',
20829                 html: this.boxLabel
20830             };
20831             
20832             if(this.tooltip){
20833                 boxLabelCfg.tooltip = this.tooltip;
20834             }
20835              
20836             cfg.cn.push(boxLabelCfg);
20837         }
20838         
20839         if(this.inputType != 'radio'){
20840             cfg.cn.push(hidden);
20841         }
20842         
20843         return cfg;
20844         
20845     },
20846     
20847     /**
20848      * return the real input element.
20849      */
20850     inputEl: function ()
20851     {
20852         return this.el.select('input.roo-' + this.inputType,true).first();
20853     },
20854     hiddenEl: function ()
20855     {
20856         return this.el.select('input.roo-hidden-value',true).first();
20857     },
20858     
20859     labelEl: function()
20860     {
20861         return this.el.select('label.control-label',true).first();
20862     },
20863     /* depricated... */
20864     
20865     label: function()
20866     {
20867         return this.labelEl();
20868     },
20869     
20870     boxLabelEl: function()
20871     {
20872         return this.el.select('label.box-label',true).first();
20873     },
20874     
20875     initEvents : function()
20876     {
20877 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20878         
20879         this.inputEl().on('click', this.onClick,  this);
20880         
20881         if (this.boxLabel) { 
20882             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20883         }
20884         
20885         this.startValue = this.getValue();
20886         
20887         if(this.groupId){
20888             Roo.bootstrap.CheckBox.register(this);
20889         }
20890     },
20891     
20892     onClick : function(e)
20893     {   
20894         if(this.fireEvent('click', this, e) !== false){
20895             this.setChecked(!this.checked);
20896         }
20897         
20898     },
20899     
20900     setChecked : function(state,suppressEvent)
20901     {
20902         this.startValue = this.getValue();
20903
20904         if(this.inputType == 'radio'){
20905             
20906             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20907                 e.dom.checked = false;
20908             });
20909             
20910             this.inputEl().dom.checked = true;
20911             
20912             this.inputEl().dom.value = this.inputValue;
20913             
20914             if(suppressEvent !== true){
20915                 this.fireEvent('check', this, true);
20916             }
20917             
20918             this.validate();
20919             
20920             return;
20921         }
20922         
20923         this.checked = state;
20924         
20925         this.inputEl().dom.checked = state;
20926         
20927         
20928         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20929         
20930         if(suppressEvent !== true){
20931             this.fireEvent('check', this, state);
20932         }
20933         
20934         this.validate();
20935     },
20936     
20937     getValue : function()
20938     {
20939         if(this.inputType == 'radio'){
20940             return this.getGroupValue();
20941         }
20942         
20943         return this.hiddenEl().dom.value;
20944         
20945     },
20946     
20947     getGroupValue : function()
20948     {
20949         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20950             return '';
20951         }
20952         
20953         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20954     },
20955     
20956     setValue : function(v,suppressEvent)
20957     {
20958         if(this.inputType == 'radio'){
20959             this.setGroupValue(v, suppressEvent);
20960             return;
20961         }
20962         
20963         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20964         
20965         this.validate();
20966     },
20967     
20968     setGroupValue : function(v, suppressEvent)
20969     {
20970         this.startValue = this.getValue();
20971         
20972         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20973             e.dom.checked = false;
20974             
20975             if(e.dom.value == v){
20976                 e.dom.checked = true;
20977             }
20978         });
20979         
20980         if(suppressEvent !== true){
20981             this.fireEvent('check', this, true);
20982         }
20983
20984         this.validate();
20985         
20986         return;
20987     },
20988     
20989     validate : function()
20990     {
20991         if(this.getVisibilityEl().hasClass('hidden')){
20992             return true;
20993         }
20994         
20995         if(
20996                 this.disabled || 
20997                 (this.inputType == 'radio' && this.validateRadio()) ||
20998                 (this.inputType == 'checkbox' && this.validateCheckbox())
20999         ){
21000             this.markValid();
21001             return true;
21002         }
21003         
21004         this.markInvalid();
21005         return false;
21006     },
21007     
21008     validateRadio : function()
21009     {
21010         if(this.getVisibilityEl().hasClass('hidden')){
21011             return true;
21012         }
21013         
21014         if(this.allowBlank){
21015             return true;
21016         }
21017         
21018         var valid = false;
21019         
21020         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21021             if(!e.dom.checked){
21022                 return;
21023             }
21024             
21025             valid = true;
21026             
21027             return false;
21028         });
21029         
21030         return valid;
21031     },
21032     
21033     validateCheckbox : function()
21034     {
21035         if(!this.groupId){
21036             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21037             //return (this.getValue() == this.inputValue) ? true : false;
21038         }
21039         
21040         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21041         
21042         if(!group){
21043             return false;
21044         }
21045         
21046         var r = false;
21047         
21048         for(var i in group){
21049             if(group[i].el.isVisible(true)){
21050                 r = false;
21051                 break;
21052             }
21053             
21054             r = true;
21055         }
21056         
21057         for(var i in group){
21058             if(r){
21059                 break;
21060             }
21061             
21062             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21063         }
21064         
21065         return r;
21066     },
21067     
21068     /**
21069      * Mark this field as valid
21070      */
21071     markValid : function()
21072     {
21073         var _this = this;
21074         
21075         this.fireEvent('valid', this);
21076         
21077         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21078         
21079         if(this.groupId){
21080             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21081         }
21082         
21083         if(label){
21084             label.markValid();
21085         }
21086
21087         if(this.inputType == 'radio'){
21088             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21089                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21090                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21091             });
21092             
21093             return;
21094         }
21095
21096         if(!this.groupId){
21097             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21098             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21099             return;
21100         }
21101         
21102         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21103         
21104         if(!group){
21105             return;
21106         }
21107         
21108         for(var i in group){
21109             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21110             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21111         }
21112     },
21113     
21114      /**
21115      * Mark this field as invalid
21116      * @param {String} msg The validation message
21117      */
21118     markInvalid : function(msg)
21119     {
21120         if(this.allowBlank){
21121             return;
21122         }
21123         
21124         var _this = this;
21125         
21126         this.fireEvent('invalid', this, msg);
21127         
21128         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21129         
21130         if(this.groupId){
21131             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21132         }
21133         
21134         if(label){
21135             label.markInvalid();
21136         }
21137             
21138         if(this.inputType == 'radio'){
21139             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21140                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21141                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21142             });
21143             
21144             return;
21145         }
21146         
21147         if(!this.groupId){
21148             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21149             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21150             return;
21151         }
21152         
21153         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21154         
21155         if(!group){
21156             return;
21157         }
21158         
21159         for(var i in group){
21160             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21161             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21162         }
21163         
21164     },
21165     
21166     clearInvalid : function()
21167     {
21168         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21169         
21170         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21171         
21172         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21173         
21174         if (label && label.iconEl) {
21175             label.iconEl.removeClass(label.validClass);
21176             label.iconEl.removeClass(label.invalidClass);
21177         }
21178     },
21179     
21180     disable : function()
21181     {
21182         if(this.inputType != 'radio'){
21183             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21184             return;
21185         }
21186         
21187         var _this = this;
21188         
21189         if(this.rendered){
21190             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21191                 _this.getActionEl().addClass(this.disabledClass);
21192                 e.dom.disabled = true;
21193             });
21194         }
21195         
21196         this.disabled = true;
21197         this.fireEvent("disable", this);
21198         return this;
21199     },
21200
21201     enable : function()
21202     {
21203         if(this.inputType != 'radio'){
21204             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21205             return;
21206         }
21207         
21208         var _this = this;
21209         
21210         if(this.rendered){
21211             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21212                 _this.getActionEl().removeClass(this.disabledClass);
21213                 e.dom.disabled = false;
21214             });
21215         }
21216         
21217         this.disabled = false;
21218         this.fireEvent("enable", this);
21219         return this;
21220     },
21221     
21222     setBoxLabel : function(v)
21223     {
21224         this.boxLabel = v;
21225         
21226         if(this.rendered){
21227             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21228         }
21229     }
21230
21231 });
21232
21233 Roo.apply(Roo.bootstrap.CheckBox, {
21234     
21235     groups: {},
21236     
21237      /**
21238     * register a CheckBox Group
21239     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21240     */
21241     register : function(checkbox)
21242     {
21243         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21244             this.groups[checkbox.groupId] = {};
21245         }
21246         
21247         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21248             return;
21249         }
21250         
21251         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21252         
21253     },
21254     /**
21255     * fetch a CheckBox Group based on the group ID
21256     * @param {string} the group ID
21257     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21258     */
21259     get: function(groupId) {
21260         if (typeof(this.groups[groupId]) == 'undefined') {
21261             return false;
21262         }
21263         
21264         return this.groups[groupId] ;
21265     }
21266     
21267     
21268 });
21269 /*
21270  * - LGPL
21271  *
21272  * RadioItem
21273  * 
21274  */
21275
21276 /**
21277  * @class Roo.bootstrap.Radio
21278  * @extends Roo.bootstrap.Component
21279  * Bootstrap Radio class
21280  * @cfg {String} boxLabel - the label associated
21281  * @cfg {String} value - the value of radio
21282  * 
21283  * @constructor
21284  * Create a new Radio
21285  * @param {Object} config The config object
21286  */
21287 Roo.bootstrap.Radio = function(config){
21288     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21289     
21290 };
21291
21292 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21293     
21294     boxLabel : '',
21295     
21296     value : '',
21297     
21298     getAutoCreate : function()
21299     {
21300         var cfg = {
21301             tag : 'div',
21302             cls : 'form-group radio',
21303             cn : [
21304                 {
21305                     tag : 'label',
21306                     cls : 'box-label',
21307                     html : this.boxLabel
21308                 }
21309             ]
21310         };
21311         
21312         return cfg;
21313     },
21314     
21315     initEvents : function() 
21316     {
21317         this.parent().register(this);
21318         
21319         this.el.on('click', this.onClick, this);
21320         
21321     },
21322     
21323     onClick : function(e)
21324     {
21325         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21326             this.setChecked(true);
21327         }
21328     },
21329     
21330     setChecked : function(state, suppressEvent)
21331     {
21332         this.parent().setValue(this.value, suppressEvent);
21333         
21334     },
21335     
21336     setBoxLabel : function(v)
21337     {
21338         this.boxLabel = v;
21339         
21340         if(this.rendered){
21341             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21342         }
21343     }
21344     
21345 });
21346  
21347
21348  /*
21349  * - LGPL
21350  *
21351  * Input
21352  * 
21353  */
21354
21355 /**
21356  * @class Roo.bootstrap.SecurePass
21357  * @extends Roo.bootstrap.Input
21358  * Bootstrap SecurePass class
21359  *
21360  * 
21361  * @constructor
21362  * Create a new SecurePass
21363  * @param {Object} config The config object
21364  */
21365  
21366 Roo.bootstrap.SecurePass = function (config) {
21367     // these go here, so the translation tool can replace them..
21368     this.errors = {
21369         PwdEmpty: "Please type a password, and then retype it to confirm.",
21370         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21371         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21372         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21373         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21374         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21375         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21376         TooWeak: "Your password is Too Weak."
21377     },
21378     this.meterLabel = "Password strength:";
21379     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21380     this.meterClass = [
21381         "roo-password-meter-tooweak", 
21382         "roo-password-meter-weak", 
21383         "roo-password-meter-medium", 
21384         "roo-password-meter-strong", 
21385         "roo-password-meter-grey"
21386     ];
21387     
21388     this.errors = {};
21389     
21390     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21391 }
21392
21393 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21394     /**
21395      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21396      * {
21397      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21398      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21399      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21400      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21401      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21402      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21403      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21404      * })
21405      */
21406     // private
21407     
21408     meterWidth: 300,
21409     errorMsg :'',    
21410     errors: false,
21411     imageRoot: '/',
21412     /**
21413      * @cfg {String/Object} Label for the strength meter (defaults to
21414      * 'Password strength:')
21415      */
21416     // private
21417     meterLabel: '',
21418     /**
21419      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21420      * ['Weak', 'Medium', 'Strong'])
21421      */
21422     // private    
21423     pwdStrengths: false,    
21424     // private
21425     strength: 0,
21426     // private
21427     _lastPwd: null,
21428     // private
21429     kCapitalLetter: 0,
21430     kSmallLetter: 1,
21431     kDigit: 2,
21432     kPunctuation: 3,
21433     
21434     insecure: false,
21435     // private
21436     initEvents: function ()
21437     {
21438         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21439
21440         if (this.el.is('input[type=password]') && Roo.isSafari) {
21441             this.el.on('keydown', this.SafariOnKeyDown, this);
21442         }
21443
21444         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21445     },
21446     // private
21447     onRender: function (ct, position)
21448     {
21449         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21450         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21451         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21452
21453         this.trigger.createChild({
21454                    cn: [
21455                     {
21456                     //id: 'PwdMeter',
21457                     tag: 'div',
21458                     cls: 'roo-password-meter-grey col-xs-12',
21459                     style: {
21460                         //width: 0,
21461                         //width: this.meterWidth + 'px'                                                
21462                         }
21463                     },
21464                     {                            
21465                          cls: 'roo-password-meter-text'                          
21466                     }
21467                 ]            
21468         });
21469
21470          
21471         if (this.hideTrigger) {
21472             this.trigger.setDisplayed(false);
21473         }
21474         this.setSize(this.width || '', this.height || '');
21475     },
21476     // private
21477     onDestroy: function ()
21478     {
21479         if (this.trigger) {
21480             this.trigger.removeAllListeners();
21481             this.trigger.remove();
21482         }
21483         if (this.wrap) {
21484             this.wrap.remove();
21485         }
21486         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21487     },
21488     // private
21489     checkStrength: function ()
21490     {
21491         var pwd = this.inputEl().getValue();
21492         if (pwd == this._lastPwd) {
21493             return;
21494         }
21495
21496         var strength;
21497         if (this.ClientSideStrongPassword(pwd)) {
21498             strength = 3;
21499         } else if (this.ClientSideMediumPassword(pwd)) {
21500             strength = 2;
21501         } else if (this.ClientSideWeakPassword(pwd)) {
21502             strength = 1;
21503         } else {
21504             strength = 0;
21505         }
21506         
21507         Roo.log('strength1: ' + strength);
21508         
21509         //var pm = this.trigger.child('div/div/div').dom;
21510         var pm = this.trigger.child('div/div');
21511         pm.removeClass(this.meterClass);
21512         pm.addClass(this.meterClass[strength]);
21513                 
21514         
21515         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21516                 
21517         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21518         
21519         this._lastPwd = pwd;
21520     },
21521     reset: function ()
21522     {
21523         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21524         
21525         this._lastPwd = '';
21526         
21527         var pm = this.trigger.child('div/div');
21528         pm.removeClass(this.meterClass);
21529         pm.addClass('roo-password-meter-grey');        
21530         
21531         
21532         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21533         
21534         pt.innerHTML = '';
21535         this.inputEl().dom.type='password';
21536     },
21537     // private
21538     validateValue: function (value)
21539     {
21540         
21541         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21542             return false;
21543         }
21544         if (value.length == 0) {
21545             if (this.allowBlank) {
21546                 this.clearInvalid();
21547                 return true;
21548             }
21549
21550             this.markInvalid(this.errors.PwdEmpty);
21551             this.errorMsg = this.errors.PwdEmpty;
21552             return false;
21553         }
21554         
21555         if(this.insecure){
21556             return true;
21557         }
21558         
21559         if ('[\x21-\x7e]*'.match(value)) {
21560             this.markInvalid(this.errors.PwdBadChar);
21561             this.errorMsg = this.errors.PwdBadChar;
21562             return false;
21563         }
21564         if (value.length < 6) {
21565             this.markInvalid(this.errors.PwdShort);
21566             this.errorMsg = this.errors.PwdShort;
21567             return false;
21568         }
21569         if (value.length > 16) {
21570             this.markInvalid(this.errors.PwdLong);
21571             this.errorMsg = this.errors.PwdLong;
21572             return false;
21573         }
21574         var strength;
21575         if (this.ClientSideStrongPassword(value)) {
21576             strength = 3;
21577         } else if (this.ClientSideMediumPassword(value)) {
21578             strength = 2;
21579         } else if (this.ClientSideWeakPassword(value)) {
21580             strength = 1;
21581         } else {
21582             strength = 0;
21583         }
21584
21585         
21586         if (strength < 2) {
21587             //this.markInvalid(this.errors.TooWeak);
21588             this.errorMsg = this.errors.TooWeak;
21589             //return false;
21590         }
21591         
21592         
21593         console.log('strength2: ' + strength);
21594         
21595         //var pm = this.trigger.child('div/div/div').dom;
21596         
21597         var pm = this.trigger.child('div/div');
21598         pm.removeClass(this.meterClass);
21599         pm.addClass(this.meterClass[strength]);
21600                 
21601         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21602                 
21603         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21604         
21605         this.errorMsg = ''; 
21606         return true;
21607     },
21608     // private
21609     CharacterSetChecks: function (type)
21610     {
21611         this.type = type;
21612         this.fResult = false;
21613     },
21614     // private
21615     isctype: function (character, type)
21616     {
21617         switch (type) {  
21618             case this.kCapitalLetter:
21619                 if (character >= 'A' && character <= 'Z') {
21620                     return true;
21621                 }
21622                 break;
21623             
21624             case this.kSmallLetter:
21625                 if (character >= 'a' && character <= 'z') {
21626                     return true;
21627                 }
21628                 break;
21629             
21630             case this.kDigit:
21631                 if (character >= '0' && character <= '9') {
21632                     return true;
21633                 }
21634                 break;
21635             
21636             case this.kPunctuation:
21637                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21638                     return true;
21639                 }
21640                 break;
21641             
21642             default:
21643                 return false;
21644         }
21645
21646     },
21647     // private
21648     IsLongEnough: function (pwd, size)
21649     {
21650         return !(pwd == null || isNaN(size) || pwd.length < size);
21651     },
21652     // private
21653     SpansEnoughCharacterSets: function (word, nb)
21654     {
21655         if (!this.IsLongEnough(word, nb))
21656         {
21657             return false;
21658         }
21659
21660         var characterSetChecks = new Array(
21661             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21662             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21663         );
21664         
21665         for (var index = 0; index < word.length; ++index) {
21666             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21667                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21668                     characterSetChecks[nCharSet].fResult = true;
21669                     break;
21670                 }
21671             }
21672         }
21673
21674         var nCharSets = 0;
21675         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21676             if (characterSetChecks[nCharSet].fResult) {
21677                 ++nCharSets;
21678             }
21679         }
21680
21681         if (nCharSets < nb) {
21682             return false;
21683         }
21684         return true;
21685     },
21686     // private
21687     ClientSideStrongPassword: function (pwd)
21688     {
21689         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21690     },
21691     // private
21692     ClientSideMediumPassword: function (pwd)
21693     {
21694         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21695     },
21696     // private
21697     ClientSideWeakPassword: function (pwd)
21698     {
21699         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21700     }
21701           
21702 })//<script type="text/javascript">
21703
21704 /*
21705  * Based  Ext JS Library 1.1.1
21706  * Copyright(c) 2006-2007, Ext JS, LLC.
21707  * LGPL
21708  *
21709  */
21710  
21711 /**
21712  * @class Roo.HtmlEditorCore
21713  * @extends Roo.Component
21714  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21715  *
21716  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21717  */
21718
21719 Roo.HtmlEditorCore = function(config){
21720     
21721     
21722     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21723     
21724     
21725     this.addEvents({
21726         /**
21727          * @event initialize
21728          * Fires when the editor is fully initialized (including the iframe)
21729          * @param {Roo.HtmlEditorCore} this
21730          */
21731         initialize: true,
21732         /**
21733          * @event activate
21734          * Fires when the editor is first receives the focus. Any insertion must wait
21735          * until after this event.
21736          * @param {Roo.HtmlEditorCore} this
21737          */
21738         activate: true,
21739          /**
21740          * @event beforesync
21741          * Fires before the textarea is updated with content from the editor iframe. Return false
21742          * to cancel the sync.
21743          * @param {Roo.HtmlEditorCore} this
21744          * @param {String} html
21745          */
21746         beforesync: true,
21747          /**
21748          * @event beforepush
21749          * Fires before the iframe editor is updated with content from the textarea. Return false
21750          * to cancel the push.
21751          * @param {Roo.HtmlEditorCore} this
21752          * @param {String} html
21753          */
21754         beforepush: true,
21755          /**
21756          * @event sync
21757          * Fires when the textarea is updated with content from the editor iframe.
21758          * @param {Roo.HtmlEditorCore} this
21759          * @param {String} html
21760          */
21761         sync: true,
21762          /**
21763          * @event push
21764          * Fires when the iframe editor is updated with content from the textarea.
21765          * @param {Roo.HtmlEditorCore} this
21766          * @param {String} html
21767          */
21768         push: true,
21769         
21770         /**
21771          * @event editorevent
21772          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21773          * @param {Roo.HtmlEditorCore} this
21774          */
21775         editorevent: true
21776         
21777     });
21778     
21779     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21780     
21781     // defaults : white / black...
21782     this.applyBlacklists();
21783     
21784     
21785     
21786 };
21787
21788
21789 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21790
21791
21792      /**
21793      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21794      */
21795     
21796     owner : false,
21797     
21798      /**
21799      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21800      *                        Roo.resizable.
21801      */
21802     resizable : false,
21803      /**
21804      * @cfg {Number} height (in pixels)
21805      */   
21806     height: 300,
21807    /**
21808      * @cfg {Number} width (in pixels)
21809      */   
21810     width: 500,
21811     
21812     /**
21813      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21814      * 
21815      */
21816     stylesheets: false,
21817     
21818     // id of frame..
21819     frameId: false,
21820     
21821     // private properties
21822     validationEvent : false,
21823     deferHeight: true,
21824     initialized : false,
21825     activated : false,
21826     sourceEditMode : false,
21827     onFocus : Roo.emptyFn,
21828     iframePad:3,
21829     hideMode:'offsets',
21830     
21831     clearUp: true,
21832     
21833     // blacklist + whitelisted elements..
21834     black: false,
21835     white: false,
21836      
21837     bodyCls : '',
21838
21839     /**
21840      * Protected method that will not generally be called directly. It
21841      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21842      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21843      */
21844     getDocMarkup : function(){
21845         // body styles..
21846         var st = '';
21847         
21848         // inherit styels from page...?? 
21849         if (this.stylesheets === false) {
21850             
21851             Roo.get(document.head).select('style').each(function(node) {
21852                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21853             });
21854             
21855             Roo.get(document.head).select('link').each(function(node) { 
21856                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21857             });
21858             
21859         } else if (!this.stylesheets.length) {
21860                 // simple..
21861                 st = '<style type="text/css">' +
21862                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21863                    '</style>';
21864         } else { 
21865             st = '<style type="text/css">' +
21866                     this.stylesheets +
21867                 '</style>';
21868         }
21869         
21870         st +=  '<style type="text/css">' +
21871             'IMG { cursor: pointer } ' +
21872         '</style>';
21873
21874         var cls = 'roo-htmleditor-body';
21875         
21876         if(this.bodyCls.length){
21877             cls += ' ' + this.bodyCls;
21878         }
21879         
21880         return '<html><head>' + st  +
21881             //<style type="text/css">' +
21882             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21883             //'</style>' +
21884             ' </head><body class="' +  cls + '"></body></html>';
21885     },
21886
21887     // private
21888     onRender : function(ct, position)
21889     {
21890         var _t = this;
21891         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21892         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21893         
21894         
21895         this.el.dom.style.border = '0 none';
21896         this.el.dom.setAttribute('tabIndex', -1);
21897         this.el.addClass('x-hidden hide');
21898         
21899         
21900         
21901         if(Roo.isIE){ // fix IE 1px bogus margin
21902             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21903         }
21904        
21905         
21906         this.frameId = Roo.id();
21907         
21908          
21909         
21910         var iframe = this.owner.wrap.createChild({
21911             tag: 'iframe',
21912             cls: 'form-control', // bootstrap..
21913             id: this.frameId,
21914             name: this.frameId,
21915             frameBorder : 'no',
21916             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21917         }, this.el
21918         );
21919         
21920         
21921         this.iframe = iframe.dom;
21922
21923          this.assignDocWin();
21924         
21925         this.doc.designMode = 'on';
21926        
21927         this.doc.open();
21928         this.doc.write(this.getDocMarkup());
21929         this.doc.close();
21930
21931         
21932         var task = { // must defer to wait for browser to be ready
21933             run : function(){
21934                 //console.log("run task?" + this.doc.readyState);
21935                 this.assignDocWin();
21936                 if(this.doc.body || this.doc.readyState == 'complete'){
21937                     try {
21938                         this.doc.designMode="on";
21939                     } catch (e) {
21940                         return;
21941                     }
21942                     Roo.TaskMgr.stop(task);
21943                     this.initEditor.defer(10, this);
21944                 }
21945             },
21946             interval : 10,
21947             duration: 10000,
21948             scope: this
21949         };
21950         Roo.TaskMgr.start(task);
21951
21952     },
21953
21954     // private
21955     onResize : function(w, h)
21956     {
21957          Roo.log('resize: ' +w + ',' + h );
21958         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21959         if(!this.iframe){
21960             return;
21961         }
21962         if(typeof w == 'number'){
21963             
21964             this.iframe.style.width = w + 'px';
21965         }
21966         if(typeof h == 'number'){
21967             
21968             this.iframe.style.height = h + 'px';
21969             if(this.doc){
21970                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21971             }
21972         }
21973         
21974     },
21975
21976     /**
21977      * Toggles the editor between standard and source edit mode.
21978      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21979      */
21980     toggleSourceEdit : function(sourceEditMode){
21981         
21982         this.sourceEditMode = sourceEditMode === true;
21983         
21984         if(this.sourceEditMode){
21985  
21986             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21987             
21988         }else{
21989             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21990             //this.iframe.className = '';
21991             this.deferFocus();
21992         }
21993         //this.setSize(this.owner.wrap.getSize());
21994         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21995     },
21996
21997     
21998   
21999
22000     /**
22001      * Protected method that will not generally be called directly. If you need/want
22002      * custom HTML cleanup, this is the method you should override.
22003      * @param {String} html The HTML to be cleaned
22004      * return {String} The cleaned HTML
22005      */
22006     cleanHtml : function(html){
22007         html = String(html);
22008         if(html.length > 5){
22009             if(Roo.isSafari){ // strip safari nonsense
22010                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22011             }
22012         }
22013         if(html == '&nbsp;'){
22014             html = '';
22015         }
22016         return html;
22017     },
22018
22019     /**
22020      * HTML Editor -> Textarea
22021      * Protected method that will not generally be called directly. Syncs the contents
22022      * of the editor iframe with the textarea.
22023      */
22024     syncValue : function(){
22025         if(this.initialized){
22026             var bd = (this.doc.body || this.doc.documentElement);
22027             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22028             var html = bd.innerHTML;
22029             if(Roo.isSafari){
22030                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22031                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22032                 if(m && m[1]){
22033                     html = '<div style="'+m[0]+'">' + html + '</div>';
22034                 }
22035             }
22036             html = this.cleanHtml(html);
22037             // fix up the special chars.. normaly like back quotes in word...
22038             // however we do not want to do this with chinese..
22039             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22040                 var cc = b.charCodeAt();
22041                 if (
22042                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22043                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22044                     (cc >= 0xf900 && cc < 0xfb00 )
22045                 ) {
22046                         return b;
22047                 }
22048                 return "&#"+cc+";" 
22049             });
22050             if(this.owner.fireEvent('beforesync', this, html) !== false){
22051                 this.el.dom.value = html;
22052                 this.owner.fireEvent('sync', this, html);
22053             }
22054         }
22055     },
22056
22057     /**
22058      * Protected method that will not generally be called directly. Pushes the value of the textarea
22059      * into the iframe editor.
22060      */
22061     pushValue : function(){
22062         if(this.initialized){
22063             var v = this.el.dom.value.trim();
22064             
22065 //            if(v.length < 1){
22066 //                v = '&#160;';
22067 //            }
22068             
22069             if(this.owner.fireEvent('beforepush', this, v) !== false){
22070                 var d = (this.doc.body || this.doc.documentElement);
22071                 d.innerHTML = v;
22072                 this.cleanUpPaste();
22073                 this.el.dom.value = d.innerHTML;
22074                 this.owner.fireEvent('push', this, v);
22075             }
22076         }
22077     },
22078
22079     // private
22080     deferFocus : function(){
22081         this.focus.defer(10, this);
22082     },
22083
22084     // doc'ed in Field
22085     focus : function(){
22086         if(this.win && !this.sourceEditMode){
22087             this.win.focus();
22088         }else{
22089             this.el.focus();
22090         }
22091     },
22092     
22093     assignDocWin: function()
22094     {
22095         var iframe = this.iframe;
22096         
22097          if(Roo.isIE){
22098             this.doc = iframe.contentWindow.document;
22099             this.win = iframe.contentWindow;
22100         } else {
22101 //            if (!Roo.get(this.frameId)) {
22102 //                return;
22103 //            }
22104 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22105 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22106             
22107             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22108                 return;
22109             }
22110             
22111             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22112             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22113         }
22114     },
22115     
22116     // private
22117     initEditor : function(){
22118         //console.log("INIT EDITOR");
22119         this.assignDocWin();
22120         
22121         
22122         
22123         this.doc.designMode="on";
22124         this.doc.open();
22125         this.doc.write(this.getDocMarkup());
22126         this.doc.close();
22127         
22128         var dbody = (this.doc.body || this.doc.documentElement);
22129         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22130         // this copies styles from the containing element into thsi one..
22131         // not sure why we need all of this..
22132         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22133         
22134         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22135         //ss['background-attachment'] = 'fixed'; // w3c
22136         dbody.bgProperties = 'fixed'; // ie
22137         //Roo.DomHelper.applyStyles(dbody, ss);
22138         Roo.EventManager.on(this.doc, {
22139             //'mousedown': this.onEditorEvent,
22140             'mouseup': this.onEditorEvent,
22141             'dblclick': this.onEditorEvent,
22142             'click': this.onEditorEvent,
22143             'keyup': this.onEditorEvent,
22144             buffer:100,
22145             scope: this
22146         });
22147         if(Roo.isGecko){
22148             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22149         }
22150         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22151             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22152         }
22153         this.initialized = true;
22154
22155         this.owner.fireEvent('initialize', this);
22156         this.pushValue();
22157     },
22158
22159     // private
22160     onDestroy : function(){
22161         
22162         
22163         
22164         if(this.rendered){
22165             
22166             //for (var i =0; i < this.toolbars.length;i++) {
22167             //    // fixme - ask toolbars for heights?
22168             //    this.toolbars[i].onDestroy();
22169            // }
22170             
22171             //this.wrap.dom.innerHTML = '';
22172             //this.wrap.remove();
22173         }
22174     },
22175
22176     // private
22177     onFirstFocus : function(){
22178         
22179         this.assignDocWin();
22180         
22181         
22182         this.activated = true;
22183          
22184     
22185         if(Roo.isGecko){ // prevent silly gecko errors
22186             this.win.focus();
22187             var s = this.win.getSelection();
22188             if(!s.focusNode || s.focusNode.nodeType != 3){
22189                 var r = s.getRangeAt(0);
22190                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22191                 r.collapse(true);
22192                 this.deferFocus();
22193             }
22194             try{
22195                 this.execCmd('useCSS', true);
22196                 this.execCmd('styleWithCSS', false);
22197             }catch(e){}
22198         }
22199         this.owner.fireEvent('activate', this);
22200     },
22201
22202     // private
22203     adjustFont: function(btn){
22204         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22205         //if(Roo.isSafari){ // safari
22206         //    adjust *= 2;
22207        // }
22208         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22209         if(Roo.isSafari){ // safari
22210             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22211             v =  (v < 10) ? 10 : v;
22212             v =  (v > 48) ? 48 : v;
22213             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22214             
22215         }
22216         
22217         
22218         v = Math.max(1, v+adjust);
22219         
22220         this.execCmd('FontSize', v  );
22221     },
22222
22223     onEditorEvent : function(e)
22224     {
22225         this.owner.fireEvent('editorevent', this, e);
22226       //  this.updateToolbar();
22227         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22228     },
22229
22230     insertTag : function(tg)
22231     {
22232         // could be a bit smarter... -> wrap the current selected tRoo..
22233         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22234             
22235             range = this.createRange(this.getSelection());
22236             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22237             wrappingNode.appendChild(range.extractContents());
22238             range.insertNode(wrappingNode);
22239
22240             return;
22241             
22242             
22243             
22244         }
22245         this.execCmd("formatblock",   tg);
22246         
22247     },
22248     
22249     insertText : function(txt)
22250     {
22251         
22252         
22253         var range = this.createRange();
22254         range.deleteContents();
22255                //alert(Sender.getAttribute('label'));
22256                
22257         range.insertNode(this.doc.createTextNode(txt));
22258     } ,
22259     
22260      
22261
22262     /**
22263      * Executes a Midas editor command on the editor document and performs necessary focus and
22264      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22265      * @param {String} cmd The Midas command
22266      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22267      */
22268     relayCmd : function(cmd, value){
22269         this.win.focus();
22270         this.execCmd(cmd, value);
22271         this.owner.fireEvent('editorevent', this);
22272         //this.updateToolbar();
22273         this.owner.deferFocus();
22274     },
22275
22276     /**
22277      * Executes a Midas editor command directly on the editor document.
22278      * For visual commands, you should use {@link #relayCmd} instead.
22279      * <b>This should only be called after the editor is initialized.</b>
22280      * @param {String} cmd The Midas command
22281      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22282      */
22283     execCmd : function(cmd, value){
22284         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22285         this.syncValue();
22286     },
22287  
22288  
22289    
22290     /**
22291      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22292      * to insert tRoo.
22293      * @param {String} text | dom node.. 
22294      */
22295     insertAtCursor : function(text)
22296     {
22297         
22298         if(!this.activated){
22299             return;
22300         }
22301         /*
22302         if(Roo.isIE){
22303             this.win.focus();
22304             var r = this.doc.selection.createRange();
22305             if(r){
22306                 r.collapse(true);
22307                 r.pasteHTML(text);
22308                 this.syncValue();
22309                 this.deferFocus();
22310             
22311             }
22312             return;
22313         }
22314         */
22315         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22316             this.win.focus();
22317             
22318             
22319             // from jquery ui (MIT licenced)
22320             var range, node;
22321             var win = this.win;
22322             
22323             if (win.getSelection && win.getSelection().getRangeAt) {
22324                 range = win.getSelection().getRangeAt(0);
22325                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22326                 range.insertNode(node);
22327             } else if (win.document.selection && win.document.selection.createRange) {
22328                 // no firefox support
22329                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22330                 win.document.selection.createRange().pasteHTML(txt);
22331             } else {
22332                 // no firefox support
22333                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22334                 this.execCmd('InsertHTML', txt);
22335             } 
22336             
22337             this.syncValue();
22338             
22339             this.deferFocus();
22340         }
22341     },
22342  // private
22343     mozKeyPress : function(e){
22344         if(e.ctrlKey){
22345             var c = e.getCharCode(), cmd;
22346           
22347             if(c > 0){
22348                 c = String.fromCharCode(c).toLowerCase();
22349                 switch(c){
22350                     case 'b':
22351                         cmd = 'bold';
22352                         break;
22353                     case 'i':
22354                         cmd = 'italic';
22355                         break;
22356                     
22357                     case 'u':
22358                         cmd = 'underline';
22359                         break;
22360                     
22361                     case 'v':
22362                         this.cleanUpPaste.defer(100, this);
22363                         return;
22364                         
22365                 }
22366                 if(cmd){
22367                     this.win.focus();
22368                     this.execCmd(cmd);
22369                     this.deferFocus();
22370                     e.preventDefault();
22371                 }
22372                 
22373             }
22374         }
22375     },
22376
22377     // private
22378     fixKeys : function(){ // load time branching for fastest keydown performance
22379         if(Roo.isIE){
22380             return function(e){
22381                 var k = e.getKey(), r;
22382                 if(k == e.TAB){
22383                     e.stopEvent();
22384                     r = this.doc.selection.createRange();
22385                     if(r){
22386                         r.collapse(true);
22387                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22388                         this.deferFocus();
22389                     }
22390                     return;
22391                 }
22392                 
22393                 if(k == e.ENTER){
22394                     r = this.doc.selection.createRange();
22395                     if(r){
22396                         var target = r.parentElement();
22397                         if(!target || target.tagName.toLowerCase() != 'li'){
22398                             e.stopEvent();
22399                             r.pasteHTML('<br />');
22400                             r.collapse(false);
22401                             r.select();
22402                         }
22403                     }
22404                 }
22405                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22406                     this.cleanUpPaste.defer(100, this);
22407                     return;
22408                 }
22409                 
22410                 
22411             };
22412         }else if(Roo.isOpera){
22413             return function(e){
22414                 var k = e.getKey();
22415                 if(k == e.TAB){
22416                     e.stopEvent();
22417                     this.win.focus();
22418                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22419                     this.deferFocus();
22420                 }
22421                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22422                     this.cleanUpPaste.defer(100, this);
22423                     return;
22424                 }
22425                 
22426             };
22427         }else if(Roo.isSafari){
22428             return function(e){
22429                 var k = e.getKey();
22430                 
22431                 if(k == e.TAB){
22432                     e.stopEvent();
22433                     this.execCmd('InsertText','\t');
22434                     this.deferFocus();
22435                     return;
22436                 }
22437                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22438                     this.cleanUpPaste.defer(100, this);
22439                     return;
22440                 }
22441                 
22442              };
22443         }
22444     }(),
22445     
22446     getAllAncestors: function()
22447     {
22448         var p = this.getSelectedNode();
22449         var a = [];
22450         if (!p) {
22451             a.push(p); // push blank onto stack..
22452             p = this.getParentElement();
22453         }
22454         
22455         
22456         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22457             a.push(p);
22458             p = p.parentNode;
22459         }
22460         a.push(this.doc.body);
22461         return a;
22462     },
22463     lastSel : false,
22464     lastSelNode : false,
22465     
22466     
22467     getSelection : function() 
22468     {
22469         this.assignDocWin();
22470         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22471     },
22472     
22473     getSelectedNode: function() 
22474     {
22475         // this may only work on Gecko!!!
22476         
22477         // should we cache this!!!!
22478         
22479         
22480         
22481          
22482         var range = this.createRange(this.getSelection()).cloneRange();
22483         
22484         if (Roo.isIE) {
22485             var parent = range.parentElement();
22486             while (true) {
22487                 var testRange = range.duplicate();
22488                 testRange.moveToElementText(parent);
22489                 if (testRange.inRange(range)) {
22490                     break;
22491                 }
22492                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22493                     break;
22494                 }
22495                 parent = parent.parentElement;
22496             }
22497             return parent;
22498         }
22499         
22500         // is ancestor a text element.
22501         var ac =  range.commonAncestorContainer;
22502         if (ac.nodeType == 3) {
22503             ac = ac.parentNode;
22504         }
22505         
22506         var ar = ac.childNodes;
22507          
22508         var nodes = [];
22509         var other_nodes = [];
22510         var has_other_nodes = false;
22511         for (var i=0;i<ar.length;i++) {
22512             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22513                 continue;
22514             }
22515             // fullly contained node.
22516             
22517             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22518                 nodes.push(ar[i]);
22519                 continue;
22520             }
22521             
22522             // probably selected..
22523             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22524                 other_nodes.push(ar[i]);
22525                 continue;
22526             }
22527             // outer..
22528             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22529                 continue;
22530             }
22531             
22532             
22533             has_other_nodes = true;
22534         }
22535         if (!nodes.length && other_nodes.length) {
22536             nodes= other_nodes;
22537         }
22538         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22539             return false;
22540         }
22541         
22542         return nodes[0];
22543     },
22544     createRange: function(sel)
22545     {
22546         // this has strange effects when using with 
22547         // top toolbar - not sure if it's a great idea.
22548         //this.editor.contentWindow.focus();
22549         if (typeof sel != "undefined") {
22550             try {
22551                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22552             } catch(e) {
22553                 return this.doc.createRange();
22554             }
22555         } else {
22556             return this.doc.createRange();
22557         }
22558     },
22559     getParentElement: function()
22560     {
22561         
22562         this.assignDocWin();
22563         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22564         
22565         var range = this.createRange(sel);
22566          
22567         try {
22568             var p = range.commonAncestorContainer;
22569             while (p.nodeType == 3) { // text node
22570                 p = p.parentNode;
22571             }
22572             return p;
22573         } catch (e) {
22574             return null;
22575         }
22576     
22577     },
22578     /***
22579      *
22580      * Range intersection.. the hard stuff...
22581      *  '-1' = before
22582      *  '0' = hits..
22583      *  '1' = after.
22584      *         [ -- selected range --- ]
22585      *   [fail]                        [fail]
22586      *
22587      *    basically..
22588      *      if end is before start or  hits it. fail.
22589      *      if start is after end or hits it fail.
22590      *
22591      *   if either hits (but other is outside. - then it's not 
22592      *   
22593      *    
22594      **/
22595     
22596     
22597     // @see http://www.thismuchiknow.co.uk/?p=64.
22598     rangeIntersectsNode : function(range, node)
22599     {
22600         var nodeRange = node.ownerDocument.createRange();
22601         try {
22602             nodeRange.selectNode(node);
22603         } catch (e) {
22604             nodeRange.selectNodeContents(node);
22605         }
22606     
22607         var rangeStartRange = range.cloneRange();
22608         rangeStartRange.collapse(true);
22609     
22610         var rangeEndRange = range.cloneRange();
22611         rangeEndRange.collapse(false);
22612     
22613         var nodeStartRange = nodeRange.cloneRange();
22614         nodeStartRange.collapse(true);
22615     
22616         var nodeEndRange = nodeRange.cloneRange();
22617         nodeEndRange.collapse(false);
22618     
22619         return rangeStartRange.compareBoundaryPoints(
22620                  Range.START_TO_START, nodeEndRange) == -1 &&
22621                rangeEndRange.compareBoundaryPoints(
22622                  Range.START_TO_START, nodeStartRange) == 1;
22623         
22624          
22625     },
22626     rangeCompareNode : function(range, node)
22627     {
22628         var nodeRange = node.ownerDocument.createRange();
22629         try {
22630             nodeRange.selectNode(node);
22631         } catch (e) {
22632             nodeRange.selectNodeContents(node);
22633         }
22634         
22635         
22636         range.collapse(true);
22637     
22638         nodeRange.collapse(true);
22639      
22640         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22641         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22642          
22643         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22644         
22645         var nodeIsBefore   =  ss == 1;
22646         var nodeIsAfter    = ee == -1;
22647         
22648         if (nodeIsBefore && nodeIsAfter) {
22649             return 0; // outer
22650         }
22651         if (!nodeIsBefore && nodeIsAfter) {
22652             return 1; //right trailed.
22653         }
22654         
22655         if (nodeIsBefore && !nodeIsAfter) {
22656             return 2;  // left trailed.
22657         }
22658         // fully contined.
22659         return 3;
22660     },
22661
22662     // private? - in a new class?
22663     cleanUpPaste :  function()
22664     {
22665         // cleans up the whole document..
22666         Roo.log('cleanuppaste');
22667         
22668         this.cleanUpChildren(this.doc.body);
22669         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22670         if (clean != this.doc.body.innerHTML) {
22671             this.doc.body.innerHTML = clean;
22672         }
22673         
22674     },
22675     
22676     cleanWordChars : function(input) {// change the chars to hex code
22677         var he = Roo.HtmlEditorCore;
22678         
22679         var output = input;
22680         Roo.each(he.swapCodes, function(sw) { 
22681             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22682             
22683             output = output.replace(swapper, sw[1]);
22684         });
22685         
22686         return output;
22687     },
22688     
22689     
22690     cleanUpChildren : function (n)
22691     {
22692         if (!n.childNodes.length) {
22693             return;
22694         }
22695         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22696            this.cleanUpChild(n.childNodes[i]);
22697         }
22698     },
22699     
22700     
22701         
22702     
22703     cleanUpChild : function (node)
22704     {
22705         var ed = this;
22706         //console.log(node);
22707         if (node.nodeName == "#text") {
22708             // clean up silly Windows -- stuff?
22709             return; 
22710         }
22711         if (node.nodeName == "#comment") {
22712             node.parentNode.removeChild(node);
22713             // clean up silly Windows -- stuff?
22714             return; 
22715         }
22716         var lcname = node.tagName.toLowerCase();
22717         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22718         // whitelist of tags..
22719         
22720         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22721             // remove node.
22722             node.parentNode.removeChild(node);
22723             return;
22724             
22725         }
22726         
22727         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22728         
22729         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22730         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22731         
22732         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22733         //    remove_keep_children = true;
22734         //}
22735         
22736         if (remove_keep_children) {
22737             this.cleanUpChildren(node);
22738             // inserts everything just before this node...
22739             while (node.childNodes.length) {
22740                 var cn = node.childNodes[0];
22741                 node.removeChild(cn);
22742                 node.parentNode.insertBefore(cn, node);
22743             }
22744             node.parentNode.removeChild(node);
22745             return;
22746         }
22747         
22748         if (!node.attributes || !node.attributes.length) {
22749             this.cleanUpChildren(node);
22750             return;
22751         }
22752         
22753         function cleanAttr(n,v)
22754         {
22755             
22756             if (v.match(/^\./) || v.match(/^\//)) {
22757                 return;
22758             }
22759             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22760                 return;
22761             }
22762             if (v.match(/^#/)) {
22763                 return;
22764             }
22765 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22766             node.removeAttribute(n);
22767             
22768         }
22769         
22770         var cwhite = this.cwhite;
22771         var cblack = this.cblack;
22772             
22773         function cleanStyle(n,v)
22774         {
22775             if (v.match(/expression/)) { //XSS?? should we even bother..
22776                 node.removeAttribute(n);
22777                 return;
22778             }
22779             
22780             var parts = v.split(/;/);
22781             var clean = [];
22782             
22783             Roo.each(parts, function(p) {
22784                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22785                 if (!p.length) {
22786                     return true;
22787                 }
22788                 var l = p.split(':').shift().replace(/\s+/g,'');
22789                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22790                 
22791                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22792 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22793                     //node.removeAttribute(n);
22794                     return true;
22795                 }
22796                 //Roo.log()
22797                 // only allow 'c whitelisted system attributes'
22798                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22799 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22800                     //node.removeAttribute(n);
22801                     return true;
22802                 }
22803                 
22804                 
22805                  
22806                 
22807                 clean.push(p);
22808                 return true;
22809             });
22810             if (clean.length) { 
22811                 node.setAttribute(n, clean.join(';'));
22812             } else {
22813                 node.removeAttribute(n);
22814             }
22815             
22816         }
22817         
22818         
22819         for (var i = node.attributes.length-1; i > -1 ; i--) {
22820             var a = node.attributes[i];
22821             //console.log(a);
22822             
22823             if (a.name.toLowerCase().substr(0,2)=='on')  {
22824                 node.removeAttribute(a.name);
22825                 continue;
22826             }
22827             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22828                 node.removeAttribute(a.name);
22829                 continue;
22830             }
22831             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22832                 cleanAttr(a.name,a.value); // fixme..
22833                 continue;
22834             }
22835             if (a.name == 'style') {
22836                 cleanStyle(a.name,a.value);
22837                 continue;
22838             }
22839             /// clean up MS crap..
22840             // tecnically this should be a list of valid class'es..
22841             
22842             
22843             if (a.name == 'class') {
22844                 if (a.value.match(/^Mso/)) {
22845                     node.className = '';
22846                 }
22847                 
22848                 if (a.value.match(/^body$/)) {
22849                     node.className = '';
22850                 }
22851                 continue;
22852             }
22853             
22854             // style cleanup!?
22855             // class cleanup?
22856             
22857         }
22858         
22859         
22860         this.cleanUpChildren(node);
22861         
22862         
22863     },
22864     
22865     /**
22866      * Clean up MS wordisms...
22867      */
22868     cleanWord : function(node)
22869     {
22870         
22871         
22872         if (!node) {
22873             this.cleanWord(this.doc.body);
22874             return;
22875         }
22876         if (node.nodeName == "#text") {
22877             // clean up silly Windows -- stuff?
22878             return; 
22879         }
22880         if (node.nodeName == "#comment") {
22881             node.parentNode.removeChild(node);
22882             // clean up silly Windows -- stuff?
22883             return; 
22884         }
22885         
22886         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22887             node.parentNode.removeChild(node);
22888             return;
22889         }
22890         
22891         // remove - but keep children..
22892         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22893             while (node.childNodes.length) {
22894                 var cn = node.childNodes[0];
22895                 node.removeChild(cn);
22896                 node.parentNode.insertBefore(cn, node);
22897             }
22898             node.parentNode.removeChild(node);
22899             this.iterateChildren(node, this.cleanWord);
22900             return;
22901         }
22902         // clean styles
22903         if (node.className.length) {
22904             
22905             var cn = node.className.split(/\W+/);
22906             var cna = [];
22907             Roo.each(cn, function(cls) {
22908                 if (cls.match(/Mso[a-zA-Z]+/)) {
22909                     return;
22910                 }
22911                 cna.push(cls);
22912             });
22913             node.className = cna.length ? cna.join(' ') : '';
22914             if (!cna.length) {
22915                 node.removeAttribute("class");
22916             }
22917         }
22918         
22919         if (node.hasAttribute("lang")) {
22920             node.removeAttribute("lang");
22921         }
22922         
22923         if (node.hasAttribute("style")) {
22924             
22925             var styles = node.getAttribute("style").split(";");
22926             var nstyle = [];
22927             Roo.each(styles, function(s) {
22928                 if (!s.match(/:/)) {
22929                     return;
22930                 }
22931                 var kv = s.split(":");
22932                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22933                     return;
22934                 }
22935                 // what ever is left... we allow.
22936                 nstyle.push(s);
22937             });
22938             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22939             if (!nstyle.length) {
22940                 node.removeAttribute('style');
22941             }
22942         }
22943         this.iterateChildren(node, this.cleanWord);
22944         
22945         
22946         
22947     },
22948     /**
22949      * iterateChildren of a Node, calling fn each time, using this as the scole..
22950      * @param {DomNode} node node to iterate children of.
22951      * @param {Function} fn method of this class to call on each item.
22952      */
22953     iterateChildren : function(node, fn)
22954     {
22955         if (!node.childNodes.length) {
22956                 return;
22957         }
22958         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22959            fn.call(this, node.childNodes[i])
22960         }
22961     },
22962     
22963     
22964     /**
22965      * cleanTableWidths.
22966      *
22967      * Quite often pasting from word etc.. results in tables with column and widths.
22968      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22969      *
22970      */
22971     cleanTableWidths : function(node)
22972     {
22973          
22974          
22975         if (!node) {
22976             this.cleanTableWidths(this.doc.body);
22977             return;
22978         }
22979         
22980         // ignore list...
22981         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22982             return; 
22983         }
22984         Roo.log(node.tagName);
22985         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22986             this.iterateChildren(node, this.cleanTableWidths);
22987             return;
22988         }
22989         if (node.hasAttribute('width')) {
22990             node.removeAttribute('width');
22991         }
22992         
22993          
22994         if (node.hasAttribute("style")) {
22995             // pretty basic...
22996             
22997             var styles = node.getAttribute("style").split(";");
22998             var nstyle = [];
22999             Roo.each(styles, function(s) {
23000                 if (!s.match(/:/)) {
23001                     return;
23002                 }
23003                 var kv = s.split(":");
23004                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23005                     return;
23006                 }
23007                 // what ever is left... we allow.
23008                 nstyle.push(s);
23009             });
23010             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23011             if (!nstyle.length) {
23012                 node.removeAttribute('style');
23013             }
23014         }
23015         
23016         this.iterateChildren(node, this.cleanTableWidths);
23017         
23018         
23019     },
23020     
23021     
23022     
23023     
23024     domToHTML : function(currentElement, depth, nopadtext) {
23025         
23026         depth = depth || 0;
23027         nopadtext = nopadtext || false;
23028     
23029         if (!currentElement) {
23030             return this.domToHTML(this.doc.body);
23031         }
23032         
23033         //Roo.log(currentElement);
23034         var j;
23035         var allText = false;
23036         var nodeName = currentElement.nodeName;
23037         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23038         
23039         if  (nodeName == '#text') {
23040             
23041             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23042         }
23043         
23044         
23045         var ret = '';
23046         if (nodeName != 'BODY') {
23047              
23048             var i = 0;
23049             // Prints the node tagName, such as <A>, <IMG>, etc
23050             if (tagName) {
23051                 var attr = [];
23052                 for(i = 0; i < currentElement.attributes.length;i++) {
23053                     // quoting?
23054                     var aname = currentElement.attributes.item(i).name;
23055                     if (!currentElement.attributes.item(i).value.length) {
23056                         continue;
23057                     }
23058                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23059                 }
23060                 
23061                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23062             } 
23063             else {
23064                 
23065                 // eack
23066             }
23067         } else {
23068             tagName = false;
23069         }
23070         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23071             return ret;
23072         }
23073         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23074             nopadtext = true;
23075         }
23076         
23077         
23078         // Traverse the tree
23079         i = 0;
23080         var currentElementChild = currentElement.childNodes.item(i);
23081         var allText = true;
23082         var innerHTML  = '';
23083         lastnode = '';
23084         while (currentElementChild) {
23085             // Formatting code (indent the tree so it looks nice on the screen)
23086             var nopad = nopadtext;
23087             if (lastnode == 'SPAN') {
23088                 nopad  = true;
23089             }
23090             // text
23091             if  (currentElementChild.nodeName == '#text') {
23092                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23093                 toadd = nopadtext ? toadd : toadd.trim();
23094                 if (!nopad && toadd.length > 80) {
23095                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23096                 }
23097                 innerHTML  += toadd;
23098                 
23099                 i++;
23100                 currentElementChild = currentElement.childNodes.item(i);
23101                 lastNode = '';
23102                 continue;
23103             }
23104             allText = false;
23105             
23106             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23107                 
23108             // Recursively traverse the tree structure of the child node
23109             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23110             lastnode = currentElementChild.nodeName;
23111             i++;
23112             currentElementChild=currentElement.childNodes.item(i);
23113         }
23114         
23115         ret += innerHTML;
23116         
23117         if (!allText) {
23118                 // The remaining code is mostly for formatting the tree
23119             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23120         }
23121         
23122         
23123         if (tagName) {
23124             ret+= "</"+tagName+">";
23125         }
23126         return ret;
23127         
23128     },
23129         
23130     applyBlacklists : function()
23131     {
23132         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23133         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23134         
23135         this.white = [];
23136         this.black = [];
23137         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23138             if (b.indexOf(tag) > -1) {
23139                 return;
23140             }
23141             this.white.push(tag);
23142             
23143         }, this);
23144         
23145         Roo.each(w, function(tag) {
23146             if (b.indexOf(tag) > -1) {
23147                 return;
23148             }
23149             if (this.white.indexOf(tag) > -1) {
23150                 return;
23151             }
23152             this.white.push(tag);
23153             
23154         }, this);
23155         
23156         
23157         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23158             if (w.indexOf(tag) > -1) {
23159                 return;
23160             }
23161             this.black.push(tag);
23162             
23163         }, this);
23164         
23165         Roo.each(b, function(tag) {
23166             if (w.indexOf(tag) > -1) {
23167                 return;
23168             }
23169             if (this.black.indexOf(tag) > -1) {
23170                 return;
23171             }
23172             this.black.push(tag);
23173             
23174         }, this);
23175         
23176         
23177         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23178         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23179         
23180         this.cwhite = [];
23181         this.cblack = [];
23182         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23183             if (b.indexOf(tag) > -1) {
23184                 return;
23185             }
23186             this.cwhite.push(tag);
23187             
23188         }, this);
23189         
23190         Roo.each(w, function(tag) {
23191             if (b.indexOf(tag) > -1) {
23192                 return;
23193             }
23194             if (this.cwhite.indexOf(tag) > -1) {
23195                 return;
23196             }
23197             this.cwhite.push(tag);
23198             
23199         }, this);
23200         
23201         
23202         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23203             if (w.indexOf(tag) > -1) {
23204                 return;
23205             }
23206             this.cblack.push(tag);
23207             
23208         }, this);
23209         
23210         Roo.each(b, function(tag) {
23211             if (w.indexOf(tag) > -1) {
23212                 return;
23213             }
23214             if (this.cblack.indexOf(tag) > -1) {
23215                 return;
23216             }
23217             this.cblack.push(tag);
23218             
23219         }, this);
23220     },
23221     
23222     setStylesheets : function(stylesheets)
23223     {
23224         if(typeof(stylesheets) == 'string'){
23225             Roo.get(this.iframe.contentDocument.head).createChild({
23226                 tag : 'link',
23227                 rel : 'stylesheet',
23228                 type : 'text/css',
23229                 href : stylesheets
23230             });
23231             
23232             return;
23233         }
23234         var _this = this;
23235      
23236         Roo.each(stylesheets, function(s) {
23237             if(!s.length){
23238                 return;
23239             }
23240             
23241             Roo.get(_this.iframe.contentDocument.head).createChild({
23242                 tag : 'link',
23243                 rel : 'stylesheet',
23244                 type : 'text/css',
23245                 href : s
23246             });
23247         });
23248
23249         
23250     },
23251     
23252     removeStylesheets : function()
23253     {
23254         var _this = this;
23255         
23256         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23257             s.remove();
23258         });
23259     },
23260     
23261     setStyle : function(style)
23262     {
23263         Roo.get(this.iframe.contentDocument.head).createChild({
23264             tag : 'style',
23265             type : 'text/css',
23266             html : style
23267         });
23268
23269         return;
23270     }
23271     
23272     // hide stuff that is not compatible
23273     /**
23274      * @event blur
23275      * @hide
23276      */
23277     /**
23278      * @event change
23279      * @hide
23280      */
23281     /**
23282      * @event focus
23283      * @hide
23284      */
23285     /**
23286      * @event specialkey
23287      * @hide
23288      */
23289     /**
23290      * @cfg {String} fieldClass @hide
23291      */
23292     /**
23293      * @cfg {String} focusClass @hide
23294      */
23295     /**
23296      * @cfg {String} autoCreate @hide
23297      */
23298     /**
23299      * @cfg {String} inputType @hide
23300      */
23301     /**
23302      * @cfg {String} invalidClass @hide
23303      */
23304     /**
23305      * @cfg {String} invalidText @hide
23306      */
23307     /**
23308      * @cfg {String} msgFx @hide
23309      */
23310     /**
23311      * @cfg {String} validateOnBlur @hide
23312      */
23313 });
23314
23315 Roo.HtmlEditorCore.white = [
23316         'area', 'br', 'img', 'input', 'hr', 'wbr',
23317         
23318        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23319        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23320        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23321        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23322        'table',   'ul',         'xmp', 
23323        
23324        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23325       'thead',   'tr', 
23326      
23327       'dir', 'menu', 'ol', 'ul', 'dl',
23328        
23329       'embed',  'object'
23330 ];
23331
23332
23333 Roo.HtmlEditorCore.black = [
23334     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23335         'applet', // 
23336         'base',   'basefont', 'bgsound', 'blink',  'body', 
23337         'frame',  'frameset', 'head',    'html',   'ilayer', 
23338         'iframe', 'layer',  'link',     'meta',    'object',   
23339         'script', 'style' ,'title',  'xml' // clean later..
23340 ];
23341 Roo.HtmlEditorCore.clean = [
23342     'script', 'style', 'title', 'xml'
23343 ];
23344 Roo.HtmlEditorCore.remove = [
23345     'font'
23346 ];
23347 // attributes..
23348
23349 Roo.HtmlEditorCore.ablack = [
23350     'on'
23351 ];
23352     
23353 Roo.HtmlEditorCore.aclean = [ 
23354     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23355 ];
23356
23357 // protocols..
23358 Roo.HtmlEditorCore.pwhite= [
23359         'http',  'https',  'mailto'
23360 ];
23361
23362 // white listed style attributes.
23363 Roo.HtmlEditorCore.cwhite= [
23364       //  'text-align', /// default is to allow most things..
23365       
23366          
23367 //        'font-size'//??
23368 ];
23369
23370 // black listed style attributes.
23371 Roo.HtmlEditorCore.cblack= [
23372       //  'font-size' -- this can be set by the project 
23373 ];
23374
23375
23376 Roo.HtmlEditorCore.swapCodes   =[ 
23377     [    8211, "--" ], 
23378     [    8212, "--" ], 
23379     [    8216,  "'" ],  
23380     [    8217, "'" ],  
23381     [    8220, '"' ],  
23382     [    8221, '"' ],  
23383     [    8226, "*" ],  
23384     [    8230, "..." ]
23385 ]; 
23386
23387     /*
23388  * - LGPL
23389  *
23390  * HtmlEditor
23391  * 
23392  */
23393
23394 /**
23395  * @class Roo.bootstrap.HtmlEditor
23396  * @extends Roo.bootstrap.TextArea
23397  * Bootstrap HtmlEditor class
23398
23399  * @constructor
23400  * Create a new HtmlEditor
23401  * @param {Object} config The config object
23402  */
23403
23404 Roo.bootstrap.HtmlEditor = function(config){
23405     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23406     if (!this.toolbars) {
23407         this.toolbars = [];
23408     }
23409     
23410     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23411     this.addEvents({
23412             /**
23413              * @event initialize
23414              * Fires when the editor is fully initialized (including the iframe)
23415              * @param {HtmlEditor} this
23416              */
23417             initialize: true,
23418             /**
23419              * @event activate
23420              * Fires when the editor is first receives the focus. Any insertion must wait
23421              * until after this event.
23422              * @param {HtmlEditor} this
23423              */
23424             activate: true,
23425              /**
23426              * @event beforesync
23427              * Fires before the textarea is updated with content from the editor iframe. Return false
23428              * to cancel the sync.
23429              * @param {HtmlEditor} this
23430              * @param {String} html
23431              */
23432             beforesync: true,
23433              /**
23434              * @event beforepush
23435              * Fires before the iframe editor is updated with content from the textarea. Return false
23436              * to cancel the push.
23437              * @param {HtmlEditor} this
23438              * @param {String} html
23439              */
23440             beforepush: true,
23441              /**
23442              * @event sync
23443              * Fires when the textarea is updated with content from the editor iframe.
23444              * @param {HtmlEditor} this
23445              * @param {String} html
23446              */
23447             sync: true,
23448              /**
23449              * @event push
23450              * Fires when the iframe editor is updated with content from the textarea.
23451              * @param {HtmlEditor} this
23452              * @param {String} html
23453              */
23454             push: true,
23455              /**
23456              * @event editmodechange
23457              * Fires when the editor switches edit modes
23458              * @param {HtmlEditor} this
23459              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23460              */
23461             editmodechange: true,
23462             /**
23463              * @event editorevent
23464              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23465              * @param {HtmlEditor} this
23466              */
23467             editorevent: true,
23468             /**
23469              * @event firstfocus
23470              * Fires when on first focus - needed by toolbars..
23471              * @param {HtmlEditor} this
23472              */
23473             firstfocus: true,
23474             /**
23475              * @event autosave
23476              * Auto save the htmlEditor value as a file into Events
23477              * @param {HtmlEditor} this
23478              */
23479             autosave: true,
23480             /**
23481              * @event savedpreview
23482              * preview the saved version of htmlEditor
23483              * @param {HtmlEditor} this
23484              */
23485             savedpreview: true
23486         });
23487 };
23488
23489
23490 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23491     
23492     
23493       /**
23494      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23495      */
23496     toolbars : false,
23497     
23498      /**
23499     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23500     */
23501     btns : [],
23502    
23503      /**
23504      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23505      *                        Roo.resizable.
23506      */
23507     resizable : false,
23508      /**
23509      * @cfg {Number} height (in pixels)
23510      */   
23511     height: 300,
23512    /**
23513      * @cfg {Number} width (in pixels)
23514      */   
23515     width: false,
23516     
23517     /**
23518      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23519      * 
23520      */
23521     stylesheets: false,
23522     
23523     // id of frame..
23524     frameId: false,
23525     
23526     // private properties
23527     validationEvent : false,
23528     deferHeight: true,
23529     initialized : false,
23530     activated : false,
23531     
23532     onFocus : Roo.emptyFn,
23533     iframePad:3,
23534     hideMode:'offsets',
23535     
23536     tbContainer : false,
23537     
23538     bodyCls : '',
23539     
23540     toolbarContainer :function() {
23541         return this.wrap.select('.x-html-editor-tb',true).first();
23542     },
23543
23544     /**
23545      * Protected method that will not generally be called directly. It
23546      * is called when the editor creates its toolbar. Override this method if you need to
23547      * add custom toolbar buttons.
23548      * @param {HtmlEditor} editor
23549      */
23550     createToolbar : function(){
23551         Roo.log('renewing');
23552         Roo.log("create toolbars");
23553         
23554         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23555         this.toolbars[0].render(this.toolbarContainer());
23556         
23557         return;
23558         
23559 //        if (!editor.toolbars || !editor.toolbars.length) {
23560 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23561 //        }
23562 //        
23563 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23564 //            editor.toolbars[i] = Roo.factory(
23565 //                    typeof(editor.toolbars[i]) == 'string' ?
23566 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23567 //                Roo.bootstrap.HtmlEditor);
23568 //            editor.toolbars[i].init(editor);
23569 //        }
23570     },
23571
23572      
23573     // private
23574     onRender : function(ct, position)
23575     {
23576        // Roo.log("Call onRender: " + this.xtype);
23577         var _t = this;
23578         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23579       
23580         this.wrap = this.inputEl().wrap({
23581             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23582         });
23583         
23584         this.editorcore.onRender(ct, position);
23585          
23586         if (this.resizable) {
23587             this.resizeEl = new Roo.Resizable(this.wrap, {
23588                 pinned : true,
23589                 wrap: true,
23590                 dynamic : true,
23591                 minHeight : this.height,
23592                 height: this.height,
23593                 handles : this.resizable,
23594                 width: this.width,
23595                 listeners : {
23596                     resize : function(r, w, h) {
23597                         _t.onResize(w,h); // -something
23598                     }
23599                 }
23600             });
23601             
23602         }
23603         this.createToolbar(this);
23604        
23605         
23606         if(!this.width && this.resizable){
23607             this.setSize(this.wrap.getSize());
23608         }
23609         if (this.resizeEl) {
23610             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23611             // should trigger onReize..
23612         }
23613         
23614     },
23615
23616     // private
23617     onResize : function(w, h)
23618     {
23619         Roo.log('resize: ' +w + ',' + h );
23620         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23621         var ew = false;
23622         var eh = false;
23623         
23624         if(this.inputEl() ){
23625             if(typeof w == 'number'){
23626                 var aw = w - this.wrap.getFrameWidth('lr');
23627                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23628                 ew = aw;
23629             }
23630             if(typeof h == 'number'){
23631                  var tbh = -11;  // fixme it needs to tool bar size!
23632                 for (var i =0; i < this.toolbars.length;i++) {
23633                     // fixme - ask toolbars for heights?
23634                     tbh += this.toolbars[i].el.getHeight();
23635                     //if (this.toolbars[i].footer) {
23636                     //    tbh += this.toolbars[i].footer.el.getHeight();
23637                     //}
23638                 }
23639               
23640                 
23641                 
23642                 
23643                 
23644                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23645                 ah -= 5; // knock a few pixes off for look..
23646                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23647                 var eh = ah;
23648             }
23649         }
23650         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23651         this.editorcore.onResize(ew,eh);
23652         
23653     },
23654
23655     /**
23656      * Toggles the editor between standard and source edit mode.
23657      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23658      */
23659     toggleSourceEdit : function(sourceEditMode)
23660     {
23661         this.editorcore.toggleSourceEdit(sourceEditMode);
23662         
23663         if(this.editorcore.sourceEditMode){
23664             Roo.log('editor - showing textarea');
23665             
23666 //            Roo.log('in');
23667 //            Roo.log(this.syncValue());
23668             this.syncValue();
23669             this.inputEl().removeClass(['hide', 'x-hidden']);
23670             this.inputEl().dom.removeAttribute('tabIndex');
23671             this.inputEl().focus();
23672         }else{
23673             Roo.log('editor - hiding textarea');
23674 //            Roo.log('out')
23675 //            Roo.log(this.pushValue()); 
23676             this.pushValue();
23677             
23678             this.inputEl().addClass(['hide', 'x-hidden']);
23679             this.inputEl().dom.setAttribute('tabIndex', -1);
23680             //this.deferFocus();
23681         }
23682          
23683         if(this.resizable){
23684             this.setSize(this.wrap.getSize());
23685         }
23686         
23687         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23688     },
23689  
23690     // private (for BoxComponent)
23691     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23692
23693     // private (for BoxComponent)
23694     getResizeEl : function(){
23695         return this.wrap;
23696     },
23697
23698     // private (for BoxComponent)
23699     getPositionEl : function(){
23700         return this.wrap;
23701     },
23702
23703     // private
23704     initEvents : function(){
23705         this.originalValue = this.getValue();
23706     },
23707
23708 //    /**
23709 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23710 //     * @method
23711 //     */
23712 //    markInvalid : Roo.emptyFn,
23713 //    /**
23714 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23715 //     * @method
23716 //     */
23717 //    clearInvalid : Roo.emptyFn,
23718
23719     setValue : function(v){
23720         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23721         this.editorcore.pushValue();
23722     },
23723
23724      
23725     // private
23726     deferFocus : function(){
23727         this.focus.defer(10, this);
23728     },
23729
23730     // doc'ed in Field
23731     focus : function(){
23732         this.editorcore.focus();
23733         
23734     },
23735       
23736
23737     // private
23738     onDestroy : function(){
23739         
23740         
23741         
23742         if(this.rendered){
23743             
23744             for (var i =0; i < this.toolbars.length;i++) {
23745                 // fixme - ask toolbars for heights?
23746                 this.toolbars[i].onDestroy();
23747             }
23748             
23749             this.wrap.dom.innerHTML = '';
23750             this.wrap.remove();
23751         }
23752     },
23753
23754     // private
23755     onFirstFocus : function(){
23756         //Roo.log("onFirstFocus");
23757         this.editorcore.onFirstFocus();
23758          for (var i =0; i < this.toolbars.length;i++) {
23759             this.toolbars[i].onFirstFocus();
23760         }
23761         
23762     },
23763     
23764     // private
23765     syncValue : function()
23766     {   
23767         this.editorcore.syncValue();
23768     },
23769     
23770     pushValue : function()
23771     {   
23772         this.editorcore.pushValue();
23773     }
23774      
23775     
23776     // hide stuff that is not compatible
23777     /**
23778      * @event blur
23779      * @hide
23780      */
23781     /**
23782      * @event change
23783      * @hide
23784      */
23785     /**
23786      * @event focus
23787      * @hide
23788      */
23789     /**
23790      * @event specialkey
23791      * @hide
23792      */
23793     /**
23794      * @cfg {String} fieldClass @hide
23795      */
23796     /**
23797      * @cfg {String} focusClass @hide
23798      */
23799     /**
23800      * @cfg {String} autoCreate @hide
23801      */
23802     /**
23803      * @cfg {String} inputType @hide
23804      */
23805     /**
23806      * @cfg {String} invalidClass @hide
23807      */
23808     /**
23809      * @cfg {String} invalidText @hide
23810      */
23811     /**
23812      * @cfg {String} msgFx @hide
23813      */
23814     /**
23815      * @cfg {String} validateOnBlur @hide
23816      */
23817 });
23818  
23819     
23820    
23821    
23822    
23823       
23824 Roo.namespace('Roo.bootstrap.htmleditor');
23825 /**
23826  * @class Roo.bootstrap.HtmlEditorToolbar1
23827  * Basic Toolbar
23828  * 
23829  * Usage:
23830  *
23831  new Roo.bootstrap.HtmlEditor({
23832     ....
23833     toolbars : [
23834         new Roo.bootstrap.HtmlEditorToolbar1({
23835             disable : { fonts: 1 , format: 1, ..., ... , ...],
23836             btns : [ .... ]
23837         })
23838     }
23839      
23840  * 
23841  * @cfg {Object} disable List of elements to disable..
23842  * @cfg {Array} btns List of additional buttons.
23843  * 
23844  * 
23845  * NEEDS Extra CSS? 
23846  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23847  */
23848  
23849 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23850 {
23851     
23852     Roo.apply(this, config);
23853     
23854     // default disabled, based on 'good practice'..
23855     this.disable = this.disable || {};
23856     Roo.applyIf(this.disable, {
23857         fontSize : true,
23858         colors : true,
23859         specialElements : true
23860     });
23861     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23862     
23863     this.editor = config.editor;
23864     this.editorcore = config.editor.editorcore;
23865     
23866     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23867     
23868     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23869     // dont call parent... till later.
23870 }
23871 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23872      
23873     bar : true,
23874     
23875     editor : false,
23876     editorcore : false,
23877     
23878     
23879     formats : [
23880         "p" ,  
23881         "h1","h2","h3","h4","h5","h6", 
23882         "pre", "code", 
23883         "abbr", "acronym", "address", "cite", "samp", "var",
23884         'div','span'
23885     ],
23886     
23887     onRender : function(ct, position)
23888     {
23889        // Roo.log("Call onRender: " + this.xtype);
23890         
23891        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23892        Roo.log(this.el);
23893        this.el.dom.style.marginBottom = '0';
23894        var _this = this;
23895        var editorcore = this.editorcore;
23896        var editor= this.editor;
23897        
23898        var children = [];
23899        var btn = function(id,cmd , toggle, handler, html){
23900        
23901             var  event = toggle ? 'toggle' : 'click';
23902        
23903             var a = {
23904                 size : 'sm',
23905                 xtype: 'Button',
23906                 xns: Roo.bootstrap,
23907                 glyphicon : id,
23908                 cmd : id || cmd,
23909                 enableToggle:toggle !== false,
23910                 html : html || '',
23911                 pressed : toggle ? false : null,
23912                 listeners : {}
23913             };
23914             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23915                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23916             };
23917             children.push(a);
23918             return a;
23919        }
23920        
23921     //    var cb_box = function...
23922         
23923         var style = {
23924                 xtype: 'Button',
23925                 size : 'sm',
23926                 xns: Roo.bootstrap,
23927                 glyphicon : 'font',
23928                 //html : 'submit'
23929                 menu : {
23930                     xtype: 'Menu',
23931                     xns: Roo.bootstrap,
23932                     items:  []
23933                 }
23934         };
23935         Roo.each(this.formats, function(f) {
23936             style.menu.items.push({
23937                 xtype :'MenuItem',
23938                 xns: Roo.bootstrap,
23939                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23940                 tagname : f,
23941                 listeners : {
23942                     click : function()
23943                     {
23944                         editorcore.insertTag(this.tagname);
23945                         editor.focus();
23946                     }
23947                 }
23948                 
23949             });
23950         });
23951         children.push(style);   
23952         
23953         btn('bold',false,true);
23954         btn('italic',false,true);
23955         btn('align-left', 'justifyleft',true);
23956         btn('align-center', 'justifycenter',true);
23957         btn('align-right' , 'justifyright',true);
23958         btn('link', false, false, function(btn) {
23959             //Roo.log("create link?");
23960             var url = prompt(this.createLinkText, this.defaultLinkValue);
23961             if(url && url != 'http:/'+'/'){
23962                 this.editorcore.relayCmd('createlink', url);
23963             }
23964         }),
23965         btn('list','insertunorderedlist',true);
23966         btn('pencil', false,true, function(btn){
23967                 Roo.log(this);
23968                 this.toggleSourceEdit(btn.pressed);
23969         });
23970         
23971         if (this.editor.btns.length > 0) {
23972             for (var i = 0; i<this.editor.btns.length; i++) {
23973                 children.push(this.editor.btns[i]);
23974             }
23975         }
23976         
23977         /*
23978         var cog = {
23979                 xtype: 'Button',
23980                 size : 'sm',
23981                 xns: Roo.bootstrap,
23982                 glyphicon : 'cog',
23983                 //html : 'submit'
23984                 menu : {
23985                     xtype: 'Menu',
23986                     xns: Roo.bootstrap,
23987                     items:  []
23988                 }
23989         };
23990         
23991         cog.menu.items.push({
23992             xtype :'MenuItem',
23993             xns: Roo.bootstrap,
23994             html : Clean styles,
23995             tagname : f,
23996             listeners : {
23997                 click : function()
23998                 {
23999                     editorcore.insertTag(this.tagname);
24000                     editor.focus();
24001                 }
24002             }
24003             
24004         });
24005        */
24006         
24007          
24008        this.xtype = 'NavSimplebar';
24009         
24010         for(var i=0;i< children.length;i++) {
24011             
24012             this.buttons.add(this.addxtypeChild(children[i]));
24013             
24014         }
24015         
24016         editor.on('editorevent', this.updateToolbar, this);
24017     },
24018     onBtnClick : function(id)
24019     {
24020        this.editorcore.relayCmd(id);
24021        this.editorcore.focus();
24022     },
24023     
24024     /**
24025      * Protected method that will not generally be called directly. It triggers
24026      * a toolbar update by reading the markup state of the current selection in the editor.
24027      */
24028     updateToolbar: function(){
24029
24030         if(!this.editorcore.activated){
24031             this.editor.onFirstFocus(); // is this neeed?
24032             return;
24033         }
24034
24035         var btns = this.buttons; 
24036         var doc = this.editorcore.doc;
24037         btns.get('bold').setActive(doc.queryCommandState('bold'));
24038         btns.get('italic').setActive(doc.queryCommandState('italic'));
24039         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24040         
24041         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24042         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24043         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24044         
24045         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24046         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24047          /*
24048         
24049         var ans = this.editorcore.getAllAncestors();
24050         if (this.formatCombo) {
24051             
24052             
24053             var store = this.formatCombo.store;
24054             this.formatCombo.setValue("");
24055             for (var i =0; i < ans.length;i++) {
24056                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24057                     // select it..
24058                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24059                     break;
24060                 }
24061             }
24062         }
24063         
24064         
24065         
24066         // hides menus... - so this cant be on a menu...
24067         Roo.bootstrap.MenuMgr.hideAll();
24068         */
24069         Roo.bootstrap.MenuMgr.hideAll();
24070         //this.editorsyncValue();
24071     },
24072     onFirstFocus: function() {
24073         this.buttons.each(function(item){
24074            item.enable();
24075         });
24076     },
24077     toggleSourceEdit : function(sourceEditMode){
24078         
24079           
24080         if(sourceEditMode){
24081             Roo.log("disabling buttons");
24082            this.buttons.each( function(item){
24083                 if(item.cmd != 'pencil'){
24084                     item.disable();
24085                 }
24086             });
24087           
24088         }else{
24089             Roo.log("enabling buttons");
24090             if(this.editorcore.initialized){
24091                 this.buttons.each( function(item){
24092                     item.enable();
24093                 });
24094             }
24095             
24096         }
24097         Roo.log("calling toggole on editor");
24098         // tell the editor that it's been pressed..
24099         this.editor.toggleSourceEdit(sourceEditMode);
24100        
24101     }
24102 });
24103
24104
24105
24106
24107
24108 /**
24109  * @class Roo.bootstrap.Table.AbstractSelectionModel
24110  * @extends Roo.util.Observable
24111  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24112  * implemented by descendant classes.  This class should not be directly instantiated.
24113  * @constructor
24114  */
24115 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24116     this.locked = false;
24117     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24118 };
24119
24120
24121 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24122     /** @ignore Called by the grid automatically. Do not call directly. */
24123     init : function(grid){
24124         this.grid = grid;
24125         this.initEvents();
24126     },
24127
24128     /**
24129      * Locks the selections.
24130      */
24131     lock : function(){
24132         this.locked = true;
24133     },
24134
24135     /**
24136      * Unlocks the selections.
24137      */
24138     unlock : function(){
24139         this.locked = false;
24140     },
24141
24142     /**
24143      * Returns true if the selections are locked.
24144      * @return {Boolean}
24145      */
24146     isLocked : function(){
24147         return this.locked;
24148     }
24149 });
24150 /**
24151  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24152  * @class Roo.bootstrap.Table.RowSelectionModel
24153  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24154  * It supports multiple selections and keyboard selection/navigation. 
24155  * @constructor
24156  * @param {Object} config
24157  */
24158
24159 Roo.bootstrap.Table.RowSelectionModel = function(config){
24160     Roo.apply(this, config);
24161     this.selections = new Roo.util.MixedCollection(false, function(o){
24162         return o.id;
24163     });
24164
24165     this.last = false;
24166     this.lastActive = false;
24167
24168     this.addEvents({
24169         /**
24170              * @event selectionchange
24171              * Fires when the selection changes
24172              * @param {SelectionModel} this
24173              */
24174             "selectionchange" : true,
24175         /**
24176              * @event afterselectionchange
24177              * Fires after the selection changes (eg. by key press or clicking)
24178              * @param {SelectionModel} this
24179              */
24180             "afterselectionchange" : true,
24181         /**
24182              * @event beforerowselect
24183              * Fires when a row is selected being selected, return false to cancel.
24184              * @param {SelectionModel} this
24185              * @param {Number} rowIndex The selected index
24186              * @param {Boolean} keepExisting False if other selections will be cleared
24187              */
24188             "beforerowselect" : true,
24189         /**
24190              * @event rowselect
24191              * Fires when a row is selected.
24192              * @param {SelectionModel} this
24193              * @param {Number} rowIndex The selected index
24194              * @param {Roo.data.Record} r The record
24195              */
24196             "rowselect" : true,
24197         /**
24198              * @event rowdeselect
24199              * Fires when a row is deselected.
24200              * @param {SelectionModel} this
24201              * @param {Number} rowIndex The selected index
24202              */
24203         "rowdeselect" : true
24204     });
24205     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24206     this.locked = false;
24207  };
24208
24209 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24210     /**
24211      * @cfg {Boolean} singleSelect
24212      * True to allow selection of only one row at a time (defaults to false)
24213      */
24214     singleSelect : false,
24215
24216     // private
24217     initEvents : function()
24218     {
24219
24220         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24221         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24222         //}else{ // allow click to work like normal
24223          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24224         //}
24225         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24226         this.grid.on("rowclick", this.handleMouseDown, this);
24227         
24228         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24229             "up" : function(e){
24230                 if(!e.shiftKey){
24231                     this.selectPrevious(e.shiftKey);
24232                 }else if(this.last !== false && this.lastActive !== false){
24233                     var last = this.last;
24234                     this.selectRange(this.last,  this.lastActive-1);
24235                     this.grid.getView().focusRow(this.lastActive);
24236                     if(last !== false){
24237                         this.last = last;
24238                     }
24239                 }else{
24240                     this.selectFirstRow();
24241                 }
24242                 this.fireEvent("afterselectionchange", this);
24243             },
24244             "down" : function(e){
24245                 if(!e.shiftKey){
24246                     this.selectNext(e.shiftKey);
24247                 }else if(this.last !== false && this.lastActive !== false){
24248                     var last = this.last;
24249                     this.selectRange(this.last,  this.lastActive+1);
24250                     this.grid.getView().focusRow(this.lastActive);
24251                     if(last !== false){
24252                         this.last = last;
24253                     }
24254                 }else{
24255                     this.selectFirstRow();
24256                 }
24257                 this.fireEvent("afterselectionchange", this);
24258             },
24259             scope: this
24260         });
24261         this.grid.store.on('load', function(){
24262             this.selections.clear();
24263         },this);
24264         /*
24265         var view = this.grid.view;
24266         view.on("refresh", this.onRefresh, this);
24267         view.on("rowupdated", this.onRowUpdated, this);
24268         view.on("rowremoved", this.onRemove, this);
24269         */
24270     },
24271
24272     // private
24273     onRefresh : function()
24274     {
24275         var ds = this.grid.store, i, v = this.grid.view;
24276         var s = this.selections;
24277         s.each(function(r){
24278             if((i = ds.indexOfId(r.id)) != -1){
24279                 v.onRowSelect(i);
24280             }else{
24281                 s.remove(r);
24282             }
24283         });
24284     },
24285
24286     // private
24287     onRemove : function(v, index, r){
24288         this.selections.remove(r);
24289     },
24290
24291     // private
24292     onRowUpdated : function(v, index, r){
24293         if(this.isSelected(r)){
24294             v.onRowSelect(index);
24295         }
24296     },
24297
24298     /**
24299      * Select records.
24300      * @param {Array} records The records to select
24301      * @param {Boolean} keepExisting (optional) True to keep existing selections
24302      */
24303     selectRecords : function(records, keepExisting)
24304     {
24305         if(!keepExisting){
24306             this.clearSelections();
24307         }
24308             var ds = this.grid.store;
24309         for(var i = 0, len = records.length; i < len; i++){
24310             this.selectRow(ds.indexOf(records[i]), true);
24311         }
24312     },
24313
24314     /**
24315      * Gets the number of selected rows.
24316      * @return {Number}
24317      */
24318     getCount : function(){
24319         return this.selections.length;
24320     },
24321
24322     /**
24323      * Selects the first row in the grid.
24324      */
24325     selectFirstRow : function(){
24326         this.selectRow(0);
24327     },
24328
24329     /**
24330      * Select the last row.
24331      * @param {Boolean} keepExisting (optional) True to keep existing selections
24332      */
24333     selectLastRow : function(keepExisting){
24334         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24335         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24336     },
24337
24338     /**
24339      * Selects the row immediately following the last selected row.
24340      * @param {Boolean} keepExisting (optional) True to keep existing selections
24341      */
24342     selectNext : function(keepExisting)
24343     {
24344             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24345             this.selectRow(this.last+1, keepExisting);
24346             this.grid.getView().focusRow(this.last);
24347         }
24348     },
24349
24350     /**
24351      * Selects the row that precedes the last selected row.
24352      * @param {Boolean} keepExisting (optional) True to keep existing selections
24353      */
24354     selectPrevious : function(keepExisting){
24355         if(this.last){
24356             this.selectRow(this.last-1, keepExisting);
24357             this.grid.getView().focusRow(this.last);
24358         }
24359     },
24360
24361     /**
24362      * Returns the selected records
24363      * @return {Array} Array of selected records
24364      */
24365     getSelections : function(){
24366         return [].concat(this.selections.items);
24367     },
24368
24369     /**
24370      * Returns the first selected record.
24371      * @return {Record}
24372      */
24373     getSelected : function(){
24374         return this.selections.itemAt(0);
24375     },
24376
24377
24378     /**
24379      * Clears all selections.
24380      */
24381     clearSelections : function(fast)
24382     {
24383         if(this.locked) {
24384             return;
24385         }
24386         if(fast !== true){
24387                 var ds = this.grid.store;
24388             var s = this.selections;
24389             s.each(function(r){
24390                 this.deselectRow(ds.indexOfId(r.id));
24391             }, this);
24392             s.clear();
24393         }else{
24394             this.selections.clear();
24395         }
24396         this.last = false;
24397     },
24398
24399
24400     /**
24401      * Selects all rows.
24402      */
24403     selectAll : function(){
24404         if(this.locked) {
24405             return;
24406         }
24407         this.selections.clear();
24408         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24409             this.selectRow(i, true);
24410         }
24411     },
24412
24413     /**
24414      * Returns True if there is a selection.
24415      * @return {Boolean}
24416      */
24417     hasSelection : function(){
24418         return this.selections.length > 0;
24419     },
24420
24421     /**
24422      * Returns True if the specified row is selected.
24423      * @param {Number/Record} record The record or index of the record to check
24424      * @return {Boolean}
24425      */
24426     isSelected : function(index){
24427             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24428         return (r && this.selections.key(r.id) ? true : false);
24429     },
24430
24431     /**
24432      * Returns True if the specified record id is selected.
24433      * @param {String} id The id of record to check
24434      * @return {Boolean}
24435      */
24436     isIdSelected : function(id){
24437         return (this.selections.key(id) ? true : false);
24438     },
24439
24440
24441     // private
24442     handleMouseDBClick : function(e, t){
24443         
24444     },
24445     // private
24446     handleMouseDown : function(e, t)
24447     {
24448             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24449         if(this.isLocked() || rowIndex < 0 ){
24450             return;
24451         };
24452         if(e.shiftKey && this.last !== false){
24453             var last = this.last;
24454             this.selectRange(last, rowIndex, e.ctrlKey);
24455             this.last = last; // reset the last
24456             t.focus();
24457     
24458         }else{
24459             var isSelected = this.isSelected(rowIndex);
24460             //Roo.log("select row:" + rowIndex);
24461             if(isSelected){
24462                 this.deselectRow(rowIndex);
24463             } else {
24464                         this.selectRow(rowIndex, true);
24465             }
24466     
24467             /*
24468                 if(e.button !== 0 && isSelected){
24469                 alert('rowIndex 2: ' + rowIndex);
24470                     view.focusRow(rowIndex);
24471                 }else if(e.ctrlKey && isSelected){
24472                     this.deselectRow(rowIndex);
24473                 }else if(!isSelected){
24474                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24475                     view.focusRow(rowIndex);
24476                 }
24477             */
24478         }
24479         this.fireEvent("afterselectionchange", this);
24480     },
24481     // private
24482     handleDragableRowClick :  function(grid, rowIndex, e) 
24483     {
24484         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24485             this.selectRow(rowIndex, false);
24486             grid.view.focusRow(rowIndex);
24487              this.fireEvent("afterselectionchange", this);
24488         }
24489     },
24490     
24491     /**
24492      * Selects multiple rows.
24493      * @param {Array} rows Array of the indexes of the row to select
24494      * @param {Boolean} keepExisting (optional) True to keep existing selections
24495      */
24496     selectRows : function(rows, keepExisting){
24497         if(!keepExisting){
24498             this.clearSelections();
24499         }
24500         for(var i = 0, len = rows.length; i < len; i++){
24501             this.selectRow(rows[i], true);
24502         }
24503     },
24504
24505     /**
24506      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24507      * @param {Number} startRow The index of the first row in the range
24508      * @param {Number} endRow The index of the last row in the range
24509      * @param {Boolean} keepExisting (optional) True to retain existing selections
24510      */
24511     selectRange : function(startRow, endRow, keepExisting){
24512         if(this.locked) {
24513             return;
24514         }
24515         if(!keepExisting){
24516             this.clearSelections();
24517         }
24518         if(startRow <= endRow){
24519             for(var i = startRow; i <= endRow; i++){
24520                 this.selectRow(i, true);
24521             }
24522         }else{
24523             for(var i = startRow; i >= endRow; i--){
24524                 this.selectRow(i, true);
24525             }
24526         }
24527     },
24528
24529     /**
24530      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24531      * @param {Number} startRow The index of the first row in the range
24532      * @param {Number} endRow The index of the last row in the range
24533      */
24534     deselectRange : function(startRow, endRow, preventViewNotify){
24535         if(this.locked) {
24536             return;
24537         }
24538         for(var i = startRow; i <= endRow; i++){
24539             this.deselectRow(i, preventViewNotify);
24540         }
24541     },
24542
24543     /**
24544      * Selects a row.
24545      * @param {Number} row The index of the row to select
24546      * @param {Boolean} keepExisting (optional) True to keep existing selections
24547      */
24548     selectRow : function(index, keepExisting, preventViewNotify)
24549     {
24550             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24551             return;
24552         }
24553         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24554             if(!keepExisting || this.singleSelect){
24555                 this.clearSelections();
24556             }
24557             
24558             var r = this.grid.store.getAt(index);
24559             //console.log('selectRow - record id :' + r.id);
24560             
24561             this.selections.add(r);
24562             this.last = this.lastActive = index;
24563             if(!preventViewNotify){
24564                 var proxy = new Roo.Element(
24565                                 this.grid.getRowDom(index)
24566                 );
24567                 proxy.addClass('bg-info info');
24568             }
24569             this.fireEvent("rowselect", this, index, r);
24570             this.fireEvent("selectionchange", this);
24571         }
24572     },
24573
24574     /**
24575      * Deselects a row.
24576      * @param {Number} row The index of the row to deselect
24577      */
24578     deselectRow : function(index, preventViewNotify)
24579     {
24580         if(this.locked) {
24581             return;
24582         }
24583         if(this.last == index){
24584             this.last = false;
24585         }
24586         if(this.lastActive == index){
24587             this.lastActive = false;
24588         }
24589         
24590         var r = this.grid.store.getAt(index);
24591         if (!r) {
24592             return;
24593         }
24594         
24595         this.selections.remove(r);
24596         //.console.log('deselectRow - record id :' + r.id);
24597         if(!preventViewNotify){
24598         
24599             var proxy = new Roo.Element(
24600                 this.grid.getRowDom(index)
24601             );
24602             proxy.removeClass('bg-info info');
24603         }
24604         this.fireEvent("rowdeselect", this, index);
24605         this.fireEvent("selectionchange", this);
24606     },
24607
24608     // private
24609     restoreLast : function(){
24610         if(this._last){
24611             this.last = this._last;
24612         }
24613     },
24614
24615     // private
24616     acceptsNav : function(row, col, cm){
24617         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24618     },
24619
24620     // private
24621     onEditorKey : function(field, e){
24622         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24623         if(k == e.TAB){
24624             e.stopEvent();
24625             ed.completeEdit();
24626             if(e.shiftKey){
24627                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24628             }else{
24629                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24630             }
24631         }else if(k == e.ENTER && !e.ctrlKey){
24632             e.stopEvent();
24633             ed.completeEdit();
24634             if(e.shiftKey){
24635                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24636             }else{
24637                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24638             }
24639         }else if(k == e.ESC){
24640             ed.cancelEdit();
24641         }
24642         if(newCell){
24643             g.startEditing(newCell[0], newCell[1]);
24644         }
24645     }
24646 });
24647 /*
24648  * Based on:
24649  * Ext JS Library 1.1.1
24650  * Copyright(c) 2006-2007, Ext JS, LLC.
24651  *
24652  * Originally Released Under LGPL - original licence link has changed is not relivant.
24653  *
24654  * Fork - LGPL
24655  * <script type="text/javascript">
24656  */
24657  
24658 /**
24659  * @class Roo.bootstrap.PagingToolbar
24660  * @extends Roo.bootstrap.NavSimplebar
24661  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24662  * @constructor
24663  * Create a new PagingToolbar
24664  * @param {Object} config The config object
24665  * @param {Roo.data.Store} store
24666  */
24667 Roo.bootstrap.PagingToolbar = function(config)
24668 {
24669     // old args format still supported... - xtype is prefered..
24670         // created from xtype...
24671     
24672     this.ds = config.dataSource;
24673     
24674     if (config.store && !this.ds) {
24675         this.store= Roo.factory(config.store, Roo.data);
24676         this.ds = this.store;
24677         this.ds.xmodule = this.xmodule || false;
24678     }
24679     
24680     this.toolbarItems = [];
24681     if (config.items) {
24682         this.toolbarItems = config.items;
24683     }
24684     
24685     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24686     
24687     this.cursor = 0;
24688     
24689     if (this.ds) { 
24690         this.bind(this.ds);
24691     }
24692     
24693     if (Roo.bootstrap.version == 4) {
24694         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24695     } else {
24696         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24697     }
24698     
24699 };
24700
24701 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24702     /**
24703      * @cfg {Roo.data.Store} dataSource
24704      * The underlying data store providing the paged data
24705      */
24706     /**
24707      * @cfg {String/HTMLElement/Element} container
24708      * container The id or element that will contain the toolbar
24709      */
24710     /**
24711      * @cfg {Boolean} displayInfo
24712      * True to display the displayMsg (defaults to false)
24713      */
24714     /**
24715      * @cfg {Number} pageSize
24716      * The number of records to display per page (defaults to 20)
24717      */
24718     pageSize: 20,
24719     /**
24720      * @cfg {String} displayMsg
24721      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24722      */
24723     displayMsg : 'Displaying {0} - {1} of {2}',
24724     /**
24725      * @cfg {String} emptyMsg
24726      * The message to display when no records are found (defaults to "No data to display")
24727      */
24728     emptyMsg : 'No data to display',
24729     /**
24730      * Customizable piece of the default paging text (defaults to "Page")
24731      * @type String
24732      */
24733     beforePageText : "Page",
24734     /**
24735      * Customizable piece of the default paging text (defaults to "of %0")
24736      * @type String
24737      */
24738     afterPageText : "of {0}",
24739     /**
24740      * Customizable piece of the default paging text (defaults to "First Page")
24741      * @type String
24742      */
24743     firstText : "First Page",
24744     /**
24745      * Customizable piece of the default paging text (defaults to "Previous Page")
24746      * @type String
24747      */
24748     prevText : "Previous Page",
24749     /**
24750      * Customizable piece of the default paging text (defaults to "Next Page")
24751      * @type String
24752      */
24753     nextText : "Next Page",
24754     /**
24755      * Customizable piece of the default paging text (defaults to "Last Page")
24756      * @type String
24757      */
24758     lastText : "Last Page",
24759     /**
24760      * Customizable piece of the default paging text (defaults to "Refresh")
24761      * @type String
24762      */
24763     refreshText : "Refresh",
24764
24765     buttons : false,
24766     // private
24767     onRender : function(ct, position) 
24768     {
24769         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24770         this.navgroup.parentId = this.id;
24771         this.navgroup.onRender(this.el, null);
24772         // add the buttons to the navgroup
24773         
24774         if(this.displayInfo){
24775             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24776             this.displayEl = this.el.select('.x-paging-info', true).first();
24777 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24778 //            this.displayEl = navel.el.select('span',true).first();
24779         }
24780         
24781         var _this = this;
24782         
24783         if(this.buttons){
24784             Roo.each(_this.buttons, function(e){ // this might need to use render????
24785                Roo.factory(e).render(_this.el);
24786             });
24787         }
24788             
24789         Roo.each(_this.toolbarItems, function(e) {
24790             _this.navgroup.addItem(e);
24791         });
24792         
24793         
24794         this.first = this.navgroup.addItem({
24795             tooltip: this.firstText,
24796             cls: "prev btn-outline-secondary",
24797             html : ' <i class="fa fa-step-backward"></i>',
24798             disabled: true,
24799             preventDefault: true,
24800             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24801         });
24802         
24803         this.prev =  this.navgroup.addItem({
24804             tooltip: this.prevText,
24805             cls: "prev btn-outline-secondary",
24806             html : ' <i class="fa fa-backward"></i>',
24807             disabled: true,
24808             preventDefault: true,
24809             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24810         });
24811     //this.addSeparator();
24812         
24813         
24814         var field = this.navgroup.addItem( {
24815             tagtype : 'span',
24816             cls : 'x-paging-position  btn-outline-secondary',
24817              disabled: true,
24818             html : this.beforePageText  +
24819                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24820                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24821          } ); //?? escaped?
24822         
24823         this.field = field.el.select('input', true).first();
24824         this.field.on("keydown", this.onPagingKeydown, this);
24825         this.field.on("focus", function(){this.dom.select();});
24826     
24827     
24828         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24829         //this.field.setHeight(18);
24830         //this.addSeparator();
24831         this.next = this.navgroup.addItem({
24832             tooltip: this.nextText,
24833             cls: "next btn-outline-secondary",
24834             html : ' <i class="fa fa-forward"></i>',
24835             disabled: true,
24836             preventDefault: true,
24837             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24838         });
24839         this.last = this.navgroup.addItem({
24840             tooltip: this.lastText,
24841             html : ' <i class="fa fa-step-forward"></i>',
24842             cls: "next btn-outline-secondary",
24843             disabled: true,
24844             preventDefault: true,
24845             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24846         });
24847     //this.addSeparator();
24848         this.loading = this.navgroup.addItem({
24849             tooltip: this.refreshText,
24850             cls: "btn-outline-secondary",
24851             html : ' <i class="fa fa-refresh"></i>',
24852             preventDefault: true,
24853             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24854         });
24855         
24856     },
24857
24858     // private
24859     updateInfo : function(){
24860         if(this.displayEl){
24861             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24862             var msg = count == 0 ?
24863                 this.emptyMsg :
24864                 String.format(
24865                     this.displayMsg,
24866                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24867                 );
24868             this.displayEl.update(msg);
24869         }
24870     },
24871
24872     // private
24873     onLoad : function(ds, r, o)
24874     {
24875         this.cursor = o.params.start ? o.params.start : 0;
24876         
24877         var d = this.getPageData(),
24878             ap = d.activePage,
24879             ps = d.pages;
24880         
24881         
24882         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24883         this.field.dom.value = ap;
24884         this.first.setDisabled(ap == 1);
24885         this.prev.setDisabled(ap == 1);
24886         this.next.setDisabled(ap == ps);
24887         this.last.setDisabled(ap == ps);
24888         this.loading.enable();
24889         this.updateInfo();
24890     },
24891
24892     // private
24893     getPageData : function(){
24894         var total = this.ds.getTotalCount();
24895         return {
24896             total : total,
24897             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24898             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24899         };
24900     },
24901
24902     // private
24903     onLoadError : function(){
24904         this.loading.enable();
24905     },
24906
24907     // private
24908     onPagingKeydown : function(e){
24909         var k = e.getKey();
24910         var d = this.getPageData();
24911         if(k == e.RETURN){
24912             var v = this.field.dom.value, pageNum;
24913             if(!v || isNaN(pageNum = parseInt(v, 10))){
24914                 this.field.dom.value = d.activePage;
24915                 return;
24916             }
24917             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24918             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24919             e.stopEvent();
24920         }
24921         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))
24922         {
24923           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24924           this.field.dom.value = pageNum;
24925           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24926           e.stopEvent();
24927         }
24928         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24929         {
24930           var v = this.field.dom.value, pageNum; 
24931           var increment = (e.shiftKey) ? 10 : 1;
24932           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24933                 increment *= -1;
24934           }
24935           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24936             this.field.dom.value = d.activePage;
24937             return;
24938           }
24939           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24940           {
24941             this.field.dom.value = parseInt(v, 10) + increment;
24942             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24943             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24944           }
24945           e.stopEvent();
24946         }
24947     },
24948
24949     // private
24950     beforeLoad : function(){
24951         if(this.loading){
24952             this.loading.disable();
24953         }
24954     },
24955
24956     // private
24957     onClick : function(which){
24958         
24959         var ds = this.ds;
24960         if (!ds) {
24961             return;
24962         }
24963         
24964         switch(which){
24965             case "first":
24966                 ds.load({params:{start: 0, limit: this.pageSize}});
24967             break;
24968             case "prev":
24969                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24970             break;
24971             case "next":
24972                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24973             break;
24974             case "last":
24975                 var total = ds.getTotalCount();
24976                 var extra = total % this.pageSize;
24977                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24978                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24979             break;
24980             case "refresh":
24981                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24982             break;
24983         }
24984     },
24985
24986     /**
24987      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24988      * @param {Roo.data.Store} store The data store to unbind
24989      */
24990     unbind : function(ds){
24991         ds.un("beforeload", this.beforeLoad, this);
24992         ds.un("load", this.onLoad, this);
24993         ds.un("loadexception", this.onLoadError, this);
24994         ds.un("remove", this.updateInfo, this);
24995         ds.un("add", this.updateInfo, this);
24996         this.ds = undefined;
24997     },
24998
24999     /**
25000      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25001      * @param {Roo.data.Store} store The data store to bind
25002      */
25003     bind : function(ds){
25004         ds.on("beforeload", this.beforeLoad, this);
25005         ds.on("load", this.onLoad, this);
25006         ds.on("loadexception", this.onLoadError, this);
25007         ds.on("remove", this.updateInfo, this);
25008         ds.on("add", this.updateInfo, this);
25009         this.ds = ds;
25010     }
25011 });/*
25012  * - LGPL
25013  *
25014  * element
25015  * 
25016  */
25017
25018 /**
25019  * @class Roo.bootstrap.MessageBar
25020  * @extends Roo.bootstrap.Component
25021  * Bootstrap MessageBar class
25022  * @cfg {String} html contents of the MessageBar
25023  * @cfg {String} weight (info | success | warning | danger) default info
25024  * @cfg {String} beforeClass insert the bar before the given class
25025  * @cfg {Boolean} closable (true | false) default false
25026  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25027  * 
25028  * @constructor
25029  * Create a new Element
25030  * @param {Object} config The config object
25031  */
25032
25033 Roo.bootstrap.MessageBar = function(config){
25034     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25035 };
25036
25037 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25038     
25039     html: '',
25040     weight: 'info',
25041     closable: false,
25042     fixed: false,
25043     beforeClass: 'bootstrap-sticky-wrap',
25044     
25045     getAutoCreate : function(){
25046         
25047         var cfg = {
25048             tag: 'div',
25049             cls: 'alert alert-dismissable alert-' + this.weight,
25050             cn: [
25051                 {
25052                     tag: 'span',
25053                     cls: 'message',
25054                     html: this.html || ''
25055                 }
25056             ]
25057         };
25058         
25059         if(this.fixed){
25060             cfg.cls += ' alert-messages-fixed';
25061         }
25062         
25063         if(this.closable){
25064             cfg.cn.push({
25065                 tag: 'button',
25066                 cls: 'close',
25067                 html: 'x'
25068             });
25069         }
25070         
25071         return cfg;
25072     },
25073     
25074     onRender : function(ct, position)
25075     {
25076         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25077         
25078         if(!this.el){
25079             var cfg = Roo.apply({},  this.getAutoCreate());
25080             cfg.id = Roo.id();
25081             
25082             if (this.cls) {
25083                 cfg.cls += ' ' + this.cls;
25084             }
25085             if (this.style) {
25086                 cfg.style = this.style;
25087             }
25088             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25089             
25090             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25091         }
25092         
25093         this.el.select('>button.close').on('click', this.hide, this);
25094         
25095     },
25096     
25097     show : function()
25098     {
25099         if (!this.rendered) {
25100             this.render();
25101         }
25102         
25103         this.el.show();
25104         
25105         this.fireEvent('show', this);
25106         
25107     },
25108     
25109     hide : function()
25110     {
25111         if (!this.rendered) {
25112             this.render();
25113         }
25114         
25115         this.el.hide();
25116         
25117         this.fireEvent('hide', this);
25118     },
25119     
25120     update : function()
25121     {
25122 //        var e = this.el.dom.firstChild;
25123 //        
25124 //        if(this.closable){
25125 //            e = e.nextSibling;
25126 //        }
25127 //        
25128 //        e.data = this.html || '';
25129
25130         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25131     }
25132    
25133 });
25134
25135  
25136
25137      /*
25138  * - LGPL
25139  *
25140  * Graph
25141  * 
25142  */
25143
25144
25145 /**
25146  * @class Roo.bootstrap.Graph
25147  * @extends Roo.bootstrap.Component
25148  * Bootstrap Graph class
25149 > Prameters
25150  -sm {number} sm 4
25151  -md {number} md 5
25152  @cfg {String} graphtype  bar | vbar | pie
25153  @cfg {number} g_x coodinator | centre x (pie)
25154  @cfg {number} g_y coodinator | centre y (pie)
25155  @cfg {number} g_r radius (pie)
25156  @cfg {number} g_height height of the chart (respected by all elements in the set)
25157  @cfg {number} g_width width of the chart (respected by all elements in the set)
25158  @cfg {Object} title The title of the chart
25159     
25160  -{Array}  values
25161  -opts (object) options for the chart 
25162      o {
25163      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25164      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25165      o vgutter (number)
25166      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.
25167      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25168      o to
25169      o stretch (boolean)
25170      o }
25171  -opts (object) options for the pie
25172      o{
25173      o cut
25174      o startAngle (number)
25175      o endAngle (number)
25176      } 
25177  *
25178  * @constructor
25179  * Create a new Input
25180  * @param {Object} config The config object
25181  */
25182
25183 Roo.bootstrap.Graph = function(config){
25184     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25185     
25186     this.addEvents({
25187         // img events
25188         /**
25189          * @event click
25190          * The img click event for the img.
25191          * @param {Roo.EventObject} e
25192          */
25193         "click" : true
25194     });
25195 };
25196
25197 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25198     
25199     sm: 4,
25200     md: 5,
25201     graphtype: 'bar',
25202     g_height: 250,
25203     g_width: 400,
25204     g_x: 50,
25205     g_y: 50,
25206     g_r: 30,
25207     opts:{
25208         //g_colors: this.colors,
25209         g_type: 'soft',
25210         g_gutter: '20%'
25211
25212     },
25213     title : false,
25214
25215     getAutoCreate : function(){
25216         
25217         var cfg = {
25218             tag: 'div',
25219             html : null
25220         };
25221         
25222         
25223         return  cfg;
25224     },
25225
25226     onRender : function(ct,position){
25227         
25228         
25229         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25230         
25231         if (typeof(Raphael) == 'undefined') {
25232             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25233             return;
25234         }
25235         
25236         this.raphael = Raphael(this.el.dom);
25237         
25238                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25239                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25240                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25241                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25242                 /*
25243                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25244                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25245                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25246                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25247                 
25248                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25249                 r.barchart(330, 10, 300, 220, data1);
25250                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25251                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25252                 */
25253                 
25254                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25255                 // r.barchart(30, 30, 560, 250,  xdata, {
25256                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25257                 //     axis : "0 0 1 1",
25258                 //     axisxlabels :  xdata
25259                 //     //yvalues : cols,
25260                    
25261                 // });
25262 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25263 //        
25264 //        this.load(null,xdata,{
25265 //                axis : "0 0 1 1",
25266 //                axisxlabels :  xdata
25267 //                });
25268
25269     },
25270
25271     load : function(graphtype,xdata,opts)
25272     {
25273         this.raphael.clear();
25274         if(!graphtype) {
25275             graphtype = this.graphtype;
25276         }
25277         if(!opts){
25278             opts = this.opts;
25279         }
25280         var r = this.raphael,
25281             fin = function () {
25282                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25283             },
25284             fout = function () {
25285                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25286             },
25287             pfin = function() {
25288                 this.sector.stop();
25289                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25290
25291                 if (this.label) {
25292                     this.label[0].stop();
25293                     this.label[0].attr({ r: 7.5 });
25294                     this.label[1].attr({ "font-weight": 800 });
25295                 }
25296             },
25297             pfout = function() {
25298                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25299
25300                 if (this.label) {
25301                     this.label[0].animate({ r: 5 }, 500, "bounce");
25302                     this.label[1].attr({ "font-weight": 400 });
25303                 }
25304             };
25305
25306         switch(graphtype){
25307             case 'bar':
25308                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25309                 break;
25310             case 'hbar':
25311                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25312                 break;
25313             case 'pie':
25314 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25315 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25316 //            
25317                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25318                 
25319                 break;
25320
25321         }
25322         
25323         if(this.title){
25324             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25325         }
25326         
25327     },
25328     
25329     setTitle: function(o)
25330     {
25331         this.title = o;
25332     },
25333     
25334     initEvents: function() {
25335         
25336         if(!this.href){
25337             this.el.on('click', this.onClick, this);
25338         }
25339     },
25340     
25341     onClick : function(e)
25342     {
25343         Roo.log('img onclick');
25344         this.fireEvent('click', this, e);
25345     }
25346    
25347 });
25348
25349  
25350 /*
25351  * - LGPL
25352  *
25353  * numberBox
25354  * 
25355  */
25356 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25357
25358 /**
25359  * @class Roo.bootstrap.dash.NumberBox
25360  * @extends Roo.bootstrap.Component
25361  * Bootstrap NumberBox class
25362  * @cfg {String} headline Box headline
25363  * @cfg {String} content Box content
25364  * @cfg {String} icon Box icon
25365  * @cfg {String} footer Footer text
25366  * @cfg {String} fhref Footer href
25367  * 
25368  * @constructor
25369  * Create a new NumberBox
25370  * @param {Object} config The config object
25371  */
25372
25373
25374 Roo.bootstrap.dash.NumberBox = function(config){
25375     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25376     
25377 };
25378
25379 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25380     
25381     headline : '',
25382     content : '',
25383     icon : '',
25384     footer : '',
25385     fhref : '',
25386     ficon : '',
25387     
25388     getAutoCreate : function(){
25389         
25390         var cfg = {
25391             tag : 'div',
25392             cls : 'small-box ',
25393             cn : [
25394                 {
25395                     tag : 'div',
25396                     cls : 'inner',
25397                     cn :[
25398                         {
25399                             tag : 'h3',
25400                             cls : 'roo-headline',
25401                             html : this.headline
25402                         },
25403                         {
25404                             tag : 'p',
25405                             cls : 'roo-content',
25406                             html : this.content
25407                         }
25408                     ]
25409                 }
25410             ]
25411         };
25412         
25413         if(this.icon){
25414             cfg.cn.push({
25415                 tag : 'div',
25416                 cls : 'icon',
25417                 cn :[
25418                     {
25419                         tag : 'i',
25420                         cls : 'ion ' + this.icon
25421                     }
25422                 ]
25423             });
25424         }
25425         
25426         if(this.footer){
25427             var footer = {
25428                 tag : 'a',
25429                 cls : 'small-box-footer',
25430                 href : this.fhref || '#',
25431                 html : this.footer
25432             };
25433             
25434             cfg.cn.push(footer);
25435             
25436         }
25437         
25438         return  cfg;
25439     },
25440
25441     onRender : function(ct,position){
25442         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25443
25444
25445        
25446                 
25447     },
25448
25449     setHeadline: function (value)
25450     {
25451         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25452     },
25453     
25454     setFooter: function (value, href)
25455     {
25456         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25457         
25458         if(href){
25459             this.el.select('a.small-box-footer',true).first().attr('href', href);
25460         }
25461         
25462     },
25463
25464     setContent: function (value)
25465     {
25466         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25467     },
25468
25469     initEvents: function() 
25470     {   
25471         
25472     }
25473     
25474 });
25475
25476  
25477 /*
25478  * - LGPL
25479  *
25480  * TabBox
25481  * 
25482  */
25483 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25484
25485 /**
25486  * @class Roo.bootstrap.dash.TabBox
25487  * @extends Roo.bootstrap.Component
25488  * Bootstrap TabBox class
25489  * @cfg {String} title Title of the TabBox
25490  * @cfg {String} icon Icon of the TabBox
25491  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25492  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25493  * 
25494  * @constructor
25495  * Create a new TabBox
25496  * @param {Object} config The config object
25497  */
25498
25499
25500 Roo.bootstrap.dash.TabBox = function(config){
25501     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25502     this.addEvents({
25503         // raw events
25504         /**
25505          * @event addpane
25506          * When a pane is added
25507          * @param {Roo.bootstrap.dash.TabPane} pane
25508          */
25509         "addpane" : true,
25510         /**
25511          * @event activatepane
25512          * When a pane is activated
25513          * @param {Roo.bootstrap.dash.TabPane} pane
25514          */
25515         "activatepane" : true
25516         
25517          
25518     });
25519     
25520     this.panes = [];
25521 };
25522
25523 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25524
25525     title : '',
25526     icon : false,
25527     showtabs : true,
25528     tabScrollable : false,
25529     
25530     getChildContainer : function()
25531     {
25532         return this.el.select('.tab-content', true).first();
25533     },
25534     
25535     getAutoCreate : function(){
25536         
25537         var header = {
25538             tag: 'li',
25539             cls: 'pull-left header',
25540             html: this.title,
25541             cn : []
25542         };
25543         
25544         if(this.icon){
25545             header.cn.push({
25546                 tag: 'i',
25547                 cls: 'fa ' + this.icon
25548             });
25549         }
25550         
25551         var h = {
25552             tag: 'ul',
25553             cls: 'nav nav-tabs pull-right',
25554             cn: [
25555                 header
25556             ]
25557         };
25558         
25559         if(this.tabScrollable){
25560             h = {
25561                 tag: 'div',
25562                 cls: 'tab-header',
25563                 cn: [
25564                     {
25565                         tag: 'ul',
25566                         cls: 'nav nav-tabs pull-right',
25567                         cn: [
25568                             header
25569                         ]
25570                     }
25571                 ]
25572             };
25573         }
25574         
25575         var cfg = {
25576             tag: 'div',
25577             cls: 'nav-tabs-custom',
25578             cn: [
25579                 h,
25580                 {
25581                     tag: 'div',
25582                     cls: 'tab-content no-padding',
25583                     cn: []
25584                 }
25585             ]
25586         };
25587
25588         return  cfg;
25589     },
25590     initEvents : function()
25591     {
25592         //Roo.log('add add pane handler');
25593         this.on('addpane', this.onAddPane, this);
25594     },
25595      /**
25596      * Updates the box title
25597      * @param {String} html to set the title to.
25598      */
25599     setTitle : function(value)
25600     {
25601         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25602     },
25603     onAddPane : function(pane)
25604     {
25605         this.panes.push(pane);
25606         //Roo.log('addpane');
25607         //Roo.log(pane);
25608         // tabs are rendere left to right..
25609         if(!this.showtabs){
25610             return;
25611         }
25612         
25613         var ctr = this.el.select('.nav-tabs', true).first();
25614          
25615          
25616         var existing = ctr.select('.nav-tab',true);
25617         var qty = existing.getCount();;
25618         
25619         
25620         var tab = ctr.createChild({
25621             tag : 'li',
25622             cls : 'nav-tab' + (qty ? '' : ' active'),
25623             cn : [
25624                 {
25625                     tag : 'a',
25626                     href:'#',
25627                     html : pane.title
25628                 }
25629             ]
25630         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25631         pane.tab = tab;
25632         
25633         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25634         if (!qty) {
25635             pane.el.addClass('active');
25636         }
25637         
25638                 
25639     },
25640     onTabClick : function(ev,un,ob,pane)
25641     {
25642         //Roo.log('tab - prev default');
25643         ev.preventDefault();
25644         
25645         
25646         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25647         pane.tab.addClass('active');
25648         //Roo.log(pane.title);
25649         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25650         // technically we should have a deactivate event.. but maybe add later.
25651         // and it should not de-activate the selected tab...
25652         this.fireEvent('activatepane', pane);
25653         pane.el.addClass('active');
25654         pane.fireEvent('activate');
25655         
25656         
25657     },
25658     
25659     getActivePane : function()
25660     {
25661         var r = false;
25662         Roo.each(this.panes, function(p) {
25663             if(p.el.hasClass('active')){
25664                 r = p;
25665                 return false;
25666             }
25667             
25668             return;
25669         });
25670         
25671         return r;
25672     }
25673     
25674     
25675 });
25676
25677  
25678 /*
25679  * - LGPL
25680  *
25681  * Tab pane
25682  * 
25683  */
25684 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25685 /**
25686  * @class Roo.bootstrap.TabPane
25687  * @extends Roo.bootstrap.Component
25688  * Bootstrap TabPane class
25689  * @cfg {Boolean} active (false | true) Default false
25690  * @cfg {String} title title of panel
25691
25692  * 
25693  * @constructor
25694  * Create a new TabPane
25695  * @param {Object} config The config object
25696  */
25697
25698 Roo.bootstrap.dash.TabPane = function(config){
25699     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25700     
25701     this.addEvents({
25702         // raw events
25703         /**
25704          * @event activate
25705          * When a pane is activated
25706          * @param {Roo.bootstrap.dash.TabPane} pane
25707          */
25708         "activate" : true
25709          
25710     });
25711 };
25712
25713 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25714     
25715     active : false,
25716     title : '',
25717     
25718     // the tabBox that this is attached to.
25719     tab : false,
25720      
25721     getAutoCreate : function() 
25722     {
25723         var cfg = {
25724             tag: 'div',
25725             cls: 'tab-pane'
25726         };
25727         
25728         if(this.active){
25729             cfg.cls += ' active';
25730         }
25731         
25732         return cfg;
25733     },
25734     initEvents  : function()
25735     {
25736         //Roo.log('trigger add pane handler');
25737         this.parent().fireEvent('addpane', this)
25738     },
25739     
25740      /**
25741      * Updates the tab title 
25742      * @param {String} html to set the title to.
25743      */
25744     setTitle: function(str)
25745     {
25746         if (!this.tab) {
25747             return;
25748         }
25749         this.title = str;
25750         this.tab.select('a', true).first().dom.innerHTML = str;
25751         
25752     }
25753     
25754     
25755     
25756 });
25757
25758  
25759
25760
25761  /*
25762  * - LGPL
25763  *
25764  * menu
25765  * 
25766  */
25767 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25768
25769 /**
25770  * @class Roo.bootstrap.menu.Menu
25771  * @extends Roo.bootstrap.Component
25772  * Bootstrap Menu class - container for Menu
25773  * @cfg {String} html Text of the menu
25774  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25775  * @cfg {String} icon Font awesome icon
25776  * @cfg {String} pos Menu align to (top | bottom) default bottom
25777  * 
25778  * 
25779  * @constructor
25780  * Create a new Menu
25781  * @param {Object} config The config object
25782  */
25783
25784
25785 Roo.bootstrap.menu.Menu = function(config){
25786     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25787     
25788     this.addEvents({
25789         /**
25790          * @event beforeshow
25791          * Fires before this menu is displayed
25792          * @param {Roo.bootstrap.menu.Menu} this
25793          */
25794         beforeshow : true,
25795         /**
25796          * @event beforehide
25797          * Fires before this menu is hidden
25798          * @param {Roo.bootstrap.menu.Menu} this
25799          */
25800         beforehide : true,
25801         /**
25802          * @event show
25803          * Fires after this menu is displayed
25804          * @param {Roo.bootstrap.menu.Menu} this
25805          */
25806         show : true,
25807         /**
25808          * @event hide
25809          * Fires after this menu is hidden
25810          * @param {Roo.bootstrap.menu.Menu} this
25811          */
25812         hide : true,
25813         /**
25814          * @event click
25815          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25816          * @param {Roo.bootstrap.menu.Menu} this
25817          * @param {Roo.EventObject} e
25818          */
25819         click : true
25820     });
25821     
25822 };
25823
25824 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25825     
25826     submenu : false,
25827     html : '',
25828     weight : 'default',
25829     icon : false,
25830     pos : 'bottom',
25831     
25832     
25833     getChildContainer : function() {
25834         if(this.isSubMenu){
25835             return this.el;
25836         }
25837         
25838         return this.el.select('ul.dropdown-menu', true).first();  
25839     },
25840     
25841     getAutoCreate : function()
25842     {
25843         var text = [
25844             {
25845                 tag : 'span',
25846                 cls : 'roo-menu-text',
25847                 html : this.html
25848             }
25849         ];
25850         
25851         if(this.icon){
25852             text.unshift({
25853                 tag : 'i',
25854                 cls : 'fa ' + this.icon
25855             })
25856         }
25857         
25858         
25859         var cfg = {
25860             tag : 'div',
25861             cls : 'btn-group',
25862             cn : [
25863                 {
25864                     tag : 'button',
25865                     cls : 'dropdown-button btn btn-' + this.weight,
25866                     cn : text
25867                 },
25868                 {
25869                     tag : 'button',
25870                     cls : 'dropdown-toggle btn btn-' + this.weight,
25871                     cn : [
25872                         {
25873                             tag : 'span',
25874                             cls : 'caret'
25875                         }
25876                     ]
25877                 },
25878                 {
25879                     tag : 'ul',
25880                     cls : 'dropdown-menu'
25881                 }
25882             ]
25883             
25884         };
25885         
25886         if(this.pos == 'top'){
25887             cfg.cls += ' dropup';
25888         }
25889         
25890         if(this.isSubMenu){
25891             cfg = {
25892                 tag : 'ul',
25893                 cls : 'dropdown-menu'
25894             }
25895         }
25896         
25897         return cfg;
25898     },
25899     
25900     onRender : function(ct, position)
25901     {
25902         this.isSubMenu = ct.hasClass('dropdown-submenu');
25903         
25904         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25905     },
25906     
25907     initEvents : function() 
25908     {
25909         if(this.isSubMenu){
25910             return;
25911         }
25912         
25913         this.hidden = true;
25914         
25915         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25916         this.triggerEl.on('click', this.onTriggerPress, this);
25917         
25918         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25919         this.buttonEl.on('click', this.onClick, this);
25920         
25921     },
25922     
25923     list : function()
25924     {
25925         if(this.isSubMenu){
25926             return this.el;
25927         }
25928         
25929         return this.el.select('ul.dropdown-menu', true).first();
25930     },
25931     
25932     onClick : function(e)
25933     {
25934         this.fireEvent("click", this, e);
25935     },
25936     
25937     onTriggerPress  : function(e)
25938     {   
25939         if (this.isVisible()) {
25940             this.hide();
25941         } else {
25942             this.show();
25943         }
25944     },
25945     
25946     isVisible : function(){
25947         return !this.hidden;
25948     },
25949     
25950     show : function()
25951     {
25952         this.fireEvent("beforeshow", this);
25953         
25954         this.hidden = false;
25955         this.el.addClass('open');
25956         
25957         Roo.get(document).on("mouseup", this.onMouseUp, this);
25958         
25959         this.fireEvent("show", this);
25960         
25961         
25962     },
25963     
25964     hide : function()
25965     {
25966         this.fireEvent("beforehide", this);
25967         
25968         this.hidden = true;
25969         this.el.removeClass('open');
25970         
25971         Roo.get(document).un("mouseup", this.onMouseUp);
25972         
25973         this.fireEvent("hide", this);
25974     },
25975     
25976     onMouseUp : function()
25977     {
25978         this.hide();
25979     }
25980     
25981 });
25982
25983  
25984  /*
25985  * - LGPL
25986  *
25987  * menu item
25988  * 
25989  */
25990 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25991
25992 /**
25993  * @class Roo.bootstrap.menu.Item
25994  * @extends Roo.bootstrap.Component
25995  * Bootstrap MenuItem class
25996  * @cfg {Boolean} submenu (true | false) default false
25997  * @cfg {String} html text of the item
25998  * @cfg {String} href the link
25999  * @cfg {Boolean} disable (true | false) default false
26000  * @cfg {Boolean} preventDefault (true | false) default true
26001  * @cfg {String} icon Font awesome icon
26002  * @cfg {String} pos Submenu align to (left | right) default right 
26003  * 
26004  * 
26005  * @constructor
26006  * Create a new Item
26007  * @param {Object} config The config object
26008  */
26009
26010
26011 Roo.bootstrap.menu.Item = function(config){
26012     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26013     this.addEvents({
26014         /**
26015          * @event mouseover
26016          * Fires when the mouse is hovering over this menu
26017          * @param {Roo.bootstrap.menu.Item} this
26018          * @param {Roo.EventObject} e
26019          */
26020         mouseover : true,
26021         /**
26022          * @event mouseout
26023          * Fires when the mouse exits this menu
26024          * @param {Roo.bootstrap.menu.Item} this
26025          * @param {Roo.EventObject} e
26026          */
26027         mouseout : true,
26028         // raw events
26029         /**
26030          * @event click
26031          * The raw click event for the entire grid.
26032          * @param {Roo.EventObject} e
26033          */
26034         click : true
26035     });
26036 };
26037
26038 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26039     
26040     submenu : false,
26041     href : '',
26042     html : '',
26043     preventDefault: true,
26044     disable : false,
26045     icon : false,
26046     pos : 'right',
26047     
26048     getAutoCreate : function()
26049     {
26050         var text = [
26051             {
26052                 tag : 'span',
26053                 cls : 'roo-menu-item-text',
26054                 html : this.html
26055             }
26056         ];
26057         
26058         if(this.icon){
26059             text.unshift({
26060                 tag : 'i',
26061                 cls : 'fa ' + this.icon
26062             })
26063         }
26064         
26065         var cfg = {
26066             tag : 'li',
26067             cn : [
26068                 {
26069                     tag : 'a',
26070                     href : this.href || '#',
26071                     cn : text
26072                 }
26073             ]
26074         };
26075         
26076         if(this.disable){
26077             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26078         }
26079         
26080         if(this.submenu){
26081             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26082             
26083             if(this.pos == 'left'){
26084                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26085             }
26086         }
26087         
26088         return cfg;
26089     },
26090     
26091     initEvents : function() 
26092     {
26093         this.el.on('mouseover', this.onMouseOver, this);
26094         this.el.on('mouseout', this.onMouseOut, this);
26095         
26096         this.el.select('a', true).first().on('click', this.onClick, this);
26097         
26098     },
26099     
26100     onClick : function(e)
26101     {
26102         if(this.preventDefault){
26103             e.preventDefault();
26104         }
26105         
26106         this.fireEvent("click", this, e);
26107     },
26108     
26109     onMouseOver : function(e)
26110     {
26111         if(this.submenu && this.pos == 'left'){
26112             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26113         }
26114         
26115         this.fireEvent("mouseover", this, e);
26116     },
26117     
26118     onMouseOut : function(e)
26119     {
26120         this.fireEvent("mouseout", this, e);
26121     }
26122 });
26123
26124  
26125
26126  /*
26127  * - LGPL
26128  *
26129  * menu separator
26130  * 
26131  */
26132 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26133
26134 /**
26135  * @class Roo.bootstrap.menu.Separator
26136  * @extends Roo.bootstrap.Component
26137  * Bootstrap Separator class
26138  * 
26139  * @constructor
26140  * Create a new Separator
26141  * @param {Object} config The config object
26142  */
26143
26144
26145 Roo.bootstrap.menu.Separator = function(config){
26146     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26147 };
26148
26149 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26150     
26151     getAutoCreate : function(){
26152         var cfg = {
26153             tag : 'li',
26154             cls: 'divider'
26155         };
26156         
26157         return cfg;
26158     }
26159    
26160 });
26161
26162  
26163
26164  /*
26165  * - LGPL
26166  *
26167  * Tooltip
26168  * 
26169  */
26170
26171 /**
26172  * @class Roo.bootstrap.Tooltip
26173  * Bootstrap Tooltip class
26174  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26175  * to determine which dom element triggers the tooltip.
26176  * 
26177  * It needs to add support for additional attributes like tooltip-position
26178  * 
26179  * @constructor
26180  * Create a new Toolti
26181  * @param {Object} config The config object
26182  */
26183
26184 Roo.bootstrap.Tooltip = function(config){
26185     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26186     
26187     this.alignment = Roo.bootstrap.Tooltip.alignment;
26188     
26189     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26190         this.alignment = config.alignment;
26191     }
26192     
26193 };
26194
26195 Roo.apply(Roo.bootstrap.Tooltip, {
26196     /**
26197      * @function init initialize tooltip monitoring.
26198      * @static
26199      */
26200     currentEl : false,
26201     currentTip : false,
26202     currentRegion : false,
26203     
26204     //  init : delay?
26205     
26206     init : function()
26207     {
26208         Roo.get(document).on('mouseover', this.enter ,this);
26209         Roo.get(document).on('mouseout', this.leave, this);
26210          
26211         
26212         this.currentTip = new Roo.bootstrap.Tooltip();
26213     },
26214     
26215     enter : function(ev)
26216     {
26217         var dom = ev.getTarget();
26218         
26219         //Roo.log(['enter',dom]);
26220         var el = Roo.fly(dom);
26221         if (this.currentEl) {
26222             //Roo.log(dom);
26223             //Roo.log(this.currentEl);
26224             //Roo.log(this.currentEl.contains(dom));
26225             if (this.currentEl == el) {
26226                 return;
26227             }
26228             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26229                 return;
26230             }
26231
26232         }
26233         
26234         if (this.currentTip.el) {
26235             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26236         }    
26237         //Roo.log(ev);
26238         
26239         if(!el || el.dom == document){
26240             return;
26241         }
26242         
26243         var bindEl = el;
26244         
26245         // you can not look for children, as if el is the body.. then everythign is the child..
26246         if (!el.attr('tooltip')) { //
26247             if (!el.select("[tooltip]").elements.length) {
26248                 return;
26249             }
26250             // is the mouse over this child...?
26251             bindEl = el.select("[tooltip]").first();
26252             var xy = ev.getXY();
26253             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26254                 //Roo.log("not in region.");
26255                 return;
26256             }
26257             //Roo.log("child element over..");
26258             
26259         }
26260         this.currentEl = bindEl;
26261         this.currentTip.bind(bindEl);
26262         this.currentRegion = Roo.lib.Region.getRegion(dom);
26263         this.currentTip.enter();
26264         
26265     },
26266     leave : function(ev)
26267     {
26268         var dom = ev.getTarget();
26269         //Roo.log(['leave',dom]);
26270         if (!this.currentEl) {
26271             return;
26272         }
26273         
26274         
26275         if (dom != this.currentEl.dom) {
26276             return;
26277         }
26278         var xy = ev.getXY();
26279         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26280             return;
26281         }
26282         // only activate leave if mouse cursor is outside... bounding box..
26283         
26284         
26285         
26286         
26287         if (this.currentTip) {
26288             this.currentTip.leave();
26289         }
26290         //Roo.log('clear currentEl');
26291         this.currentEl = false;
26292         
26293         
26294     },
26295     alignment : {
26296         'left' : ['r-l', [-2,0], 'right'],
26297         'right' : ['l-r', [2,0], 'left'],
26298         'bottom' : ['t-b', [0,2], 'top'],
26299         'top' : [ 'b-t', [0,-2], 'bottom']
26300     }
26301     
26302 });
26303
26304
26305 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26306     
26307     
26308     bindEl : false,
26309     
26310     delay : null, // can be { show : 300 , hide: 500}
26311     
26312     timeout : null,
26313     
26314     hoverState : null, //???
26315     
26316     placement : 'bottom', 
26317     
26318     alignment : false,
26319     
26320     getAutoCreate : function(){
26321     
26322         var cfg = {
26323            cls : 'tooltip',
26324            role : 'tooltip',
26325            cn : [
26326                 {
26327                     cls : 'tooltip-arrow'
26328                 },
26329                 {
26330                     cls : 'tooltip-inner'
26331                 }
26332            ]
26333         };
26334         
26335         return cfg;
26336     },
26337     bind : function(el)
26338     {
26339         this.bindEl = el;
26340     },
26341       
26342     
26343     enter : function () {
26344        
26345         if (this.timeout != null) {
26346             clearTimeout(this.timeout);
26347         }
26348         
26349         this.hoverState = 'in';
26350          //Roo.log("enter - show");
26351         if (!this.delay || !this.delay.show) {
26352             this.show();
26353             return;
26354         }
26355         var _t = this;
26356         this.timeout = setTimeout(function () {
26357             if (_t.hoverState == 'in') {
26358                 _t.show();
26359             }
26360         }, this.delay.show);
26361     },
26362     leave : function()
26363     {
26364         clearTimeout(this.timeout);
26365     
26366         this.hoverState = 'out';
26367          if (!this.delay || !this.delay.hide) {
26368             this.hide();
26369             return;
26370         }
26371        
26372         var _t = this;
26373         this.timeout = setTimeout(function () {
26374             //Roo.log("leave - timeout");
26375             
26376             if (_t.hoverState == 'out') {
26377                 _t.hide();
26378                 Roo.bootstrap.Tooltip.currentEl = false;
26379             }
26380         }, delay);
26381     },
26382     
26383     show : function (msg)
26384     {
26385         if (!this.el) {
26386             this.render(document.body);
26387         }
26388         // set content.
26389         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26390         
26391         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26392         
26393         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26394         
26395         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26396         
26397         var placement = typeof this.placement == 'function' ?
26398             this.placement.call(this, this.el, on_el) :
26399             this.placement;
26400             
26401         var autoToken = /\s?auto?\s?/i;
26402         var autoPlace = autoToken.test(placement);
26403         if (autoPlace) {
26404             placement = placement.replace(autoToken, '') || 'top';
26405         }
26406         
26407         //this.el.detach()
26408         //this.el.setXY([0,0]);
26409         this.el.show();
26410         //this.el.dom.style.display='block';
26411         
26412         //this.el.appendTo(on_el);
26413         
26414         var p = this.getPosition();
26415         var box = this.el.getBox();
26416         
26417         if (autoPlace) {
26418             // fixme..
26419         }
26420         
26421         var align = this.alignment[placement];
26422         
26423         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26424         
26425         if(placement == 'top' || placement == 'bottom'){
26426             if(xy[0] < 0){
26427                 placement = 'right';
26428             }
26429             
26430             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26431                 placement = 'left';
26432             }
26433             
26434             var scroll = Roo.select('body', true).first().getScroll();
26435             
26436             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26437                 placement = 'top';
26438             }
26439             
26440             align = this.alignment[placement];
26441         }
26442         
26443         this.el.alignTo(this.bindEl, align[0],align[1]);
26444         //var arrow = this.el.select('.arrow',true).first();
26445         //arrow.set(align[2], 
26446         
26447         this.el.addClass(placement);
26448         
26449         this.el.addClass('in fade');
26450         
26451         this.hoverState = null;
26452         
26453         if (this.el.hasClass('fade')) {
26454             // fade it?
26455         }
26456         
26457     },
26458     hide : function()
26459     {
26460          
26461         if (!this.el) {
26462             return;
26463         }
26464         //this.el.setXY([0,0]);
26465         this.el.removeClass('in');
26466         //this.el.hide();
26467         
26468     }
26469     
26470 });
26471  
26472
26473  /*
26474  * - LGPL
26475  *
26476  * Location Picker
26477  * 
26478  */
26479
26480 /**
26481  * @class Roo.bootstrap.LocationPicker
26482  * @extends Roo.bootstrap.Component
26483  * Bootstrap LocationPicker class
26484  * @cfg {Number} latitude Position when init default 0
26485  * @cfg {Number} longitude Position when init default 0
26486  * @cfg {Number} zoom default 15
26487  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26488  * @cfg {Boolean} mapTypeControl default false
26489  * @cfg {Boolean} disableDoubleClickZoom default false
26490  * @cfg {Boolean} scrollwheel default true
26491  * @cfg {Boolean} streetViewControl default false
26492  * @cfg {Number} radius default 0
26493  * @cfg {String} locationName
26494  * @cfg {Boolean} draggable default true
26495  * @cfg {Boolean} enableAutocomplete default false
26496  * @cfg {Boolean} enableReverseGeocode default true
26497  * @cfg {String} markerTitle
26498  * 
26499  * @constructor
26500  * Create a new LocationPicker
26501  * @param {Object} config The config object
26502  */
26503
26504
26505 Roo.bootstrap.LocationPicker = function(config){
26506     
26507     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26508     
26509     this.addEvents({
26510         /**
26511          * @event initial
26512          * Fires when the picker initialized.
26513          * @param {Roo.bootstrap.LocationPicker} this
26514          * @param {Google Location} location
26515          */
26516         initial : true,
26517         /**
26518          * @event positionchanged
26519          * Fires when the picker position changed.
26520          * @param {Roo.bootstrap.LocationPicker} this
26521          * @param {Google Location} location
26522          */
26523         positionchanged : true,
26524         /**
26525          * @event resize
26526          * Fires when the map resize.
26527          * @param {Roo.bootstrap.LocationPicker} this
26528          */
26529         resize : true,
26530         /**
26531          * @event show
26532          * Fires when the map show.
26533          * @param {Roo.bootstrap.LocationPicker} this
26534          */
26535         show : true,
26536         /**
26537          * @event hide
26538          * Fires when the map hide.
26539          * @param {Roo.bootstrap.LocationPicker} this
26540          */
26541         hide : true,
26542         /**
26543          * @event mapClick
26544          * Fires when click the map.
26545          * @param {Roo.bootstrap.LocationPicker} this
26546          * @param {Map event} e
26547          */
26548         mapClick : true,
26549         /**
26550          * @event mapRightClick
26551          * Fires when right click the map.
26552          * @param {Roo.bootstrap.LocationPicker} this
26553          * @param {Map event} e
26554          */
26555         mapRightClick : true,
26556         /**
26557          * @event markerClick
26558          * Fires when click the marker.
26559          * @param {Roo.bootstrap.LocationPicker} this
26560          * @param {Map event} e
26561          */
26562         markerClick : true,
26563         /**
26564          * @event markerRightClick
26565          * Fires when right click the marker.
26566          * @param {Roo.bootstrap.LocationPicker} this
26567          * @param {Map event} e
26568          */
26569         markerRightClick : true,
26570         /**
26571          * @event OverlayViewDraw
26572          * Fires when OverlayView Draw
26573          * @param {Roo.bootstrap.LocationPicker} this
26574          */
26575         OverlayViewDraw : true,
26576         /**
26577          * @event OverlayViewOnAdd
26578          * Fires when OverlayView Draw
26579          * @param {Roo.bootstrap.LocationPicker} this
26580          */
26581         OverlayViewOnAdd : true,
26582         /**
26583          * @event OverlayViewOnRemove
26584          * Fires when OverlayView Draw
26585          * @param {Roo.bootstrap.LocationPicker} this
26586          */
26587         OverlayViewOnRemove : true,
26588         /**
26589          * @event OverlayViewShow
26590          * Fires when OverlayView Draw
26591          * @param {Roo.bootstrap.LocationPicker} this
26592          * @param {Pixel} cpx
26593          */
26594         OverlayViewShow : true,
26595         /**
26596          * @event OverlayViewHide
26597          * Fires when OverlayView Draw
26598          * @param {Roo.bootstrap.LocationPicker} this
26599          */
26600         OverlayViewHide : true,
26601         /**
26602          * @event loadexception
26603          * Fires when load google lib failed.
26604          * @param {Roo.bootstrap.LocationPicker} this
26605          */
26606         loadexception : true
26607     });
26608         
26609 };
26610
26611 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26612     
26613     gMapContext: false,
26614     
26615     latitude: 0,
26616     longitude: 0,
26617     zoom: 15,
26618     mapTypeId: false,
26619     mapTypeControl: false,
26620     disableDoubleClickZoom: false,
26621     scrollwheel: true,
26622     streetViewControl: false,
26623     radius: 0,
26624     locationName: '',
26625     draggable: true,
26626     enableAutocomplete: false,
26627     enableReverseGeocode: true,
26628     markerTitle: '',
26629     
26630     getAutoCreate: function()
26631     {
26632
26633         var cfg = {
26634             tag: 'div',
26635             cls: 'roo-location-picker'
26636         };
26637         
26638         return cfg
26639     },
26640     
26641     initEvents: function(ct, position)
26642     {       
26643         if(!this.el.getWidth() || this.isApplied()){
26644             return;
26645         }
26646         
26647         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26648         
26649         this.initial();
26650     },
26651     
26652     initial: function()
26653     {
26654         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26655             this.fireEvent('loadexception', this);
26656             return;
26657         }
26658         
26659         if(!this.mapTypeId){
26660             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26661         }
26662         
26663         this.gMapContext = this.GMapContext();
26664         
26665         this.initOverlayView();
26666         
26667         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26668         
26669         var _this = this;
26670                 
26671         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26672             _this.setPosition(_this.gMapContext.marker.position);
26673         });
26674         
26675         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26676             _this.fireEvent('mapClick', this, event);
26677             
26678         });
26679
26680         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26681             _this.fireEvent('mapRightClick', this, event);
26682             
26683         });
26684         
26685         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26686             _this.fireEvent('markerClick', this, event);
26687             
26688         });
26689
26690         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26691             _this.fireEvent('markerRightClick', this, event);
26692             
26693         });
26694         
26695         this.setPosition(this.gMapContext.location);
26696         
26697         this.fireEvent('initial', this, this.gMapContext.location);
26698     },
26699     
26700     initOverlayView: function()
26701     {
26702         var _this = this;
26703         
26704         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26705             
26706             draw: function()
26707             {
26708                 _this.fireEvent('OverlayViewDraw', _this);
26709             },
26710             
26711             onAdd: function()
26712             {
26713                 _this.fireEvent('OverlayViewOnAdd', _this);
26714             },
26715             
26716             onRemove: function()
26717             {
26718                 _this.fireEvent('OverlayViewOnRemove', _this);
26719             },
26720             
26721             show: function(cpx)
26722             {
26723                 _this.fireEvent('OverlayViewShow', _this, cpx);
26724             },
26725             
26726             hide: function()
26727             {
26728                 _this.fireEvent('OverlayViewHide', _this);
26729             }
26730             
26731         });
26732     },
26733     
26734     fromLatLngToContainerPixel: function(event)
26735     {
26736         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26737     },
26738     
26739     isApplied: function() 
26740     {
26741         return this.getGmapContext() == false ? false : true;
26742     },
26743     
26744     getGmapContext: function() 
26745     {
26746         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26747     },
26748     
26749     GMapContext: function() 
26750     {
26751         var position = new google.maps.LatLng(this.latitude, this.longitude);
26752         
26753         var _map = new google.maps.Map(this.el.dom, {
26754             center: position,
26755             zoom: this.zoom,
26756             mapTypeId: this.mapTypeId,
26757             mapTypeControl: this.mapTypeControl,
26758             disableDoubleClickZoom: this.disableDoubleClickZoom,
26759             scrollwheel: this.scrollwheel,
26760             streetViewControl: this.streetViewControl,
26761             locationName: this.locationName,
26762             draggable: this.draggable,
26763             enableAutocomplete: this.enableAutocomplete,
26764             enableReverseGeocode: this.enableReverseGeocode
26765         });
26766         
26767         var _marker = new google.maps.Marker({
26768             position: position,
26769             map: _map,
26770             title: this.markerTitle,
26771             draggable: this.draggable
26772         });
26773         
26774         return {
26775             map: _map,
26776             marker: _marker,
26777             circle: null,
26778             location: position,
26779             radius: this.radius,
26780             locationName: this.locationName,
26781             addressComponents: {
26782                 formatted_address: null,
26783                 addressLine1: null,
26784                 addressLine2: null,
26785                 streetName: null,
26786                 streetNumber: null,
26787                 city: null,
26788                 district: null,
26789                 state: null,
26790                 stateOrProvince: null
26791             },
26792             settings: this,
26793             domContainer: this.el.dom,
26794             geodecoder: new google.maps.Geocoder()
26795         };
26796     },
26797     
26798     drawCircle: function(center, radius, options) 
26799     {
26800         if (this.gMapContext.circle != null) {
26801             this.gMapContext.circle.setMap(null);
26802         }
26803         if (radius > 0) {
26804             radius *= 1;
26805             options = Roo.apply({}, options, {
26806                 strokeColor: "#0000FF",
26807                 strokeOpacity: .35,
26808                 strokeWeight: 2,
26809                 fillColor: "#0000FF",
26810                 fillOpacity: .2
26811             });
26812             
26813             options.map = this.gMapContext.map;
26814             options.radius = radius;
26815             options.center = center;
26816             this.gMapContext.circle = new google.maps.Circle(options);
26817             return this.gMapContext.circle;
26818         }
26819         
26820         return null;
26821     },
26822     
26823     setPosition: function(location) 
26824     {
26825         this.gMapContext.location = location;
26826         this.gMapContext.marker.setPosition(location);
26827         this.gMapContext.map.panTo(location);
26828         this.drawCircle(location, this.gMapContext.radius, {});
26829         
26830         var _this = this;
26831         
26832         if (this.gMapContext.settings.enableReverseGeocode) {
26833             this.gMapContext.geodecoder.geocode({
26834                 latLng: this.gMapContext.location
26835             }, function(results, status) {
26836                 
26837                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26838                     _this.gMapContext.locationName = results[0].formatted_address;
26839                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26840                     
26841                     _this.fireEvent('positionchanged', this, location);
26842                 }
26843             });
26844             
26845             return;
26846         }
26847         
26848         this.fireEvent('positionchanged', this, location);
26849     },
26850     
26851     resize: function()
26852     {
26853         google.maps.event.trigger(this.gMapContext.map, "resize");
26854         
26855         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26856         
26857         this.fireEvent('resize', this);
26858     },
26859     
26860     setPositionByLatLng: function(latitude, longitude)
26861     {
26862         this.setPosition(new google.maps.LatLng(latitude, longitude));
26863     },
26864     
26865     getCurrentPosition: function() 
26866     {
26867         return {
26868             latitude: this.gMapContext.location.lat(),
26869             longitude: this.gMapContext.location.lng()
26870         };
26871     },
26872     
26873     getAddressName: function() 
26874     {
26875         return this.gMapContext.locationName;
26876     },
26877     
26878     getAddressComponents: function() 
26879     {
26880         return this.gMapContext.addressComponents;
26881     },
26882     
26883     address_component_from_google_geocode: function(address_components) 
26884     {
26885         var result = {};
26886         
26887         for (var i = 0; i < address_components.length; i++) {
26888             var component = address_components[i];
26889             if (component.types.indexOf("postal_code") >= 0) {
26890                 result.postalCode = component.short_name;
26891             } else if (component.types.indexOf("street_number") >= 0) {
26892                 result.streetNumber = component.short_name;
26893             } else if (component.types.indexOf("route") >= 0) {
26894                 result.streetName = component.short_name;
26895             } else if (component.types.indexOf("neighborhood") >= 0) {
26896                 result.city = component.short_name;
26897             } else if (component.types.indexOf("locality") >= 0) {
26898                 result.city = component.short_name;
26899             } else if (component.types.indexOf("sublocality") >= 0) {
26900                 result.district = component.short_name;
26901             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26902                 result.stateOrProvince = component.short_name;
26903             } else if (component.types.indexOf("country") >= 0) {
26904                 result.country = component.short_name;
26905             }
26906         }
26907         
26908         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26909         result.addressLine2 = "";
26910         return result;
26911     },
26912     
26913     setZoomLevel: function(zoom)
26914     {
26915         this.gMapContext.map.setZoom(zoom);
26916     },
26917     
26918     show: function()
26919     {
26920         if(!this.el){
26921             return;
26922         }
26923         
26924         this.el.show();
26925         
26926         this.resize();
26927         
26928         this.fireEvent('show', this);
26929     },
26930     
26931     hide: function()
26932     {
26933         if(!this.el){
26934             return;
26935         }
26936         
26937         this.el.hide();
26938         
26939         this.fireEvent('hide', this);
26940     }
26941     
26942 });
26943
26944 Roo.apply(Roo.bootstrap.LocationPicker, {
26945     
26946     OverlayView : function(map, options)
26947     {
26948         options = options || {};
26949         
26950         this.setMap(map);
26951     }
26952     
26953     
26954 });/*
26955  * - LGPL
26956  *
26957  * Alert
26958  * 
26959  */
26960
26961 /**
26962  * @class Roo.bootstrap.Alert
26963  * @extends Roo.bootstrap.Component
26964  * Bootstrap Alert class
26965  * @cfg {String} title The title of alert
26966  * @cfg {String} html The content of alert
26967  * @cfg {String} weight (  success | info | warning | danger )
26968  * @cfg {String} faicon font-awesomeicon
26969  * 
26970  * @constructor
26971  * Create a new alert
26972  * @param {Object} config The config object
26973  */
26974
26975
26976 Roo.bootstrap.Alert = function(config){
26977     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26978     
26979 };
26980
26981 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26982     
26983     title: '',
26984     html: '',
26985     weight: false,
26986     faicon: false,
26987     
26988     getAutoCreate : function()
26989     {
26990         
26991         var cfg = {
26992             tag : 'div',
26993             cls : 'alert',
26994             cn : [
26995                 {
26996                     tag : 'i',
26997                     cls : 'roo-alert-icon'
26998                     
26999                 },
27000                 {
27001                     tag : 'b',
27002                     cls : 'roo-alert-title',
27003                     html : this.title
27004                 },
27005                 {
27006                     tag : 'span',
27007                     cls : 'roo-alert-text',
27008                     html : this.html
27009                 }
27010             ]
27011         };
27012         
27013         if(this.faicon){
27014             cfg.cn[0].cls += ' fa ' + this.faicon;
27015         }
27016         
27017         if(this.weight){
27018             cfg.cls += ' alert-' + this.weight;
27019         }
27020         
27021         return cfg;
27022     },
27023     
27024     initEvents: function() 
27025     {
27026         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27027     },
27028     
27029     setTitle : function(str)
27030     {
27031         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27032     },
27033     
27034     setText : function(str)
27035     {
27036         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27037     },
27038     
27039     setWeight : function(weight)
27040     {
27041         if(this.weight){
27042             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27043         }
27044         
27045         this.weight = weight;
27046         
27047         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27048     },
27049     
27050     setIcon : function(icon)
27051     {
27052         if(this.faicon){
27053             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27054         }
27055         
27056         this.faicon = icon;
27057         
27058         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27059     },
27060     
27061     hide: function() 
27062     {
27063         this.el.hide();   
27064     },
27065     
27066     show: function() 
27067     {  
27068         this.el.show();   
27069     }
27070     
27071 });
27072
27073  
27074 /*
27075 * Licence: LGPL
27076 */
27077
27078 /**
27079  * @class Roo.bootstrap.UploadCropbox
27080  * @extends Roo.bootstrap.Component
27081  * Bootstrap UploadCropbox class
27082  * @cfg {String} emptyText show when image has been loaded
27083  * @cfg {String} rotateNotify show when image too small to rotate
27084  * @cfg {Number} errorTimeout default 3000
27085  * @cfg {Number} minWidth default 300
27086  * @cfg {Number} minHeight default 300
27087  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27088  * @cfg {Boolean} isDocument (true|false) default false
27089  * @cfg {String} url action url
27090  * @cfg {String} paramName default 'imageUpload'
27091  * @cfg {String} method default POST
27092  * @cfg {Boolean} loadMask (true|false) default true
27093  * @cfg {Boolean} loadingText default 'Loading...'
27094  * 
27095  * @constructor
27096  * Create a new UploadCropbox
27097  * @param {Object} config The config object
27098  */
27099
27100 Roo.bootstrap.UploadCropbox = function(config){
27101     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27102     
27103     this.addEvents({
27104         /**
27105          * @event beforeselectfile
27106          * Fire before select file
27107          * @param {Roo.bootstrap.UploadCropbox} this
27108          */
27109         "beforeselectfile" : true,
27110         /**
27111          * @event initial
27112          * Fire after initEvent
27113          * @param {Roo.bootstrap.UploadCropbox} this
27114          */
27115         "initial" : true,
27116         /**
27117          * @event crop
27118          * Fire after initEvent
27119          * @param {Roo.bootstrap.UploadCropbox} this
27120          * @param {String} data
27121          */
27122         "crop" : true,
27123         /**
27124          * @event prepare
27125          * Fire when preparing the file data
27126          * @param {Roo.bootstrap.UploadCropbox} this
27127          * @param {Object} file
27128          */
27129         "prepare" : true,
27130         /**
27131          * @event exception
27132          * Fire when get exception
27133          * @param {Roo.bootstrap.UploadCropbox} this
27134          * @param {XMLHttpRequest} xhr
27135          */
27136         "exception" : true,
27137         /**
27138          * @event beforeloadcanvas
27139          * Fire before load the canvas
27140          * @param {Roo.bootstrap.UploadCropbox} this
27141          * @param {String} src
27142          */
27143         "beforeloadcanvas" : true,
27144         /**
27145          * @event trash
27146          * Fire when trash image
27147          * @param {Roo.bootstrap.UploadCropbox} this
27148          */
27149         "trash" : true,
27150         /**
27151          * @event download
27152          * Fire when download the image
27153          * @param {Roo.bootstrap.UploadCropbox} this
27154          */
27155         "download" : true,
27156         /**
27157          * @event footerbuttonclick
27158          * Fire when footerbuttonclick
27159          * @param {Roo.bootstrap.UploadCropbox} this
27160          * @param {String} type
27161          */
27162         "footerbuttonclick" : true,
27163         /**
27164          * @event resize
27165          * Fire when resize
27166          * @param {Roo.bootstrap.UploadCropbox} this
27167          */
27168         "resize" : true,
27169         /**
27170          * @event rotate
27171          * Fire when rotate the image
27172          * @param {Roo.bootstrap.UploadCropbox} this
27173          * @param {String} pos
27174          */
27175         "rotate" : true,
27176         /**
27177          * @event inspect
27178          * Fire when inspect the file
27179          * @param {Roo.bootstrap.UploadCropbox} this
27180          * @param {Object} file
27181          */
27182         "inspect" : true,
27183         /**
27184          * @event upload
27185          * Fire when xhr upload the file
27186          * @param {Roo.bootstrap.UploadCropbox} this
27187          * @param {Object} data
27188          */
27189         "upload" : true,
27190         /**
27191          * @event arrange
27192          * Fire when arrange the file data
27193          * @param {Roo.bootstrap.UploadCropbox} this
27194          * @param {Object} formData
27195          */
27196         "arrange" : true
27197     });
27198     
27199     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27200 };
27201
27202 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27203     
27204     emptyText : 'Click to upload image',
27205     rotateNotify : 'Image is too small to rotate',
27206     errorTimeout : 3000,
27207     scale : 0,
27208     baseScale : 1,
27209     rotate : 0,
27210     dragable : false,
27211     pinching : false,
27212     mouseX : 0,
27213     mouseY : 0,
27214     cropData : false,
27215     minWidth : 300,
27216     minHeight : 300,
27217     file : false,
27218     exif : {},
27219     baseRotate : 1,
27220     cropType : 'image/jpeg',
27221     buttons : false,
27222     canvasLoaded : false,
27223     isDocument : false,
27224     method : 'POST',
27225     paramName : 'imageUpload',
27226     loadMask : true,
27227     loadingText : 'Loading...',
27228     maskEl : false,
27229     
27230     getAutoCreate : function()
27231     {
27232         var cfg = {
27233             tag : 'div',
27234             cls : 'roo-upload-cropbox',
27235             cn : [
27236                 {
27237                     tag : 'input',
27238                     cls : 'roo-upload-cropbox-selector',
27239                     type : 'file'
27240                 },
27241                 {
27242                     tag : 'div',
27243                     cls : 'roo-upload-cropbox-body',
27244                     style : 'cursor:pointer',
27245                     cn : [
27246                         {
27247                             tag : 'div',
27248                             cls : 'roo-upload-cropbox-preview'
27249                         },
27250                         {
27251                             tag : 'div',
27252                             cls : 'roo-upload-cropbox-thumb'
27253                         },
27254                         {
27255                             tag : 'div',
27256                             cls : 'roo-upload-cropbox-empty-notify',
27257                             html : this.emptyText
27258                         },
27259                         {
27260                             tag : 'div',
27261                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27262                             html : this.rotateNotify
27263                         }
27264                     ]
27265                 },
27266                 {
27267                     tag : 'div',
27268                     cls : 'roo-upload-cropbox-footer',
27269                     cn : {
27270                         tag : 'div',
27271                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27272                         cn : []
27273                     }
27274                 }
27275             ]
27276         };
27277         
27278         return cfg;
27279     },
27280     
27281     onRender : function(ct, position)
27282     {
27283         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27284         
27285         if (this.buttons.length) {
27286             
27287             Roo.each(this.buttons, function(bb) {
27288                 
27289                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27290                 
27291                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27292                 
27293             }, this);
27294         }
27295         
27296         if(this.loadMask){
27297             this.maskEl = this.el;
27298         }
27299     },
27300     
27301     initEvents : function()
27302     {
27303         this.urlAPI = (window.createObjectURL && window) || 
27304                                 (window.URL && URL.revokeObjectURL && URL) || 
27305                                 (window.webkitURL && webkitURL);
27306                         
27307         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27308         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27309         
27310         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27311         this.selectorEl.hide();
27312         
27313         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27314         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27315         
27316         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27317         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27318         this.thumbEl.hide();
27319         
27320         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27321         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27322         
27323         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27324         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27325         this.errorEl.hide();
27326         
27327         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27328         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27329         this.footerEl.hide();
27330         
27331         this.setThumbBoxSize();
27332         
27333         this.bind();
27334         
27335         this.resize();
27336         
27337         this.fireEvent('initial', this);
27338     },
27339
27340     bind : function()
27341     {
27342         var _this = this;
27343         
27344         window.addEventListener("resize", function() { _this.resize(); } );
27345         
27346         this.bodyEl.on('click', this.beforeSelectFile, this);
27347         
27348         if(Roo.isTouch){
27349             this.bodyEl.on('touchstart', this.onTouchStart, this);
27350             this.bodyEl.on('touchmove', this.onTouchMove, this);
27351             this.bodyEl.on('touchend', this.onTouchEnd, this);
27352         }
27353         
27354         if(!Roo.isTouch){
27355             this.bodyEl.on('mousedown', this.onMouseDown, this);
27356             this.bodyEl.on('mousemove', this.onMouseMove, this);
27357             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27358             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27359             Roo.get(document).on('mouseup', this.onMouseUp, this);
27360         }
27361         
27362         this.selectorEl.on('change', this.onFileSelected, this);
27363     },
27364     
27365     reset : function()
27366     {    
27367         this.scale = 0;
27368         this.baseScale = 1;
27369         this.rotate = 0;
27370         this.baseRotate = 1;
27371         this.dragable = false;
27372         this.pinching = false;
27373         this.mouseX = 0;
27374         this.mouseY = 0;
27375         this.cropData = false;
27376         this.notifyEl.dom.innerHTML = this.emptyText;
27377         
27378         this.selectorEl.dom.value = '';
27379         
27380     },
27381     
27382     resize : function()
27383     {
27384         if(this.fireEvent('resize', this) != false){
27385             this.setThumbBoxPosition();
27386             this.setCanvasPosition();
27387         }
27388     },
27389     
27390     onFooterButtonClick : function(e, el, o, type)
27391     {
27392         switch (type) {
27393             case 'rotate-left' :
27394                 this.onRotateLeft(e);
27395                 break;
27396             case 'rotate-right' :
27397                 this.onRotateRight(e);
27398                 break;
27399             case 'picture' :
27400                 this.beforeSelectFile(e);
27401                 break;
27402             case 'trash' :
27403                 this.trash(e);
27404                 break;
27405             case 'crop' :
27406                 this.crop(e);
27407                 break;
27408             case 'download' :
27409                 this.download(e);
27410                 break;
27411             default :
27412                 break;
27413         }
27414         
27415         this.fireEvent('footerbuttonclick', this, type);
27416     },
27417     
27418     beforeSelectFile : function(e)
27419     {
27420         e.preventDefault();
27421         
27422         if(this.fireEvent('beforeselectfile', this) != false){
27423             this.selectorEl.dom.click();
27424         }
27425     },
27426     
27427     onFileSelected : function(e)
27428     {
27429         e.preventDefault();
27430         
27431         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27432             return;
27433         }
27434         
27435         var file = this.selectorEl.dom.files[0];
27436         
27437         if(this.fireEvent('inspect', this, file) != false){
27438             this.prepare(file);
27439         }
27440         
27441     },
27442     
27443     trash : function(e)
27444     {
27445         this.fireEvent('trash', this);
27446     },
27447     
27448     download : function(e)
27449     {
27450         this.fireEvent('download', this);
27451     },
27452     
27453     loadCanvas : function(src)
27454     {   
27455         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27456             
27457             this.reset();
27458             
27459             this.imageEl = document.createElement('img');
27460             
27461             var _this = this;
27462             
27463             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27464             
27465             this.imageEl.src = src;
27466         }
27467     },
27468     
27469     onLoadCanvas : function()
27470     {   
27471         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27472         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27473         
27474         this.bodyEl.un('click', this.beforeSelectFile, this);
27475         
27476         this.notifyEl.hide();
27477         this.thumbEl.show();
27478         this.footerEl.show();
27479         
27480         this.baseRotateLevel();
27481         
27482         if(this.isDocument){
27483             this.setThumbBoxSize();
27484         }
27485         
27486         this.setThumbBoxPosition();
27487         
27488         this.baseScaleLevel();
27489         
27490         this.draw();
27491         
27492         this.resize();
27493         
27494         this.canvasLoaded = true;
27495         
27496         if(this.loadMask){
27497             this.maskEl.unmask();
27498         }
27499         
27500     },
27501     
27502     setCanvasPosition : function()
27503     {   
27504         if(!this.canvasEl){
27505             return;
27506         }
27507         
27508         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27509         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27510         
27511         this.previewEl.setLeft(pw);
27512         this.previewEl.setTop(ph);
27513         
27514     },
27515     
27516     onMouseDown : function(e)
27517     {   
27518         e.stopEvent();
27519         
27520         this.dragable = true;
27521         this.pinching = false;
27522         
27523         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27524             this.dragable = false;
27525             return;
27526         }
27527         
27528         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27529         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27530         
27531     },
27532     
27533     onMouseMove : function(e)
27534     {   
27535         e.stopEvent();
27536         
27537         if(!this.canvasLoaded){
27538             return;
27539         }
27540         
27541         if (!this.dragable){
27542             return;
27543         }
27544         
27545         var minX = Math.ceil(this.thumbEl.getLeft(true));
27546         var minY = Math.ceil(this.thumbEl.getTop(true));
27547         
27548         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27549         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27550         
27551         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27552         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27553         
27554         x = x - this.mouseX;
27555         y = y - this.mouseY;
27556         
27557         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27558         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27559         
27560         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27561         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27562         
27563         this.previewEl.setLeft(bgX);
27564         this.previewEl.setTop(bgY);
27565         
27566         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27567         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27568     },
27569     
27570     onMouseUp : function(e)
27571     {   
27572         e.stopEvent();
27573         
27574         this.dragable = false;
27575     },
27576     
27577     onMouseWheel : function(e)
27578     {   
27579         e.stopEvent();
27580         
27581         this.startScale = this.scale;
27582         
27583         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27584         
27585         if(!this.zoomable()){
27586             this.scale = this.startScale;
27587             return;
27588         }
27589         
27590         this.draw();
27591         
27592         return;
27593     },
27594     
27595     zoomable : function()
27596     {
27597         var minScale = this.thumbEl.getWidth() / this.minWidth;
27598         
27599         if(this.minWidth < this.minHeight){
27600             minScale = this.thumbEl.getHeight() / this.minHeight;
27601         }
27602         
27603         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27604         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27605         
27606         if(
27607                 this.isDocument &&
27608                 (this.rotate == 0 || this.rotate == 180) && 
27609                 (
27610                     width > this.imageEl.OriginWidth || 
27611                     height > this.imageEl.OriginHeight ||
27612                     (width < this.minWidth && height < this.minHeight)
27613                 )
27614         ){
27615             return false;
27616         }
27617         
27618         if(
27619                 this.isDocument &&
27620                 (this.rotate == 90 || this.rotate == 270) && 
27621                 (
27622                     width > this.imageEl.OriginWidth || 
27623                     height > this.imageEl.OriginHeight ||
27624                     (width < this.minHeight && height < this.minWidth)
27625                 )
27626         ){
27627             return false;
27628         }
27629         
27630         if(
27631                 !this.isDocument &&
27632                 (this.rotate == 0 || this.rotate == 180) && 
27633                 (
27634                     width < this.minWidth || 
27635                     width > this.imageEl.OriginWidth || 
27636                     height < this.minHeight || 
27637                     height > this.imageEl.OriginHeight
27638                 )
27639         ){
27640             return false;
27641         }
27642         
27643         if(
27644                 !this.isDocument &&
27645                 (this.rotate == 90 || this.rotate == 270) && 
27646                 (
27647                     width < this.minHeight || 
27648                     width > this.imageEl.OriginWidth || 
27649                     height < this.minWidth || 
27650                     height > this.imageEl.OriginHeight
27651                 )
27652         ){
27653             return false;
27654         }
27655         
27656         return true;
27657         
27658     },
27659     
27660     onRotateLeft : function(e)
27661     {   
27662         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27663             
27664             var minScale = this.thumbEl.getWidth() / this.minWidth;
27665             
27666             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27667             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27668             
27669             this.startScale = this.scale;
27670             
27671             while (this.getScaleLevel() < minScale){
27672             
27673                 this.scale = this.scale + 1;
27674                 
27675                 if(!this.zoomable()){
27676                     break;
27677                 }
27678                 
27679                 if(
27680                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27681                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27682                 ){
27683                     continue;
27684                 }
27685                 
27686                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27687
27688                 this.draw();
27689                 
27690                 return;
27691             }
27692             
27693             this.scale = this.startScale;
27694             
27695             this.onRotateFail();
27696             
27697             return false;
27698         }
27699         
27700         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27701
27702         if(this.isDocument){
27703             this.setThumbBoxSize();
27704             this.setThumbBoxPosition();
27705             this.setCanvasPosition();
27706         }
27707         
27708         this.draw();
27709         
27710         this.fireEvent('rotate', this, 'left');
27711         
27712     },
27713     
27714     onRotateRight : function(e)
27715     {
27716         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27717             
27718             var minScale = this.thumbEl.getWidth() / this.minWidth;
27719         
27720             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27721             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27722             
27723             this.startScale = this.scale;
27724             
27725             while (this.getScaleLevel() < minScale){
27726             
27727                 this.scale = this.scale + 1;
27728                 
27729                 if(!this.zoomable()){
27730                     break;
27731                 }
27732                 
27733                 if(
27734                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27735                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27736                 ){
27737                     continue;
27738                 }
27739                 
27740                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27741
27742                 this.draw();
27743                 
27744                 return;
27745             }
27746             
27747             this.scale = this.startScale;
27748             
27749             this.onRotateFail();
27750             
27751             return false;
27752         }
27753         
27754         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27755
27756         if(this.isDocument){
27757             this.setThumbBoxSize();
27758             this.setThumbBoxPosition();
27759             this.setCanvasPosition();
27760         }
27761         
27762         this.draw();
27763         
27764         this.fireEvent('rotate', this, 'right');
27765     },
27766     
27767     onRotateFail : function()
27768     {
27769         this.errorEl.show(true);
27770         
27771         var _this = this;
27772         
27773         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27774     },
27775     
27776     draw : function()
27777     {
27778         this.previewEl.dom.innerHTML = '';
27779         
27780         var canvasEl = document.createElement("canvas");
27781         
27782         var contextEl = canvasEl.getContext("2d");
27783         
27784         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27785         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27786         var center = this.imageEl.OriginWidth / 2;
27787         
27788         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27789             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27790             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27791             center = this.imageEl.OriginHeight / 2;
27792         }
27793         
27794         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27795         
27796         contextEl.translate(center, center);
27797         contextEl.rotate(this.rotate * Math.PI / 180);
27798
27799         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27800         
27801         this.canvasEl = document.createElement("canvas");
27802         
27803         this.contextEl = this.canvasEl.getContext("2d");
27804         
27805         switch (this.rotate) {
27806             case 0 :
27807                 
27808                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27809                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27810                 
27811                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27812                 
27813                 break;
27814             case 90 : 
27815                 
27816                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27817                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27818                 
27819                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27820                     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);
27821                     break;
27822                 }
27823                 
27824                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27825                 
27826                 break;
27827             case 180 :
27828                 
27829                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27830                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27831                 
27832                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27833                     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);
27834                     break;
27835                 }
27836                 
27837                 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);
27838                 
27839                 break;
27840             case 270 :
27841                 
27842                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27843                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27844         
27845                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27846                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27847                     break;
27848                 }
27849                 
27850                 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);
27851                 
27852                 break;
27853             default : 
27854                 break;
27855         }
27856         
27857         this.previewEl.appendChild(this.canvasEl);
27858         
27859         this.setCanvasPosition();
27860     },
27861     
27862     crop : function()
27863     {
27864         if(!this.canvasLoaded){
27865             return;
27866         }
27867         
27868         var imageCanvas = document.createElement("canvas");
27869         
27870         var imageContext = imageCanvas.getContext("2d");
27871         
27872         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27873         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27874         
27875         var center = imageCanvas.width / 2;
27876         
27877         imageContext.translate(center, center);
27878         
27879         imageContext.rotate(this.rotate * Math.PI / 180);
27880         
27881         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27882         
27883         var canvas = document.createElement("canvas");
27884         
27885         var context = canvas.getContext("2d");
27886                 
27887         canvas.width = this.minWidth;
27888         canvas.height = this.minHeight;
27889
27890         switch (this.rotate) {
27891             case 0 :
27892                 
27893                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27894                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27895                 
27896                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27897                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27898                 
27899                 var targetWidth = this.minWidth - 2 * x;
27900                 var targetHeight = this.minHeight - 2 * y;
27901                 
27902                 var scale = 1;
27903                 
27904                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27905                     scale = targetWidth / width;
27906                 }
27907                 
27908                 if(x > 0 && y == 0){
27909                     scale = targetHeight / height;
27910                 }
27911                 
27912                 if(x > 0 && y > 0){
27913                     scale = targetWidth / width;
27914                     
27915                     if(width < height){
27916                         scale = targetHeight / height;
27917                     }
27918                 }
27919                 
27920                 context.scale(scale, scale);
27921                 
27922                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27923                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27924
27925                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27926                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27927
27928                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27929                 
27930                 break;
27931             case 90 : 
27932                 
27933                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27934                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27935                 
27936                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27937                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27938                 
27939                 var targetWidth = this.minWidth - 2 * x;
27940                 var targetHeight = this.minHeight - 2 * y;
27941                 
27942                 var scale = 1;
27943                 
27944                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27945                     scale = targetWidth / width;
27946                 }
27947                 
27948                 if(x > 0 && y == 0){
27949                     scale = targetHeight / height;
27950                 }
27951                 
27952                 if(x > 0 && y > 0){
27953                     scale = targetWidth / width;
27954                     
27955                     if(width < height){
27956                         scale = targetHeight / height;
27957                     }
27958                 }
27959                 
27960                 context.scale(scale, scale);
27961                 
27962                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27963                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27964
27965                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27966                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27967                 
27968                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27969                 
27970                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27971                 
27972                 break;
27973             case 180 :
27974                 
27975                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27976                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27977                 
27978                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27979                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27980                 
27981                 var targetWidth = this.minWidth - 2 * x;
27982                 var targetHeight = this.minHeight - 2 * y;
27983                 
27984                 var scale = 1;
27985                 
27986                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27987                     scale = targetWidth / width;
27988                 }
27989                 
27990                 if(x > 0 && y == 0){
27991                     scale = targetHeight / height;
27992                 }
27993                 
27994                 if(x > 0 && y > 0){
27995                     scale = targetWidth / width;
27996                     
27997                     if(width < height){
27998                         scale = targetHeight / height;
27999                     }
28000                 }
28001                 
28002                 context.scale(scale, scale);
28003                 
28004                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28005                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28006
28007                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28008                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28009
28010                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28011                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28012                 
28013                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28014                 
28015                 break;
28016             case 270 :
28017                 
28018                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28019                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28020                 
28021                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28022                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28023                 
28024                 var targetWidth = this.minWidth - 2 * x;
28025                 var targetHeight = this.minHeight - 2 * y;
28026                 
28027                 var scale = 1;
28028                 
28029                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28030                     scale = targetWidth / width;
28031                 }
28032                 
28033                 if(x > 0 && y == 0){
28034                     scale = targetHeight / height;
28035                 }
28036                 
28037                 if(x > 0 && y > 0){
28038                     scale = targetWidth / width;
28039                     
28040                     if(width < height){
28041                         scale = targetHeight / height;
28042                     }
28043                 }
28044                 
28045                 context.scale(scale, scale);
28046                 
28047                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28048                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28049
28050                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28051                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28052                 
28053                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28054                 
28055                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28056                 
28057                 break;
28058             default : 
28059                 break;
28060         }
28061         
28062         this.cropData = canvas.toDataURL(this.cropType);
28063         
28064         if(this.fireEvent('crop', this, this.cropData) !== false){
28065             this.process(this.file, this.cropData);
28066         }
28067         
28068         return;
28069         
28070     },
28071     
28072     setThumbBoxSize : function()
28073     {
28074         var width, height;
28075         
28076         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28077             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28078             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28079             
28080             this.minWidth = width;
28081             this.minHeight = height;
28082             
28083             if(this.rotate == 90 || this.rotate == 270){
28084                 this.minWidth = height;
28085                 this.minHeight = width;
28086             }
28087         }
28088         
28089         height = 300;
28090         width = Math.ceil(this.minWidth * height / this.minHeight);
28091         
28092         if(this.minWidth > this.minHeight){
28093             width = 300;
28094             height = Math.ceil(this.minHeight * width / this.minWidth);
28095         }
28096         
28097         this.thumbEl.setStyle({
28098             width : width + 'px',
28099             height : height + 'px'
28100         });
28101
28102         return;
28103             
28104     },
28105     
28106     setThumbBoxPosition : function()
28107     {
28108         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28109         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28110         
28111         this.thumbEl.setLeft(x);
28112         this.thumbEl.setTop(y);
28113         
28114     },
28115     
28116     baseRotateLevel : function()
28117     {
28118         this.baseRotate = 1;
28119         
28120         if(
28121                 typeof(this.exif) != 'undefined' &&
28122                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28123                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28124         ){
28125             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28126         }
28127         
28128         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28129         
28130     },
28131     
28132     baseScaleLevel : function()
28133     {
28134         var width, height;
28135         
28136         if(this.isDocument){
28137             
28138             if(this.baseRotate == 6 || this.baseRotate == 8){
28139             
28140                 height = this.thumbEl.getHeight();
28141                 this.baseScale = height / this.imageEl.OriginWidth;
28142
28143                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28144                     width = this.thumbEl.getWidth();
28145                     this.baseScale = width / this.imageEl.OriginHeight;
28146                 }
28147
28148                 return;
28149             }
28150
28151             height = this.thumbEl.getHeight();
28152             this.baseScale = height / this.imageEl.OriginHeight;
28153
28154             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28155                 width = this.thumbEl.getWidth();
28156                 this.baseScale = width / this.imageEl.OriginWidth;
28157             }
28158
28159             return;
28160         }
28161         
28162         if(this.baseRotate == 6 || this.baseRotate == 8){
28163             
28164             width = this.thumbEl.getHeight();
28165             this.baseScale = width / this.imageEl.OriginHeight;
28166             
28167             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28168                 height = this.thumbEl.getWidth();
28169                 this.baseScale = height / this.imageEl.OriginHeight;
28170             }
28171             
28172             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28173                 height = this.thumbEl.getWidth();
28174                 this.baseScale = height / this.imageEl.OriginHeight;
28175                 
28176                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28177                     width = this.thumbEl.getHeight();
28178                     this.baseScale = width / this.imageEl.OriginWidth;
28179                 }
28180             }
28181             
28182             return;
28183         }
28184         
28185         width = this.thumbEl.getWidth();
28186         this.baseScale = width / this.imageEl.OriginWidth;
28187         
28188         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28189             height = this.thumbEl.getHeight();
28190             this.baseScale = height / this.imageEl.OriginHeight;
28191         }
28192         
28193         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28194             
28195             height = this.thumbEl.getHeight();
28196             this.baseScale = height / this.imageEl.OriginHeight;
28197             
28198             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28199                 width = this.thumbEl.getWidth();
28200                 this.baseScale = width / this.imageEl.OriginWidth;
28201             }
28202             
28203         }
28204         
28205         return;
28206     },
28207     
28208     getScaleLevel : function()
28209     {
28210         return this.baseScale * Math.pow(1.1, this.scale);
28211     },
28212     
28213     onTouchStart : function(e)
28214     {
28215         if(!this.canvasLoaded){
28216             this.beforeSelectFile(e);
28217             return;
28218         }
28219         
28220         var touches = e.browserEvent.touches;
28221         
28222         if(!touches){
28223             return;
28224         }
28225         
28226         if(touches.length == 1){
28227             this.onMouseDown(e);
28228             return;
28229         }
28230         
28231         if(touches.length != 2){
28232             return;
28233         }
28234         
28235         var coords = [];
28236         
28237         for(var i = 0, finger; finger = touches[i]; i++){
28238             coords.push(finger.pageX, finger.pageY);
28239         }
28240         
28241         var x = Math.pow(coords[0] - coords[2], 2);
28242         var y = Math.pow(coords[1] - coords[3], 2);
28243         
28244         this.startDistance = Math.sqrt(x + y);
28245         
28246         this.startScale = this.scale;
28247         
28248         this.pinching = true;
28249         this.dragable = false;
28250         
28251     },
28252     
28253     onTouchMove : function(e)
28254     {
28255         if(!this.pinching && !this.dragable){
28256             return;
28257         }
28258         
28259         var touches = e.browserEvent.touches;
28260         
28261         if(!touches){
28262             return;
28263         }
28264         
28265         if(this.dragable){
28266             this.onMouseMove(e);
28267             return;
28268         }
28269         
28270         var coords = [];
28271         
28272         for(var i = 0, finger; finger = touches[i]; i++){
28273             coords.push(finger.pageX, finger.pageY);
28274         }
28275         
28276         var x = Math.pow(coords[0] - coords[2], 2);
28277         var y = Math.pow(coords[1] - coords[3], 2);
28278         
28279         this.endDistance = Math.sqrt(x + y);
28280         
28281         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28282         
28283         if(!this.zoomable()){
28284             this.scale = this.startScale;
28285             return;
28286         }
28287         
28288         this.draw();
28289         
28290     },
28291     
28292     onTouchEnd : function(e)
28293     {
28294         this.pinching = false;
28295         this.dragable = false;
28296         
28297     },
28298     
28299     process : function(file, crop)
28300     {
28301         if(this.loadMask){
28302             this.maskEl.mask(this.loadingText);
28303         }
28304         
28305         this.xhr = new XMLHttpRequest();
28306         
28307         file.xhr = this.xhr;
28308
28309         this.xhr.open(this.method, this.url, true);
28310         
28311         var headers = {
28312             "Accept": "application/json",
28313             "Cache-Control": "no-cache",
28314             "X-Requested-With": "XMLHttpRequest"
28315         };
28316         
28317         for (var headerName in headers) {
28318             var headerValue = headers[headerName];
28319             if (headerValue) {
28320                 this.xhr.setRequestHeader(headerName, headerValue);
28321             }
28322         }
28323         
28324         var _this = this;
28325         
28326         this.xhr.onload = function()
28327         {
28328             _this.xhrOnLoad(_this.xhr);
28329         }
28330         
28331         this.xhr.onerror = function()
28332         {
28333             _this.xhrOnError(_this.xhr);
28334         }
28335         
28336         var formData = new FormData();
28337
28338         formData.append('returnHTML', 'NO');
28339         
28340         if(crop){
28341             formData.append('crop', crop);
28342         }
28343         
28344         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28345             formData.append(this.paramName, file, file.name);
28346         }
28347         
28348         if(typeof(file.filename) != 'undefined'){
28349             formData.append('filename', file.filename);
28350         }
28351         
28352         if(typeof(file.mimetype) != 'undefined'){
28353             formData.append('mimetype', file.mimetype);
28354         }
28355         
28356         if(this.fireEvent('arrange', this, formData) != false){
28357             this.xhr.send(formData);
28358         };
28359     },
28360     
28361     xhrOnLoad : function(xhr)
28362     {
28363         if(this.loadMask){
28364             this.maskEl.unmask();
28365         }
28366         
28367         if (xhr.readyState !== 4) {
28368             this.fireEvent('exception', this, xhr);
28369             return;
28370         }
28371
28372         var response = Roo.decode(xhr.responseText);
28373         
28374         if(!response.success){
28375             this.fireEvent('exception', this, xhr);
28376             return;
28377         }
28378         
28379         var response = Roo.decode(xhr.responseText);
28380         
28381         this.fireEvent('upload', this, response);
28382         
28383     },
28384     
28385     xhrOnError : function()
28386     {
28387         if(this.loadMask){
28388             this.maskEl.unmask();
28389         }
28390         
28391         Roo.log('xhr on error');
28392         
28393         var response = Roo.decode(xhr.responseText);
28394           
28395         Roo.log(response);
28396         
28397     },
28398     
28399     prepare : function(file)
28400     {   
28401         if(this.loadMask){
28402             this.maskEl.mask(this.loadingText);
28403         }
28404         
28405         this.file = false;
28406         this.exif = {};
28407         
28408         if(typeof(file) === 'string'){
28409             this.loadCanvas(file);
28410             return;
28411         }
28412         
28413         if(!file || !this.urlAPI){
28414             return;
28415         }
28416         
28417         this.file = file;
28418         this.cropType = file.type;
28419         
28420         var _this = this;
28421         
28422         if(this.fireEvent('prepare', this, this.file) != false){
28423             
28424             var reader = new FileReader();
28425             
28426             reader.onload = function (e) {
28427                 if (e.target.error) {
28428                     Roo.log(e.target.error);
28429                     return;
28430                 }
28431                 
28432                 var buffer = e.target.result,
28433                     dataView = new DataView(buffer),
28434                     offset = 2,
28435                     maxOffset = dataView.byteLength - 4,
28436                     markerBytes,
28437                     markerLength;
28438                 
28439                 if (dataView.getUint16(0) === 0xffd8) {
28440                     while (offset < maxOffset) {
28441                         markerBytes = dataView.getUint16(offset);
28442                         
28443                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28444                             markerLength = dataView.getUint16(offset + 2) + 2;
28445                             if (offset + markerLength > dataView.byteLength) {
28446                                 Roo.log('Invalid meta data: Invalid segment size.');
28447                                 break;
28448                             }
28449                             
28450                             if(markerBytes == 0xffe1){
28451                                 _this.parseExifData(
28452                                     dataView,
28453                                     offset,
28454                                     markerLength
28455                                 );
28456                             }
28457                             
28458                             offset += markerLength;
28459                             
28460                             continue;
28461                         }
28462                         
28463                         break;
28464                     }
28465                     
28466                 }
28467                 
28468                 var url = _this.urlAPI.createObjectURL(_this.file);
28469                 
28470                 _this.loadCanvas(url);
28471                 
28472                 return;
28473             }
28474             
28475             reader.readAsArrayBuffer(this.file);
28476             
28477         }
28478         
28479     },
28480     
28481     parseExifData : function(dataView, offset, length)
28482     {
28483         var tiffOffset = offset + 10,
28484             littleEndian,
28485             dirOffset;
28486     
28487         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28488             // No Exif data, might be XMP data instead
28489             return;
28490         }
28491         
28492         // Check for the ASCII code for "Exif" (0x45786966):
28493         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28494             // No Exif data, might be XMP data instead
28495             return;
28496         }
28497         if (tiffOffset + 8 > dataView.byteLength) {
28498             Roo.log('Invalid Exif data: Invalid segment size.');
28499             return;
28500         }
28501         // Check for the two null bytes:
28502         if (dataView.getUint16(offset + 8) !== 0x0000) {
28503             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28504             return;
28505         }
28506         // Check the byte alignment:
28507         switch (dataView.getUint16(tiffOffset)) {
28508         case 0x4949:
28509             littleEndian = true;
28510             break;
28511         case 0x4D4D:
28512             littleEndian = false;
28513             break;
28514         default:
28515             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28516             return;
28517         }
28518         // Check for the TIFF tag marker (0x002A):
28519         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28520             Roo.log('Invalid Exif data: Missing TIFF marker.');
28521             return;
28522         }
28523         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28524         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28525         
28526         this.parseExifTags(
28527             dataView,
28528             tiffOffset,
28529             tiffOffset + dirOffset,
28530             littleEndian
28531         );
28532     },
28533     
28534     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28535     {
28536         var tagsNumber,
28537             dirEndOffset,
28538             i;
28539         if (dirOffset + 6 > dataView.byteLength) {
28540             Roo.log('Invalid Exif data: Invalid directory offset.');
28541             return;
28542         }
28543         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28544         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28545         if (dirEndOffset + 4 > dataView.byteLength) {
28546             Roo.log('Invalid Exif data: Invalid directory size.');
28547             return;
28548         }
28549         for (i = 0; i < tagsNumber; i += 1) {
28550             this.parseExifTag(
28551                 dataView,
28552                 tiffOffset,
28553                 dirOffset + 2 + 12 * i, // tag offset
28554                 littleEndian
28555             );
28556         }
28557         // Return the offset to the next directory:
28558         return dataView.getUint32(dirEndOffset, littleEndian);
28559     },
28560     
28561     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28562     {
28563         var tag = dataView.getUint16(offset, littleEndian);
28564         
28565         this.exif[tag] = this.getExifValue(
28566             dataView,
28567             tiffOffset,
28568             offset,
28569             dataView.getUint16(offset + 2, littleEndian), // tag type
28570             dataView.getUint32(offset + 4, littleEndian), // tag length
28571             littleEndian
28572         );
28573     },
28574     
28575     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28576     {
28577         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28578             tagSize,
28579             dataOffset,
28580             values,
28581             i,
28582             str,
28583             c;
28584     
28585         if (!tagType) {
28586             Roo.log('Invalid Exif data: Invalid tag type.');
28587             return;
28588         }
28589         
28590         tagSize = tagType.size * length;
28591         // Determine if the value is contained in the dataOffset bytes,
28592         // or if the value at the dataOffset is a pointer to the actual data:
28593         dataOffset = tagSize > 4 ?
28594                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28595         if (dataOffset + tagSize > dataView.byteLength) {
28596             Roo.log('Invalid Exif data: Invalid data offset.');
28597             return;
28598         }
28599         if (length === 1) {
28600             return tagType.getValue(dataView, dataOffset, littleEndian);
28601         }
28602         values = [];
28603         for (i = 0; i < length; i += 1) {
28604             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28605         }
28606         
28607         if (tagType.ascii) {
28608             str = '';
28609             // Concatenate the chars:
28610             for (i = 0; i < values.length; i += 1) {
28611                 c = values[i];
28612                 // Ignore the terminating NULL byte(s):
28613                 if (c === '\u0000') {
28614                     break;
28615                 }
28616                 str += c;
28617             }
28618             return str;
28619         }
28620         return values;
28621     }
28622     
28623 });
28624
28625 Roo.apply(Roo.bootstrap.UploadCropbox, {
28626     tags : {
28627         'Orientation': 0x0112
28628     },
28629     
28630     Orientation: {
28631             1: 0, //'top-left',
28632 //            2: 'top-right',
28633             3: 180, //'bottom-right',
28634 //            4: 'bottom-left',
28635 //            5: 'left-top',
28636             6: 90, //'right-top',
28637 //            7: 'right-bottom',
28638             8: 270 //'left-bottom'
28639     },
28640     
28641     exifTagTypes : {
28642         // byte, 8-bit unsigned int:
28643         1: {
28644             getValue: function (dataView, dataOffset) {
28645                 return dataView.getUint8(dataOffset);
28646             },
28647             size: 1
28648         },
28649         // ascii, 8-bit byte:
28650         2: {
28651             getValue: function (dataView, dataOffset) {
28652                 return String.fromCharCode(dataView.getUint8(dataOffset));
28653             },
28654             size: 1,
28655             ascii: true
28656         },
28657         // short, 16 bit int:
28658         3: {
28659             getValue: function (dataView, dataOffset, littleEndian) {
28660                 return dataView.getUint16(dataOffset, littleEndian);
28661             },
28662             size: 2
28663         },
28664         // long, 32 bit int:
28665         4: {
28666             getValue: function (dataView, dataOffset, littleEndian) {
28667                 return dataView.getUint32(dataOffset, littleEndian);
28668             },
28669             size: 4
28670         },
28671         // rational = two long values, first is numerator, second is denominator:
28672         5: {
28673             getValue: function (dataView, dataOffset, littleEndian) {
28674                 return dataView.getUint32(dataOffset, littleEndian) /
28675                     dataView.getUint32(dataOffset + 4, littleEndian);
28676             },
28677             size: 8
28678         },
28679         // slong, 32 bit signed int:
28680         9: {
28681             getValue: function (dataView, dataOffset, littleEndian) {
28682                 return dataView.getInt32(dataOffset, littleEndian);
28683             },
28684             size: 4
28685         },
28686         // srational, two slongs, first is numerator, second is denominator:
28687         10: {
28688             getValue: function (dataView, dataOffset, littleEndian) {
28689                 return dataView.getInt32(dataOffset, littleEndian) /
28690                     dataView.getInt32(dataOffset + 4, littleEndian);
28691             },
28692             size: 8
28693         }
28694     },
28695     
28696     footer : {
28697         STANDARD : [
28698             {
28699                 tag : 'div',
28700                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28701                 action : 'rotate-left',
28702                 cn : [
28703                     {
28704                         tag : 'button',
28705                         cls : 'btn btn-default',
28706                         html : '<i class="fa fa-undo"></i>'
28707                     }
28708                 ]
28709             },
28710             {
28711                 tag : 'div',
28712                 cls : 'btn-group roo-upload-cropbox-picture',
28713                 action : 'picture',
28714                 cn : [
28715                     {
28716                         tag : 'button',
28717                         cls : 'btn btn-default',
28718                         html : '<i class="fa fa-picture-o"></i>'
28719                     }
28720                 ]
28721             },
28722             {
28723                 tag : 'div',
28724                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28725                 action : 'rotate-right',
28726                 cn : [
28727                     {
28728                         tag : 'button',
28729                         cls : 'btn btn-default',
28730                         html : '<i class="fa fa-repeat"></i>'
28731                     }
28732                 ]
28733             }
28734         ],
28735         DOCUMENT : [
28736             {
28737                 tag : 'div',
28738                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28739                 action : 'rotate-left',
28740                 cn : [
28741                     {
28742                         tag : 'button',
28743                         cls : 'btn btn-default',
28744                         html : '<i class="fa fa-undo"></i>'
28745                     }
28746                 ]
28747             },
28748             {
28749                 tag : 'div',
28750                 cls : 'btn-group roo-upload-cropbox-download',
28751                 action : 'download',
28752                 cn : [
28753                     {
28754                         tag : 'button',
28755                         cls : 'btn btn-default',
28756                         html : '<i class="fa fa-download"></i>'
28757                     }
28758                 ]
28759             },
28760             {
28761                 tag : 'div',
28762                 cls : 'btn-group roo-upload-cropbox-crop',
28763                 action : 'crop',
28764                 cn : [
28765                     {
28766                         tag : 'button',
28767                         cls : 'btn btn-default',
28768                         html : '<i class="fa fa-crop"></i>'
28769                     }
28770                 ]
28771             },
28772             {
28773                 tag : 'div',
28774                 cls : 'btn-group roo-upload-cropbox-trash',
28775                 action : 'trash',
28776                 cn : [
28777                     {
28778                         tag : 'button',
28779                         cls : 'btn btn-default',
28780                         html : '<i class="fa fa-trash"></i>'
28781                     }
28782                 ]
28783             },
28784             {
28785                 tag : 'div',
28786                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28787                 action : 'rotate-right',
28788                 cn : [
28789                     {
28790                         tag : 'button',
28791                         cls : 'btn btn-default',
28792                         html : '<i class="fa fa-repeat"></i>'
28793                     }
28794                 ]
28795             }
28796         ],
28797         ROTATOR : [
28798             {
28799                 tag : 'div',
28800                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28801                 action : 'rotate-left',
28802                 cn : [
28803                     {
28804                         tag : 'button',
28805                         cls : 'btn btn-default',
28806                         html : '<i class="fa fa-undo"></i>'
28807                     }
28808                 ]
28809             },
28810             {
28811                 tag : 'div',
28812                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28813                 action : 'rotate-right',
28814                 cn : [
28815                     {
28816                         tag : 'button',
28817                         cls : 'btn btn-default',
28818                         html : '<i class="fa fa-repeat"></i>'
28819                     }
28820                 ]
28821             }
28822         ]
28823     }
28824 });
28825
28826 /*
28827 * Licence: LGPL
28828 */
28829
28830 /**
28831  * @class Roo.bootstrap.DocumentManager
28832  * @extends Roo.bootstrap.Component
28833  * Bootstrap DocumentManager class
28834  * @cfg {String} paramName default 'imageUpload'
28835  * @cfg {String} toolTipName default 'filename'
28836  * @cfg {String} method default POST
28837  * @cfg {String} url action url
28838  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28839  * @cfg {Boolean} multiple multiple upload default true
28840  * @cfg {Number} thumbSize default 300
28841  * @cfg {String} fieldLabel
28842  * @cfg {Number} labelWidth default 4
28843  * @cfg {String} labelAlign (left|top) default left
28844  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28845 * @cfg {Number} labellg set the width of label (1-12)
28846  * @cfg {Number} labelmd set the width of label (1-12)
28847  * @cfg {Number} labelsm set the width of label (1-12)
28848  * @cfg {Number} labelxs set the width of label (1-12)
28849  * 
28850  * @constructor
28851  * Create a new DocumentManager
28852  * @param {Object} config The config object
28853  */
28854
28855 Roo.bootstrap.DocumentManager = function(config){
28856     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28857     
28858     this.files = [];
28859     this.delegates = [];
28860     
28861     this.addEvents({
28862         /**
28863          * @event initial
28864          * Fire when initial the DocumentManager
28865          * @param {Roo.bootstrap.DocumentManager} this
28866          */
28867         "initial" : true,
28868         /**
28869          * @event inspect
28870          * inspect selected file
28871          * @param {Roo.bootstrap.DocumentManager} this
28872          * @param {File} file
28873          */
28874         "inspect" : true,
28875         /**
28876          * @event exception
28877          * Fire when xhr load exception
28878          * @param {Roo.bootstrap.DocumentManager} this
28879          * @param {XMLHttpRequest} xhr
28880          */
28881         "exception" : true,
28882         /**
28883          * @event afterupload
28884          * Fire when xhr load exception
28885          * @param {Roo.bootstrap.DocumentManager} this
28886          * @param {XMLHttpRequest} xhr
28887          */
28888         "afterupload" : true,
28889         /**
28890          * @event prepare
28891          * prepare the form data
28892          * @param {Roo.bootstrap.DocumentManager} this
28893          * @param {Object} formData
28894          */
28895         "prepare" : true,
28896         /**
28897          * @event remove
28898          * Fire when remove the file
28899          * @param {Roo.bootstrap.DocumentManager} this
28900          * @param {Object} file
28901          */
28902         "remove" : true,
28903         /**
28904          * @event refresh
28905          * Fire after refresh the file
28906          * @param {Roo.bootstrap.DocumentManager} this
28907          */
28908         "refresh" : true,
28909         /**
28910          * @event click
28911          * Fire after click the image
28912          * @param {Roo.bootstrap.DocumentManager} this
28913          * @param {Object} file
28914          */
28915         "click" : true,
28916         /**
28917          * @event edit
28918          * Fire when upload a image and editable set to true
28919          * @param {Roo.bootstrap.DocumentManager} this
28920          * @param {Object} file
28921          */
28922         "edit" : true,
28923         /**
28924          * @event beforeselectfile
28925          * Fire before select file
28926          * @param {Roo.bootstrap.DocumentManager} this
28927          */
28928         "beforeselectfile" : true,
28929         /**
28930          * @event process
28931          * Fire before process file
28932          * @param {Roo.bootstrap.DocumentManager} this
28933          * @param {Object} file
28934          */
28935         "process" : true,
28936         /**
28937          * @event previewrendered
28938          * Fire when preview rendered
28939          * @param {Roo.bootstrap.DocumentManager} this
28940          * @param {Object} file
28941          */
28942         "previewrendered" : true,
28943         /**
28944          */
28945         "previewResize" : true
28946         
28947     });
28948 };
28949
28950 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28951     
28952     boxes : 0,
28953     inputName : '',
28954     thumbSize : 300,
28955     multiple : true,
28956     files : false,
28957     method : 'POST',
28958     url : '',
28959     paramName : 'imageUpload',
28960     toolTipName : 'filename',
28961     fieldLabel : '',
28962     labelWidth : 4,
28963     labelAlign : 'left',
28964     editable : true,
28965     delegates : false,
28966     xhr : false, 
28967     
28968     labellg : 0,
28969     labelmd : 0,
28970     labelsm : 0,
28971     labelxs : 0,
28972     
28973     getAutoCreate : function()
28974     {   
28975         var managerWidget = {
28976             tag : 'div',
28977             cls : 'roo-document-manager',
28978             cn : [
28979                 {
28980                     tag : 'input',
28981                     cls : 'roo-document-manager-selector',
28982                     type : 'file'
28983                 },
28984                 {
28985                     tag : 'div',
28986                     cls : 'roo-document-manager-uploader',
28987                     cn : [
28988                         {
28989                             tag : 'div',
28990                             cls : 'roo-document-manager-upload-btn',
28991                             html : '<i class="fa fa-plus"></i>'
28992                         }
28993                     ]
28994                     
28995                 }
28996             ]
28997         };
28998         
28999         var content = [
29000             {
29001                 tag : 'div',
29002                 cls : 'column col-md-12',
29003                 cn : managerWidget
29004             }
29005         ];
29006         
29007         if(this.fieldLabel.length){
29008             
29009             content = [
29010                 {
29011                     tag : 'div',
29012                     cls : 'column col-md-12',
29013                     html : this.fieldLabel
29014                 },
29015                 {
29016                     tag : 'div',
29017                     cls : 'column col-md-12',
29018                     cn : managerWidget
29019                 }
29020             ];
29021
29022             if(this.labelAlign == 'left'){
29023                 content = [
29024                     {
29025                         tag : 'div',
29026                         cls : 'column',
29027                         html : this.fieldLabel
29028                     },
29029                     {
29030                         tag : 'div',
29031                         cls : 'column',
29032                         cn : managerWidget
29033                     }
29034                 ];
29035                 
29036                 if(this.labelWidth > 12){
29037                     content[0].style = "width: " + this.labelWidth + 'px';
29038                 }
29039
29040                 if(this.labelWidth < 13 && this.labelmd == 0){
29041                     this.labelmd = this.labelWidth;
29042                 }
29043
29044                 if(this.labellg > 0){
29045                     content[0].cls += ' col-lg-' + this.labellg;
29046                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29047                 }
29048
29049                 if(this.labelmd > 0){
29050                     content[0].cls += ' col-md-' + this.labelmd;
29051                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29052                 }
29053
29054                 if(this.labelsm > 0){
29055                     content[0].cls += ' col-sm-' + this.labelsm;
29056                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29057                 }
29058
29059                 if(this.labelxs > 0){
29060                     content[0].cls += ' col-xs-' + this.labelxs;
29061                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29062                 }
29063                 
29064             }
29065         }
29066         
29067         var cfg = {
29068             tag : 'div',
29069             cls : 'row clearfix',
29070             cn : content
29071         };
29072         
29073         return cfg;
29074         
29075     },
29076     
29077     initEvents : function()
29078     {
29079         this.managerEl = this.el.select('.roo-document-manager', true).first();
29080         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29081         
29082         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29083         this.selectorEl.hide();
29084         
29085         if(this.multiple){
29086             this.selectorEl.attr('multiple', 'multiple');
29087         }
29088         
29089         this.selectorEl.on('change', this.onFileSelected, this);
29090         
29091         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29092         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29093         
29094         this.uploader.on('click', this.onUploaderClick, this);
29095         
29096         this.renderProgressDialog();
29097         
29098         var _this = this;
29099         
29100         window.addEventListener("resize", function() { _this.refresh(); } );
29101         
29102         this.fireEvent('initial', this);
29103     },
29104     
29105     renderProgressDialog : function()
29106     {
29107         var _this = this;
29108         
29109         this.progressDialog = new Roo.bootstrap.Modal({
29110             cls : 'roo-document-manager-progress-dialog',
29111             allow_close : false,
29112             title : '',
29113             buttons : [
29114                 {
29115                     name  :'cancel',
29116                     weight : 'danger',
29117                     html : 'Cancel'
29118                 }
29119             ], 
29120             listeners : { 
29121                 btnclick : function() {
29122                     _this.uploadCancel();
29123                     this.hide();
29124                 }
29125             }
29126         });
29127          
29128         this.progressDialog.render(Roo.get(document.body));
29129          
29130         this.progress = new Roo.bootstrap.Progress({
29131             cls : 'roo-document-manager-progress',
29132             active : true,
29133             striped : true
29134         });
29135         
29136         this.progress.render(this.progressDialog.getChildContainer());
29137         
29138         this.progressBar = new Roo.bootstrap.ProgressBar({
29139             cls : 'roo-document-manager-progress-bar',
29140             aria_valuenow : 0,
29141             aria_valuemin : 0,
29142             aria_valuemax : 12,
29143             panel : 'success'
29144         });
29145         
29146         this.progressBar.render(this.progress.getChildContainer());
29147     },
29148     
29149     onUploaderClick : function(e)
29150     {
29151         e.preventDefault();
29152      
29153         if(this.fireEvent('beforeselectfile', this) != false){
29154             this.selectorEl.dom.click();
29155         }
29156         
29157     },
29158     
29159     onFileSelected : function(e)
29160     {
29161         e.preventDefault();
29162         
29163         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29164             return;
29165         }
29166         
29167         Roo.each(this.selectorEl.dom.files, function(file){
29168             if(this.fireEvent('inspect', this, file) != false){
29169                 this.files.push(file);
29170             }
29171         }, this);
29172         
29173         this.queue();
29174         
29175     },
29176     
29177     queue : function()
29178     {
29179         this.selectorEl.dom.value = '';
29180         
29181         if(!this.files || !this.files.length){
29182             return;
29183         }
29184         
29185         if(this.boxes > 0 && this.files.length > this.boxes){
29186             this.files = this.files.slice(0, this.boxes);
29187         }
29188         
29189         this.uploader.show();
29190         
29191         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29192             this.uploader.hide();
29193         }
29194         
29195         var _this = this;
29196         
29197         var files = [];
29198         
29199         var docs = [];
29200         
29201         Roo.each(this.files, function(file){
29202             
29203             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29204                 var f = this.renderPreview(file);
29205                 files.push(f);
29206                 return;
29207             }
29208             
29209             if(file.type.indexOf('image') != -1){
29210                 this.delegates.push(
29211                     (function(){
29212                         _this.process(file);
29213                     }).createDelegate(this)
29214                 );
29215         
29216                 return;
29217             }
29218             
29219             docs.push(
29220                 (function(){
29221                     _this.process(file);
29222                 }).createDelegate(this)
29223             );
29224             
29225         }, this);
29226         
29227         this.files = files;
29228         
29229         this.delegates = this.delegates.concat(docs);
29230         
29231         if(!this.delegates.length){
29232             this.refresh();
29233             return;
29234         }
29235         
29236         this.progressBar.aria_valuemax = this.delegates.length;
29237         
29238         this.arrange();
29239         
29240         return;
29241     },
29242     
29243     arrange : function()
29244     {
29245         if(!this.delegates.length){
29246             this.progressDialog.hide();
29247             this.refresh();
29248             return;
29249         }
29250         
29251         var delegate = this.delegates.shift();
29252         
29253         this.progressDialog.show();
29254         
29255         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29256         
29257         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29258         
29259         delegate();
29260     },
29261     
29262     refresh : function()
29263     {
29264         this.uploader.show();
29265         
29266         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29267             this.uploader.hide();
29268         }
29269         
29270         Roo.isTouch ? this.closable(false) : this.closable(true);
29271         
29272         this.fireEvent('refresh', this);
29273     },
29274     
29275     onRemove : function(e, el, o)
29276     {
29277         e.preventDefault();
29278         
29279         this.fireEvent('remove', this, o);
29280         
29281     },
29282     
29283     remove : function(o)
29284     {
29285         var files = [];
29286         
29287         Roo.each(this.files, function(file){
29288             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29289                 files.push(file);
29290                 return;
29291             }
29292
29293             o.target.remove();
29294
29295         }, this);
29296         
29297         this.files = files;
29298         
29299         this.refresh();
29300     },
29301     
29302     clear : function()
29303     {
29304         Roo.each(this.files, function(file){
29305             if(!file.target){
29306                 return;
29307             }
29308             
29309             file.target.remove();
29310
29311         }, this);
29312         
29313         this.files = [];
29314         
29315         this.refresh();
29316     },
29317     
29318     onClick : function(e, el, o)
29319     {
29320         e.preventDefault();
29321         
29322         this.fireEvent('click', this, o);
29323         
29324     },
29325     
29326     closable : function(closable)
29327     {
29328         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29329             
29330             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29331             
29332             if(closable){
29333                 el.show();
29334                 return;
29335             }
29336             
29337             el.hide();
29338             
29339         }, this);
29340     },
29341     
29342     xhrOnLoad : function(xhr)
29343     {
29344         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29345             el.remove();
29346         }, this);
29347         
29348         if (xhr.readyState !== 4) {
29349             this.arrange();
29350             this.fireEvent('exception', this, xhr);
29351             return;
29352         }
29353
29354         var response = Roo.decode(xhr.responseText);
29355         
29356         if(!response.success){
29357             this.arrange();
29358             this.fireEvent('exception', this, xhr);
29359             return;
29360         }
29361         
29362         var file = this.renderPreview(response.data);
29363         
29364         this.files.push(file);
29365         
29366         this.arrange();
29367         
29368         this.fireEvent('afterupload', this, xhr);
29369         
29370     },
29371     
29372     xhrOnError : function(xhr)
29373     {
29374         Roo.log('xhr on error');
29375         
29376         var response = Roo.decode(xhr.responseText);
29377           
29378         Roo.log(response);
29379         
29380         this.arrange();
29381     },
29382     
29383     process : function(file)
29384     {
29385         if(this.fireEvent('process', this, file) !== false){
29386             if(this.editable && file.type.indexOf('image') != -1){
29387                 this.fireEvent('edit', this, file);
29388                 return;
29389             }
29390
29391             this.uploadStart(file, false);
29392
29393             return;
29394         }
29395         
29396     },
29397     
29398     uploadStart : function(file, crop)
29399     {
29400         this.xhr = new XMLHttpRequest();
29401         
29402         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29403             this.arrange();
29404             return;
29405         }
29406         
29407         file.xhr = this.xhr;
29408             
29409         this.managerEl.createChild({
29410             tag : 'div',
29411             cls : 'roo-document-manager-loading',
29412             cn : [
29413                 {
29414                     tag : 'div',
29415                     tooltip : file.name,
29416                     cls : 'roo-document-manager-thumb',
29417                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29418                 }
29419             ]
29420
29421         });
29422
29423         this.xhr.open(this.method, this.url, true);
29424         
29425         var headers = {
29426             "Accept": "application/json",
29427             "Cache-Control": "no-cache",
29428             "X-Requested-With": "XMLHttpRequest"
29429         };
29430         
29431         for (var headerName in headers) {
29432             var headerValue = headers[headerName];
29433             if (headerValue) {
29434                 this.xhr.setRequestHeader(headerName, headerValue);
29435             }
29436         }
29437         
29438         var _this = this;
29439         
29440         this.xhr.onload = function()
29441         {
29442             _this.xhrOnLoad(_this.xhr);
29443         }
29444         
29445         this.xhr.onerror = function()
29446         {
29447             _this.xhrOnError(_this.xhr);
29448         }
29449         
29450         var formData = new FormData();
29451
29452         formData.append('returnHTML', 'NO');
29453         
29454         if(crop){
29455             formData.append('crop', crop);
29456         }
29457         
29458         formData.append(this.paramName, file, file.name);
29459         
29460         var options = {
29461             file : file, 
29462             manually : false
29463         };
29464         
29465         if(this.fireEvent('prepare', this, formData, options) != false){
29466             
29467             if(options.manually){
29468                 return;
29469             }
29470             
29471             this.xhr.send(formData);
29472             return;
29473         };
29474         
29475         this.uploadCancel();
29476     },
29477     
29478     uploadCancel : function()
29479     {
29480         if (this.xhr) {
29481             this.xhr.abort();
29482         }
29483         
29484         this.delegates = [];
29485         
29486         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29487             el.remove();
29488         }, this);
29489         
29490         this.arrange();
29491     },
29492     
29493     renderPreview : function(file)
29494     {
29495         if(typeof(file.target) != 'undefined' && file.target){
29496             return file;
29497         }
29498         
29499         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29500         
29501         var previewEl = this.managerEl.createChild({
29502             tag : 'div',
29503             cls : 'roo-document-manager-preview',
29504             cn : [
29505                 {
29506                     tag : 'div',
29507                     tooltip : file[this.toolTipName],
29508                     cls : 'roo-document-manager-thumb',
29509                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29510                 },
29511                 {
29512                     tag : 'button',
29513                     cls : 'close',
29514                     html : '<i class="fa fa-times-circle"></i>'
29515                 }
29516             ]
29517         });
29518
29519         var close = previewEl.select('button.close', true).first();
29520
29521         close.on('click', this.onRemove, this, file);
29522
29523         file.target = previewEl;
29524
29525         var image = previewEl.select('img', true).first();
29526         
29527         var _this = this;
29528         
29529         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29530         
29531         image.on('click', this.onClick, this, file);
29532         
29533         this.fireEvent('previewrendered', this, file);
29534         
29535         return file;
29536         
29537     },
29538     
29539     onPreviewLoad : function(file, image)
29540     {
29541         if(typeof(file.target) == 'undefined' || !file.target){
29542             return;
29543         }
29544         
29545         var width = image.dom.naturalWidth || image.dom.width;
29546         var height = image.dom.naturalHeight || image.dom.height;
29547         
29548         if(!this.previewResize) {
29549             return;
29550         }
29551         
29552         if(width > height){
29553             file.target.addClass('wide');
29554             return;
29555         }
29556         
29557         file.target.addClass('tall');
29558         return;
29559         
29560     },
29561     
29562     uploadFromSource : function(file, crop)
29563     {
29564         this.xhr = new XMLHttpRequest();
29565         
29566         this.managerEl.createChild({
29567             tag : 'div',
29568             cls : 'roo-document-manager-loading',
29569             cn : [
29570                 {
29571                     tag : 'div',
29572                     tooltip : file.name,
29573                     cls : 'roo-document-manager-thumb',
29574                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29575                 }
29576             ]
29577
29578         });
29579
29580         this.xhr.open(this.method, this.url, true);
29581         
29582         var headers = {
29583             "Accept": "application/json",
29584             "Cache-Control": "no-cache",
29585             "X-Requested-With": "XMLHttpRequest"
29586         };
29587         
29588         for (var headerName in headers) {
29589             var headerValue = headers[headerName];
29590             if (headerValue) {
29591                 this.xhr.setRequestHeader(headerName, headerValue);
29592             }
29593         }
29594         
29595         var _this = this;
29596         
29597         this.xhr.onload = function()
29598         {
29599             _this.xhrOnLoad(_this.xhr);
29600         }
29601         
29602         this.xhr.onerror = function()
29603         {
29604             _this.xhrOnError(_this.xhr);
29605         }
29606         
29607         var formData = new FormData();
29608
29609         formData.append('returnHTML', 'NO');
29610         
29611         formData.append('crop', crop);
29612         
29613         if(typeof(file.filename) != 'undefined'){
29614             formData.append('filename', file.filename);
29615         }
29616         
29617         if(typeof(file.mimetype) != 'undefined'){
29618             formData.append('mimetype', file.mimetype);
29619         }
29620         
29621         Roo.log(formData);
29622         
29623         if(this.fireEvent('prepare', this, formData) != false){
29624             this.xhr.send(formData);
29625         };
29626     }
29627 });
29628
29629 /*
29630 * Licence: LGPL
29631 */
29632
29633 /**
29634  * @class Roo.bootstrap.DocumentViewer
29635  * @extends Roo.bootstrap.Component
29636  * Bootstrap DocumentViewer class
29637  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29638  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29639  * 
29640  * @constructor
29641  * Create a new DocumentViewer
29642  * @param {Object} config The config object
29643  */
29644
29645 Roo.bootstrap.DocumentViewer = function(config){
29646     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29647     
29648     this.addEvents({
29649         /**
29650          * @event initial
29651          * Fire after initEvent
29652          * @param {Roo.bootstrap.DocumentViewer} this
29653          */
29654         "initial" : true,
29655         /**
29656          * @event click
29657          * Fire after click
29658          * @param {Roo.bootstrap.DocumentViewer} this
29659          */
29660         "click" : true,
29661         /**
29662          * @event download
29663          * Fire after download button
29664          * @param {Roo.bootstrap.DocumentViewer} this
29665          */
29666         "download" : true,
29667         /**
29668          * @event trash
29669          * Fire after trash button
29670          * @param {Roo.bootstrap.DocumentViewer} this
29671          */
29672         "trash" : true
29673         
29674     });
29675 };
29676
29677 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29678     
29679     showDownload : true,
29680     
29681     showTrash : true,
29682     
29683     getAutoCreate : function()
29684     {
29685         var cfg = {
29686             tag : 'div',
29687             cls : 'roo-document-viewer',
29688             cn : [
29689                 {
29690                     tag : 'div',
29691                     cls : 'roo-document-viewer-body',
29692                     cn : [
29693                         {
29694                             tag : 'div',
29695                             cls : 'roo-document-viewer-thumb',
29696                             cn : [
29697                                 {
29698                                     tag : 'img',
29699                                     cls : 'roo-document-viewer-image'
29700                                 }
29701                             ]
29702                         }
29703                     ]
29704                 },
29705                 {
29706                     tag : 'div',
29707                     cls : 'roo-document-viewer-footer',
29708                     cn : {
29709                         tag : 'div',
29710                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29711                         cn : [
29712                             {
29713                                 tag : 'div',
29714                                 cls : 'btn-group roo-document-viewer-download',
29715                                 cn : [
29716                                     {
29717                                         tag : 'button',
29718                                         cls : 'btn btn-default',
29719                                         html : '<i class="fa fa-download"></i>'
29720                                     }
29721                                 ]
29722                             },
29723                             {
29724                                 tag : 'div',
29725                                 cls : 'btn-group roo-document-viewer-trash',
29726                                 cn : [
29727                                     {
29728                                         tag : 'button',
29729                                         cls : 'btn btn-default',
29730                                         html : '<i class="fa fa-trash"></i>'
29731                                     }
29732                                 ]
29733                             }
29734                         ]
29735                     }
29736                 }
29737             ]
29738         };
29739         
29740         return cfg;
29741     },
29742     
29743     initEvents : function()
29744     {
29745         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29746         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29747         
29748         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29749         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29750         
29751         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29752         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29753         
29754         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29755         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29756         
29757         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29758         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29759         
29760         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29761         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29762         
29763         this.bodyEl.on('click', this.onClick, this);
29764         this.downloadBtn.on('click', this.onDownload, this);
29765         this.trashBtn.on('click', this.onTrash, this);
29766         
29767         this.downloadBtn.hide();
29768         this.trashBtn.hide();
29769         
29770         if(this.showDownload){
29771             this.downloadBtn.show();
29772         }
29773         
29774         if(this.showTrash){
29775             this.trashBtn.show();
29776         }
29777         
29778         if(!this.showDownload && !this.showTrash) {
29779             this.footerEl.hide();
29780         }
29781         
29782     },
29783     
29784     initial : function()
29785     {
29786         this.fireEvent('initial', this);
29787         
29788     },
29789     
29790     onClick : function(e)
29791     {
29792         e.preventDefault();
29793         
29794         this.fireEvent('click', this);
29795     },
29796     
29797     onDownload : function(e)
29798     {
29799         e.preventDefault();
29800         
29801         this.fireEvent('download', this);
29802     },
29803     
29804     onTrash : function(e)
29805     {
29806         e.preventDefault();
29807         
29808         this.fireEvent('trash', this);
29809     }
29810     
29811 });
29812 /*
29813  * - LGPL
29814  *
29815  * nav progress bar
29816  * 
29817  */
29818
29819 /**
29820  * @class Roo.bootstrap.NavProgressBar
29821  * @extends Roo.bootstrap.Component
29822  * Bootstrap NavProgressBar class
29823  * 
29824  * @constructor
29825  * Create a new nav progress bar
29826  * @param {Object} config The config object
29827  */
29828
29829 Roo.bootstrap.NavProgressBar = function(config){
29830     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29831
29832     this.bullets = this.bullets || [];
29833    
29834 //    Roo.bootstrap.NavProgressBar.register(this);
29835      this.addEvents({
29836         /**
29837              * @event changed
29838              * Fires when the active item changes
29839              * @param {Roo.bootstrap.NavProgressBar} this
29840              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29841              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29842          */
29843         'changed': true
29844      });
29845     
29846 };
29847
29848 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29849     
29850     bullets : [],
29851     barItems : [],
29852     
29853     getAutoCreate : function()
29854     {
29855         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29856         
29857         cfg = {
29858             tag : 'div',
29859             cls : 'roo-navigation-bar-group',
29860             cn : [
29861                 {
29862                     tag : 'div',
29863                     cls : 'roo-navigation-top-bar'
29864                 },
29865                 {
29866                     tag : 'div',
29867                     cls : 'roo-navigation-bullets-bar',
29868                     cn : [
29869                         {
29870                             tag : 'ul',
29871                             cls : 'roo-navigation-bar'
29872                         }
29873                     ]
29874                 },
29875                 
29876                 {
29877                     tag : 'div',
29878                     cls : 'roo-navigation-bottom-bar'
29879                 }
29880             ]
29881             
29882         };
29883         
29884         return cfg;
29885         
29886     },
29887     
29888     initEvents: function() 
29889     {
29890         
29891     },
29892     
29893     onRender : function(ct, position) 
29894     {
29895         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29896         
29897         if(this.bullets.length){
29898             Roo.each(this.bullets, function(b){
29899                this.addItem(b);
29900             }, this);
29901         }
29902         
29903         this.format();
29904         
29905     },
29906     
29907     addItem : function(cfg)
29908     {
29909         var item = new Roo.bootstrap.NavProgressItem(cfg);
29910         
29911         item.parentId = this.id;
29912         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29913         
29914         if(cfg.html){
29915             var top = new Roo.bootstrap.Element({
29916                 tag : 'div',
29917                 cls : 'roo-navigation-bar-text'
29918             });
29919             
29920             var bottom = new Roo.bootstrap.Element({
29921                 tag : 'div',
29922                 cls : 'roo-navigation-bar-text'
29923             });
29924             
29925             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29926             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29927             
29928             var topText = new Roo.bootstrap.Element({
29929                 tag : 'span',
29930                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29931             });
29932             
29933             var bottomText = new Roo.bootstrap.Element({
29934                 tag : 'span',
29935                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29936             });
29937             
29938             topText.onRender(top.el, null);
29939             bottomText.onRender(bottom.el, null);
29940             
29941             item.topEl = top;
29942             item.bottomEl = bottom;
29943         }
29944         
29945         this.barItems.push(item);
29946         
29947         return item;
29948     },
29949     
29950     getActive : function()
29951     {
29952         var active = false;
29953         
29954         Roo.each(this.barItems, function(v){
29955             
29956             if (!v.isActive()) {
29957                 return;
29958             }
29959             
29960             active = v;
29961             return false;
29962             
29963         });
29964         
29965         return active;
29966     },
29967     
29968     setActiveItem : function(item)
29969     {
29970         var prev = false;
29971         
29972         Roo.each(this.barItems, function(v){
29973             if (v.rid == item.rid) {
29974                 return ;
29975             }
29976             
29977             if (v.isActive()) {
29978                 v.setActive(false);
29979                 prev = v;
29980             }
29981         });
29982
29983         item.setActive(true);
29984         
29985         this.fireEvent('changed', this, item, prev);
29986     },
29987     
29988     getBarItem: function(rid)
29989     {
29990         var ret = false;
29991         
29992         Roo.each(this.barItems, function(e) {
29993             if (e.rid != rid) {
29994                 return;
29995             }
29996             
29997             ret =  e;
29998             return false;
29999         });
30000         
30001         return ret;
30002     },
30003     
30004     indexOfItem : function(item)
30005     {
30006         var index = false;
30007         
30008         Roo.each(this.barItems, function(v, i){
30009             
30010             if (v.rid != item.rid) {
30011                 return;
30012             }
30013             
30014             index = i;
30015             return false
30016         });
30017         
30018         return index;
30019     },
30020     
30021     setActiveNext : function()
30022     {
30023         var i = this.indexOfItem(this.getActive());
30024         
30025         if (i > this.barItems.length) {
30026             return;
30027         }
30028         
30029         this.setActiveItem(this.barItems[i+1]);
30030     },
30031     
30032     setActivePrev : function()
30033     {
30034         var i = this.indexOfItem(this.getActive());
30035         
30036         if (i  < 1) {
30037             return;
30038         }
30039         
30040         this.setActiveItem(this.barItems[i-1]);
30041     },
30042     
30043     format : function()
30044     {
30045         if(!this.barItems.length){
30046             return;
30047         }
30048      
30049         var width = 100 / this.barItems.length;
30050         
30051         Roo.each(this.barItems, function(i){
30052             i.el.setStyle('width', width + '%');
30053             i.topEl.el.setStyle('width', width + '%');
30054             i.bottomEl.el.setStyle('width', width + '%');
30055         }, this);
30056         
30057     }
30058     
30059 });
30060 /*
30061  * - LGPL
30062  *
30063  * Nav Progress Item
30064  * 
30065  */
30066
30067 /**
30068  * @class Roo.bootstrap.NavProgressItem
30069  * @extends Roo.bootstrap.Component
30070  * Bootstrap NavProgressItem class
30071  * @cfg {String} rid the reference id
30072  * @cfg {Boolean} active (true|false) Is item active default false
30073  * @cfg {Boolean} disabled (true|false) Is item active default false
30074  * @cfg {String} html
30075  * @cfg {String} position (top|bottom) text position default bottom
30076  * @cfg {String} icon show icon instead of number
30077  * 
30078  * @constructor
30079  * Create a new NavProgressItem
30080  * @param {Object} config The config object
30081  */
30082 Roo.bootstrap.NavProgressItem = function(config){
30083     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30084     this.addEvents({
30085         // raw events
30086         /**
30087          * @event click
30088          * The raw click event for the entire grid.
30089          * @param {Roo.bootstrap.NavProgressItem} this
30090          * @param {Roo.EventObject} e
30091          */
30092         "click" : true
30093     });
30094    
30095 };
30096
30097 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30098     
30099     rid : '',
30100     active : false,
30101     disabled : false,
30102     html : '',
30103     position : 'bottom',
30104     icon : false,
30105     
30106     getAutoCreate : function()
30107     {
30108         var iconCls = 'roo-navigation-bar-item-icon';
30109         
30110         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30111         
30112         var cfg = {
30113             tag: 'li',
30114             cls: 'roo-navigation-bar-item',
30115             cn : [
30116                 {
30117                     tag : 'i',
30118                     cls : iconCls
30119                 }
30120             ]
30121         };
30122         
30123         if(this.active){
30124             cfg.cls += ' active';
30125         }
30126         if(this.disabled){
30127             cfg.cls += ' disabled';
30128         }
30129         
30130         return cfg;
30131     },
30132     
30133     disable : function()
30134     {
30135         this.setDisabled(true);
30136     },
30137     
30138     enable : function()
30139     {
30140         this.setDisabled(false);
30141     },
30142     
30143     initEvents: function() 
30144     {
30145         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30146         
30147         this.iconEl.on('click', this.onClick, this);
30148     },
30149     
30150     onClick : function(e)
30151     {
30152         e.preventDefault();
30153         
30154         if(this.disabled){
30155             return;
30156         }
30157         
30158         if(this.fireEvent('click', this, e) === false){
30159             return;
30160         };
30161         
30162         this.parent().setActiveItem(this);
30163     },
30164     
30165     isActive: function () 
30166     {
30167         return this.active;
30168     },
30169     
30170     setActive : function(state)
30171     {
30172         if(this.active == state){
30173             return;
30174         }
30175         
30176         this.active = state;
30177         
30178         if (state) {
30179             this.el.addClass('active');
30180             return;
30181         }
30182         
30183         this.el.removeClass('active');
30184         
30185         return;
30186     },
30187     
30188     setDisabled : function(state)
30189     {
30190         if(this.disabled == state){
30191             return;
30192         }
30193         
30194         this.disabled = state;
30195         
30196         if (state) {
30197             this.el.addClass('disabled');
30198             return;
30199         }
30200         
30201         this.el.removeClass('disabled');
30202     },
30203     
30204     tooltipEl : function()
30205     {
30206         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30207     }
30208 });
30209  
30210
30211  /*
30212  * - LGPL
30213  *
30214  * FieldLabel
30215  * 
30216  */
30217
30218 /**
30219  * @class Roo.bootstrap.FieldLabel
30220  * @extends Roo.bootstrap.Component
30221  * Bootstrap FieldLabel class
30222  * @cfg {String} html contents of the element
30223  * @cfg {String} tag tag of the element default label
30224  * @cfg {String} cls class of the element
30225  * @cfg {String} target label target 
30226  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30227  * @cfg {String} invalidClass default "text-warning"
30228  * @cfg {String} validClass default "text-success"
30229  * @cfg {String} iconTooltip default "This field is required"
30230  * @cfg {String} indicatorpos (left|right) default left
30231  * 
30232  * @constructor
30233  * Create a new FieldLabel
30234  * @param {Object} config The config object
30235  */
30236
30237 Roo.bootstrap.FieldLabel = function(config){
30238     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30239     
30240     this.addEvents({
30241             /**
30242              * @event invalid
30243              * Fires after the field has been marked as invalid.
30244              * @param {Roo.form.FieldLabel} this
30245              * @param {String} msg The validation message
30246              */
30247             invalid : true,
30248             /**
30249              * @event valid
30250              * Fires after the field has been validated with no errors.
30251              * @param {Roo.form.FieldLabel} this
30252              */
30253             valid : true
30254         });
30255 };
30256
30257 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30258     
30259     tag: 'label',
30260     cls: '',
30261     html: '',
30262     target: '',
30263     allowBlank : true,
30264     invalidClass : 'has-warning',
30265     validClass : 'has-success',
30266     iconTooltip : 'This field is required',
30267     indicatorpos : 'left',
30268     
30269     getAutoCreate : function(){
30270         
30271         var cls = "";
30272         if (!this.allowBlank) {
30273             cls  = "visible";
30274         }
30275         
30276         var cfg = {
30277             tag : this.tag,
30278             cls : 'roo-bootstrap-field-label ' + this.cls,
30279             for : this.target,
30280             cn : [
30281                 {
30282                     tag : 'i',
30283                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30284                     tooltip : this.iconTooltip
30285                 },
30286                 {
30287                     tag : 'span',
30288                     html : this.html
30289                 }
30290             ] 
30291         };
30292         
30293         if(this.indicatorpos == 'right'){
30294             var cfg = {
30295                 tag : this.tag,
30296                 cls : 'roo-bootstrap-field-label ' + this.cls,
30297                 for : this.target,
30298                 cn : [
30299                     {
30300                         tag : 'span',
30301                         html : this.html
30302                     },
30303                     {
30304                         tag : 'i',
30305                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30306                         tooltip : this.iconTooltip
30307                     }
30308                 ] 
30309             };
30310         }
30311         
30312         return cfg;
30313     },
30314     
30315     initEvents: function() 
30316     {
30317         Roo.bootstrap.Element.superclass.initEvents.call(this);
30318         
30319         this.indicator = this.indicatorEl();
30320         
30321         if(this.indicator){
30322             this.indicator.removeClass('visible');
30323             this.indicator.addClass('invisible');
30324         }
30325         
30326         Roo.bootstrap.FieldLabel.register(this);
30327     },
30328     
30329     indicatorEl : function()
30330     {
30331         var indicator = this.el.select('i.roo-required-indicator',true).first();
30332         
30333         if(!indicator){
30334             return false;
30335         }
30336         
30337         return indicator;
30338         
30339     },
30340     
30341     /**
30342      * Mark this field as valid
30343      */
30344     markValid : function()
30345     {
30346         if(this.indicator){
30347             this.indicator.removeClass('visible');
30348             this.indicator.addClass('invisible');
30349         }
30350         
30351         this.el.removeClass(this.invalidClass);
30352         
30353         this.el.addClass(this.validClass);
30354         
30355         this.fireEvent('valid', this);
30356     },
30357     
30358     /**
30359      * Mark this field as invalid
30360      * @param {String} msg The validation message
30361      */
30362     markInvalid : function(msg)
30363     {
30364         if(this.indicator){
30365             this.indicator.removeClass('invisible');
30366             this.indicator.addClass('visible');
30367         }
30368         
30369         this.el.removeClass(this.validClass);
30370         
30371         this.el.addClass(this.invalidClass);
30372         
30373         this.fireEvent('invalid', this, msg);
30374     }
30375     
30376    
30377 });
30378
30379 Roo.apply(Roo.bootstrap.FieldLabel, {
30380     
30381     groups: {},
30382     
30383      /**
30384     * register a FieldLabel Group
30385     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30386     */
30387     register : function(label)
30388     {
30389         if(this.groups.hasOwnProperty(label.target)){
30390             return;
30391         }
30392      
30393         this.groups[label.target] = label;
30394         
30395     },
30396     /**
30397     * fetch a FieldLabel Group based on the target
30398     * @param {string} target
30399     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30400     */
30401     get: function(target) {
30402         if (typeof(this.groups[target]) == 'undefined') {
30403             return false;
30404         }
30405         
30406         return this.groups[target] ;
30407     }
30408 });
30409
30410  
30411
30412  /*
30413  * - LGPL
30414  *
30415  * page DateSplitField.
30416  * 
30417  */
30418
30419
30420 /**
30421  * @class Roo.bootstrap.DateSplitField
30422  * @extends Roo.bootstrap.Component
30423  * Bootstrap DateSplitField class
30424  * @cfg {string} fieldLabel - the label associated
30425  * @cfg {Number} labelWidth set the width of label (0-12)
30426  * @cfg {String} labelAlign (top|left)
30427  * @cfg {Boolean} dayAllowBlank (true|false) default false
30428  * @cfg {Boolean} monthAllowBlank (true|false) default false
30429  * @cfg {Boolean} yearAllowBlank (true|false) default false
30430  * @cfg {string} dayPlaceholder 
30431  * @cfg {string} monthPlaceholder
30432  * @cfg {string} yearPlaceholder
30433  * @cfg {string} dayFormat default 'd'
30434  * @cfg {string} monthFormat default 'm'
30435  * @cfg {string} yearFormat default 'Y'
30436  * @cfg {Number} labellg set the width of label (1-12)
30437  * @cfg {Number} labelmd set the width of label (1-12)
30438  * @cfg {Number} labelsm set the width of label (1-12)
30439  * @cfg {Number} labelxs set the width of label (1-12)
30440
30441  *     
30442  * @constructor
30443  * Create a new DateSplitField
30444  * @param {Object} config The config object
30445  */
30446
30447 Roo.bootstrap.DateSplitField = function(config){
30448     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30449     
30450     this.addEvents({
30451         // raw events
30452          /**
30453          * @event years
30454          * getting the data of years
30455          * @param {Roo.bootstrap.DateSplitField} this
30456          * @param {Object} years
30457          */
30458         "years" : true,
30459         /**
30460          * @event days
30461          * getting the data of days
30462          * @param {Roo.bootstrap.DateSplitField} this
30463          * @param {Object} days
30464          */
30465         "days" : true,
30466         /**
30467          * @event invalid
30468          * Fires after the field has been marked as invalid.
30469          * @param {Roo.form.Field} this
30470          * @param {String} msg The validation message
30471          */
30472         invalid : true,
30473        /**
30474          * @event valid
30475          * Fires after the field has been validated with no errors.
30476          * @param {Roo.form.Field} this
30477          */
30478         valid : true
30479     });
30480 };
30481
30482 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30483     
30484     fieldLabel : '',
30485     labelAlign : 'top',
30486     labelWidth : 3,
30487     dayAllowBlank : false,
30488     monthAllowBlank : false,
30489     yearAllowBlank : false,
30490     dayPlaceholder : '',
30491     monthPlaceholder : '',
30492     yearPlaceholder : '',
30493     dayFormat : 'd',
30494     monthFormat : 'm',
30495     yearFormat : 'Y',
30496     isFormField : true,
30497     labellg : 0,
30498     labelmd : 0,
30499     labelsm : 0,
30500     labelxs : 0,
30501     
30502     getAutoCreate : function()
30503     {
30504         var cfg = {
30505             tag : 'div',
30506             cls : 'row roo-date-split-field-group',
30507             cn : [
30508                 {
30509                     tag : 'input',
30510                     type : 'hidden',
30511                     cls : 'form-hidden-field roo-date-split-field-group-value',
30512                     name : this.name
30513                 }
30514             ]
30515         };
30516         
30517         var labelCls = 'col-md-12';
30518         var contentCls = 'col-md-4';
30519         
30520         if(this.fieldLabel){
30521             
30522             var label = {
30523                 tag : 'div',
30524                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30525                 cn : [
30526                     {
30527                         tag : 'label',
30528                         html : this.fieldLabel
30529                     }
30530                 ]
30531             };
30532             
30533             if(this.labelAlign == 'left'){
30534             
30535                 if(this.labelWidth > 12){
30536                     label.style = "width: " + this.labelWidth + 'px';
30537                 }
30538
30539                 if(this.labelWidth < 13 && this.labelmd == 0){
30540                     this.labelmd = this.labelWidth;
30541                 }
30542
30543                 if(this.labellg > 0){
30544                     labelCls = ' col-lg-' + this.labellg;
30545                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30546                 }
30547
30548                 if(this.labelmd > 0){
30549                     labelCls = ' col-md-' + this.labelmd;
30550                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30551                 }
30552
30553                 if(this.labelsm > 0){
30554                     labelCls = ' col-sm-' + this.labelsm;
30555                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30556                 }
30557
30558                 if(this.labelxs > 0){
30559                     labelCls = ' col-xs-' + this.labelxs;
30560                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30561                 }
30562             }
30563             
30564             label.cls += ' ' + labelCls;
30565             
30566             cfg.cn.push(label);
30567         }
30568         
30569         Roo.each(['day', 'month', 'year'], function(t){
30570             cfg.cn.push({
30571                 tag : 'div',
30572                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30573             });
30574         }, this);
30575         
30576         return cfg;
30577     },
30578     
30579     inputEl: function ()
30580     {
30581         return this.el.select('.roo-date-split-field-group-value', true).first();
30582     },
30583     
30584     onRender : function(ct, position) 
30585     {
30586         var _this = this;
30587         
30588         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30589         
30590         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30591         
30592         this.dayField = new Roo.bootstrap.ComboBox({
30593             allowBlank : this.dayAllowBlank,
30594             alwaysQuery : true,
30595             displayField : 'value',
30596             editable : false,
30597             fieldLabel : '',
30598             forceSelection : true,
30599             mode : 'local',
30600             placeholder : this.dayPlaceholder,
30601             selectOnFocus : true,
30602             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30603             triggerAction : 'all',
30604             typeAhead : true,
30605             valueField : 'value',
30606             store : new Roo.data.SimpleStore({
30607                 data : (function() {    
30608                     var days = [];
30609                     _this.fireEvent('days', _this, days);
30610                     return days;
30611                 })(),
30612                 fields : [ 'value' ]
30613             }),
30614             listeners : {
30615                 select : function (_self, record, index)
30616                 {
30617                     _this.setValue(_this.getValue());
30618                 }
30619             }
30620         });
30621
30622         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30623         
30624         this.monthField = new Roo.bootstrap.MonthField({
30625             after : '<i class=\"fa fa-calendar\"></i>',
30626             allowBlank : this.monthAllowBlank,
30627             placeholder : this.monthPlaceholder,
30628             readOnly : true,
30629             listeners : {
30630                 render : function (_self)
30631                 {
30632                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30633                         e.preventDefault();
30634                         _self.focus();
30635                     });
30636                 },
30637                 select : function (_self, oldvalue, newvalue)
30638                 {
30639                     _this.setValue(_this.getValue());
30640                 }
30641             }
30642         });
30643         
30644         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30645         
30646         this.yearField = new Roo.bootstrap.ComboBox({
30647             allowBlank : this.yearAllowBlank,
30648             alwaysQuery : true,
30649             displayField : 'value',
30650             editable : false,
30651             fieldLabel : '',
30652             forceSelection : true,
30653             mode : 'local',
30654             placeholder : this.yearPlaceholder,
30655             selectOnFocus : true,
30656             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30657             triggerAction : 'all',
30658             typeAhead : true,
30659             valueField : 'value',
30660             store : new Roo.data.SimpleStore({
30661                 data : (function() {
30662                     var years = [];
30663                     _this.fireEvent('years', _this, years);
30664                     return years;
30665                 })(),
30666                 fields : [ 'value' ]
30667             }),
30668             listeners : {
30669                 select : function (_self, record, index)
30670                 {
30671                     _this.setValue(_this.getValue());
30672                 }
30673             }
30674         });
30675
30676         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30677     },
30678     
30679     setValue : function(v, format)
30680     {
30681         this.inputEl.dom.value = v;
30682         
30683         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30684         
30685         var d = Date.parseDate(v, f);
30686         
30687         if(!d){
30688             this.validate();
30689             return;
30690         }
30691         
30692         this.setDay(d.format(this.dayFormat));
30693         this.setMonth(d.format(this.monthFormat));
30694         this.setYear(d.format(this.yearFormat));
30695         
30696         this.validate();
30697         
30698         return;
30699     },
30700     
30701     setDay : function(v)
30702     {
30703         this.dayField.setValue(v);
30704         this.inputEl.dom.value = this.getValue();
30705         this.validate();
30706         return;
30707     },
30708     
30709     setMonth : function(v)
30710     {
30711         this.monthField.setValue(v, true);
30712         this.inputEl.dom.value = this.getValue();
30713         this.validate();
30714         return;
30715     },
30716     
30717     setYear : function(v)
30718     {
30719         this.yearField.setValue(v);
30720         this.inputEl.dom.value = this.getValue();
30721         this.validate();
30722         return;
30723     },
30724     
30725     getDay : function()
30726     {
30727         return this.dayField.getValue();
30728     },
30729     
30730     getMonth : function()
30731     {
30732         return this.monthField.getValue();
30733     },
30734     
30735     getYear : function()
30736     {
30737         return this.yearField.getValue();
30738     },
30739     
30740     getValue : function()
30741     {
30742         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30743         
30744         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30745         
30746         return date;
30747     },
30748     
30749     reset : function()
30750     {
30751         this.setDay('');
30752         this.setMonth('');
30753         this.setYear('');
30754         this.inputEl.dom.value = '';
30755         this.validate();
30756         return;
30757     },
30758     
30759     validate : function()
30760     {
30761         var d = this.dayField.validate();
30762         var m = this.monthField.validate();
30763         var y = this.yearField.validate();
30764         
30765         var valid = true;
30766         
30767         if(
30768                 (!this.dayAllowBlank && !d) ||
30769                 (!this.monthAllowBlank && !m) ||
30770                 (!this.yearAllowBlank && !y)
30771         ){
30772             valid = false;
30773         }
30774         
30775         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30776             return valid;
30777         }
30778         
30779         if(valid){
30780             this.markValid();
30781             return valid;
30782         }
30783         
30784         this.markInvalid();
30785         
30786         return valid;
30787     },
30788     
30789     markValid : function()
30790     {
30791         
30792         var label = this.el.select('label', true).first();
30793         var icon = this.el.select('i.fa-star', true).first();
30794
30795         if(label && icon){
30796             icon.remove();
30797         }
30798         
30799         this.fireEvent('valid', this);
30800     },
30801     
30802      /**
30803      * Mark this field as invalid
30804      * @param {String} msg The validation message
30805      */
30806     markInvalid : function(msg)
30807     {
30808         
30809         var label = this.el.select('label', true).first();
30810         var icon = this.el.select('i.fa-star', true).first();
30811
30812         if(label && !icon){
30813             this.el.select('.roo-date-split-field-label', true).createChild({
30814                 tag : 'i',
30815                 cls : 'text-danger fa fa-lg fa-star',
30816                 tooltip : 'This field is required',
30817                 style : 'margin-right:5px;'
30818             }, label, true);
30819         }
30820         
30821         this.fireEvent('invalid', this, msg);
30822     },
30823     
30824     clearInvalid : function()
30825     {
30826         var label = this.el.select('label', true).first();
30827         var icon = this.el.select('i.fa-star', true).first();
30828
30829         if(label && icon){
30830             icon.remove();
30831         }
30832         
30833         this.fireEvent('valid', this);
30834     },
30835     
30836     getName: function()
30837     {
30838         return this.name;
30839     }
30840     
30841 });
30842
30843  /**
30844  *
30845  * This is based on 
30846  * http://masonry.desandro.com
30847  *
30848  * The idea is to render all the bricks based on vertical width...
30849  *
30850  * The original code extends 'outlayer' - we might need to use that....
30851  * 
30852  */
30853
30854
30855 /**
30856  * @class Roo.bootstrap.LayoutMasonry
30857  * @extends Roo.bootstrap.Component
30858  * Bootstrap Layout Masonry class
30859  * 
30860  * @constructor
30861  * Create a new Element
30862  * @param {Object} config The config object
30863  */
30864
30865 Roo.bootstrap.LayoutMasonry = function(config){
30866     
30867     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30868     
30869     this.bricks = [];
30870     
30871     Roo.bootstrap.LayoutMasonry.register(this);
30872     
30873     this.addEvents({
30874         // raw events
30875         /**
30876          * @event layout
30877          * Fire after layout the items
30878          * @param {Roo.bootstrap.LayoutMasonry} this
30879          * @param {Roo.EventObject} e
30880          */
30881         "layout" : true
30882     });
30883     
30884 };
30885
30886 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30887     
30888     /**
30889      * @cfg {Boolean} isLayoutInstant = no animation?
30890      */   
30891     isLayoutInstant : false, // needed?
30892    
30893     /**
30894      * @cfg {Number} boxWidth  width of the columns
30895      */   
30896     boxWidth : 450,
30897     
30898       /**
30899      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30900      */   
30901     boxHeight : 0,
30902     
30903     /**
30904      * @cfg {Number} padWidth padding below box..
30905      */   
30906     padWidth : 10, 
30907     
30908     /**
30909      * @cfg {Number} gutter gutter width..
30910      */   
30911     gutter : 10,
30912     
30913      /**
30914      * @cfg {Number} maxCols maximum number of columns
30915      */   
30916     
30917     maxCols: 0,
30918     
30919     /**
30920      * @cfg {Boolean} isAutoInitial defalut true
30921      */   
30922     isAutoInitial : true, 
30923     
30924     containerWidth: 0,
30925     
30926     /**
30927      * @cfg {Boolean} isHorizontal defalut false
30928      */   
30929     isHorizontal : false, 
30930
30931     currentSize : null,
30932     
30933     tag: 'div',
30934     
30935     cls: '',
30936     
30937     bricks: null, //CompositeElement
30938     
30939     cols : 1,
30940     
30941     _isLayoutInited : false,
30942     
30943 //    isAlternative : false, // only use for vertical layout...
30944     
30945     /**
30946      * @cfg {Number} alternativePadWidth padding below box..
30947      */   
30948     alternativePadWidth : 50,
30949     
30950     selectedBrick : [],
30951     
30952     getAutoCreate : function(){
30953         
30954         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30955         
30956         var cfg = {
30957             tag: this.tag,
30958             cls: 'blog-masonary-wrapper ' + this.cls,
30959             cn : {
30960                 cls : 'mas-boxes masonary'
30961             }
30962         };
30963         
30964         return cfg;
30965     },
30966     
30967     getChildContainer: function( )
30968     {
30969         if (this.boxesEl) {
30970             return this.boxesEl;
30971         }
30972         
30973         this.boxesEl = this.el.select('.mas-boxes').first();
30974         
30975         return this.boxesEl;
30976     },
30977     
30978     
30979     initEvents : function()
30980     {
30981         var _this = this;
30982         
30983         if(this.isAutoInitial){
30984             Roo.log('hook children rendered');
30985             this.on('childrenrendered', function() {
30986                 Roo.log('children rendered');
30987                 _this.initial();
30988             } ,this);
30989         }
30990     },
30991     
30992     initial : function()
30993     {
30994         this.selectedBrick = [];
30995         
30996         this.currentSize = this.el.getBox(true);
30997         
30998         Roo.EventManager.onWindowResize(this.resize, this); 
30999
31000         if(!this.isAutoInitial){
31001             this.layout();
31002             return;
31003         }
31004         
31005         this.layout();
31006         
31007         return;
31008         //this.layout.defer(500,this);
31009         
31010     },
31011     
31012     resize : function()
31013     {
31014         var cs = this.el.getBox(true);
31015         
31016         if (
31017                 this.currentSize.width == cs.width && 
31018                 this.currentSize.x == cs.x && 
31019                 this.currentSize.height == cs.height && 
31020                 this.currentSize.y == cs.y 
31021         ) {
31022             Roo.log("no change in with or X or Y");
31023             return;
31024         }
31025         
31026         this.currentSize = cs;
31027         
31028         this.layout();
31029         
31030     },
31031     
31032     layout : function()
31033     {   
31034         this._resetLayout();
31035         
31036         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31037         
31038         this.layoutItems( isInstant );
31039       
31040         this._isLayoutInited = true;
31041         
31042         this.fireEvent('layout', this);
31043         
31044     },
31045     
31046     _resetLayout : function()
31047     {
31048         if(this.isHorizontal){
31049             this.horizontalMeasureColumns();
31050             return;
31051         }
31052         
31053         this.verticalMeasureColumns();
31054         
31055     },
31056     
31057     verticalMeasureColumns : function()
31058     {
31059         this.getContainerWidth();
31060         
31061 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31062 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31063 //            return;
31064 //        }
31065         
31066         var boxWidth = this.boxWidth + this.padWidth;
31067         
31068         if(this.containerWidth < this.boxWidth){
31069             boxWidth = this.containerWidth
31070         }
31071         
31072         var containerWidth = this.containerWidth;
31073         
31074         var cols = Math.floor(containerWidth / boxWidth);
31075         
31076         this.cols = Math.max( cols, 1 );
31077         
31078         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31079         
31080         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31081         
31082         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31083         
31084         this.colWidth = boxWidth + avail - this.padWidth;
31085         
31086         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31087         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31088     },
31089     
31090     horizontalMeasureColumns : function()
31091     {
31092         this.getContainerWidth();
31093         
31094         var boxWidth = this.boxWidth;
31095         
31096         if(this.containerWidth < boxWidth){
31097             boxWidth = this.containerWidth;
31098         }
31099         
31100         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31101         
31102         this.el.setHeight(boxWidth);
31103         
31104     },
31105     
31106     getContainerWidth : function()
31107     {
31108         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31109     },
31110     
31111     layoutItems : function( isInstant )
31112     {
31113         Roo.log(this.bricks);
31114         
31115         var items = Roo.apply([], this.bricks);
31116         
31117         if(this.isHorizontal){
31118             this._horizontalLayoutItems( items , isInstant );
31119             return;
31120         }
31121         
31122 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31123 //            this._verticalAlternativeLayoutItems( items , isInstant );
31124 //            return;
31125 //        }
31126         
31127         this._verticalLayoutItems( items , isInstant );
31128         
31129     },
31130     
31131     _verticalLayoutItems : function ( items , isInstant)
31132     {
31133         if ( !items || !items.length ) {
31134             return;
31135         }
31136         
31137         var standard = [
31138             ['xs', 'xs', 'xs', 'tall'],
31139             ['xs', 'xs', 'tall'],
31140             ['xs', 'xs', 'sm'],
31141             ['xs', 'xs', 'xs'],
31142             ['xs', 'tall'],
31143             ['xs', 'sm'],
31144             ['xs', 'xs'],
31145             ['xs'],
31146             
31147             ['sm', 'xs', 'xs'],
31148             ['sm', 'xs'],
31149             ['sm'],
31150             
31151             ['tall', 'xs', 'xs', 'xs'],
31152             ['tall', 'xs', 'xs'],
31153             ['tall', 'xs'],
31154             ['tall']
31155             
31156         ];
31157         
31158         var queue = [];
31159         
31160         var boxes = [];
31161         
31162         var box = [];
31163         
31164         Roo.each(items, function(item, k){
31165             
31166             switch (item.size) {
31167                 // these layouts take up a full box,
31168                 case 'md' :
31169                 case 'md-left' :
31170                 case 'md-right' :
31171                 case 'wide' :
31172                     
31173                     if(box.length){
31174                         boxes.push(box);
31175                         box = [];
31176                     }
31177                     
31178                     boxes.push([item]);
31179                     
31180                     break;
31181                     
31182                 case 'xs' :
31183                 case 'sm' :
31184                 case 'tall' :
31185                     
31186                     box.push(item);
31187                     
31188                     break;
31189                 default :
31190                     break;
31191                     
31192             }
31193             
31194         }, this);
31195         
31196         if(box.length){
31197             boxes.push(box);
31198             box = [];
31199         }
31200         
31201         var filterPattern = function(box, length)
31202         {
31203             if(!box.length){
31204                 return;
31205             }
31206             
31207             var match = false;
31208             
31209             var pattern = box.slice(0, length);
31210             
31211             var format = [];
31212             
31213             Roo.each(pattern, function(i){
31214                 format.push(i.size);
31215             }, this);
31216             
31217             Roo.each(standard, function(s){
31218                 
31219                 if(String(s) != String(format)){
31220                     return;
31221                 }
31222                 
31223                 match = true;
31224                 return false;
31225                 
31226             }, this);
31227             
31228             if(!match && length == 1){
31229                 return;
31230             }
31231             
31232             if(!match){
31233                 filterPattern(box, length - 1);
31234                 return;
31235             }
31236                 
31237             queue.push(pattern);
31238
31239             box = box.slice(length, box.length);
31240
31241             filterPattern(box, 4);
31242
31243             return;
31244             
31245         }
31246         
31247         Roo.each(boxes, function(box, k){
31248             
31249             if(!box.length){
31250                 return;
31251             }
31252             
31253             if(box.length == 1){
31254                 queue.push(box);
31255                 return;
31256             }
31257             
31258             filterPattern(box, 4);
31259             
31260         }, this);
31261         
31262         this._processVerticalLayoutQueue( queue, isInstant );
31263         
31264     },
31265     
31266 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31267 //    {
31268 //        if ( !items || !items.length ) {
31269 //            return;
31270 //        }
31271 //
31272 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31273 //        
31274 //    },
31275     
31276     _horizontalLayoutItems : function ( items , isInstant)
31277     {
31278         if ( !items || !items.length || items.length < 3) {
31279             return;
31280         }
31281         
31282         items.reverse();
31283         
31284         var eItems = items.slice(0, 3);
31285         
31286         items = items.slice(3, items.length);
31287         
31288         var standard = [
31289             ['xs', 'xs', 'xs', 'wide'],
31290             ['xs', 'xs', 'wide'],
31291             ['xs', 'xs', 'sm'],
31292             ['xs', 'xs', 'xs'],
31293             ['xs', 'wide'],
31294             ['xs', 'sm'],
31295             ['xs', 'xs'],
31296             ['xs'],
31297             
31298             ['sm', 'xs', 'xs'],
31299             ['sm', 'xs'],
31300             ['sm'],
31301             
31302             ['wide', 'xs', 'xs', 'xs'],
31303             ['wide', 'xs', 'xs'],
31304             ['wide', 'xs'],
31305             ['wide'],
31306             
31307             ['wide-thin']
31308         ];
31309         
31310         var queue = [];
31311         
31312         var boxes = [];
31313         
31314         var box = [];
31315         
31316         Roo.each(items, function(item, k){
31317             
31318             switch (item.size) {
31319                 case 'md' :
31320                 case 'md-left' :
31321                 case 'md-right' :
31322                 case 'tall' :
31323                     
31324                     if(box.length){
31325                         boxes.push(box);
31326                         box = [];
31327                     }
31328                     
31329                     boxes.push([item]);
31330                     
31331                     break;
31332                     
31333                 case 'xs' :
31334                 case 'sm' :
31335                 case 'wide' :
31336                 case 'wide-thin' :
31337                     
31338                     box.push(item);
31339                     
31340                     break;
31341                 default :
31342                     break;
31343                     
31344             }
31345             
31346         }, this);
31347         
31348         if(box.length){
31349             boxes.push(box);
31350             box = [];
31351         }
31352         
31353         var filterPattern = function(box, length)
31354         {
31355             if(!box.length){
31356                 return;
31357             }
31358             
31359             var match = false;
31360             
31361             var pattern = box.slice(0, length);
31362             
31363             var format = [];
31364             
31365             Roo.each(pattern, function(i){
31366                 format.push(i.size);
31367             }, this);
31368             
31369             Roo.each(standard, function(s){
31370                 
31371                 if(String(s) != String(format)){
31372                     return;
31373                 }
31374                 
31375                 match = true;
31376                 return false;
31377                 
31378             }, this);
31379             
31380             if(!match && length == 1){
31381                 return;
31382             }
31383             
31384             if(!match){
31385                 filterPattern(box, length - 1);
31386                 return;
31387             }
31388                 
31389             queue.push(pattern);
31390
31391             box = box.slice(length, box.length);
31392
31393             filterPattern(box, 4);
31394
31395             return;
31396             
31397         }
31398         
31399         Roo.each(boxes, function(box, k){
31400             
31401             if(!box.length){
31402                 return;
31403             }
31404             
31405             if(box.length == 1){
31406                 queue.push(box);
31407                 return;
31408             }
31409             
31410             filterPattern(box, 4);
31411             
31412         }, this);
31413         
31414         
31415         var prune = [];
31416         
31417         var pos = this.el.getBox(true);
31418         
31419         var minX = pos.x;
31420         
31421         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31422         
31423         var hit_end = false;
31424         
31425         Roo.each(queue, function(box){
31426             
31427             if(hit_end){
31428                 
31429                 Roo.each(box, function(b){
31430                 
31431                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31432                     b.el.hide();
31433
31434                 }, this);
31435
31436                 return;
31437             }
31438             
31439             var mx = 0;
31440             
31441             Roo.each(box, function(b){
31442                 
31443                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31444                 b.el.show();
31445
31446                 mx = Math.max(mx, b.x);
31447                 
31448             }, this);
31449             
31450             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31451             
31452             if(maxX < minX){
31453                 
31454                 Roo.each(box, function(b){
31455                 
31456                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31457                     b.el.hide();
31458                     
31459                 }, this);
31460                 
31461                 hit_end = true;
31462                 
31463                 return;
31464             }
31465             
31466             prune.push(box);
31467             
31468         }, this);
31469         
31470         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31471     },
31472     
31473     /** Sets position of item in DOM
31474     * @param {Element} item
31475     * @param {Number} x - horizontal position
31476     * @param {Number} y - vertical position
31477     * @param {Boolean} isInstant - disables transitions
31478     */
31479     _processVerticalLayoutQueue : function( queue, isInstant )
31480     {
31481         var pos = this.el.getBox(true);
31482         var x = pos.x;
31483         var y = pos.y;
31484         var maxY = [];
31485         
31486         for (var i = 0; i < this.cols; i++){
31487             maxY[i] = pos.y;
31488         }
31489         
31490         Roo.each(queue, function(box, k){
31491             
31492             var col = k % this.cols;
31493             
31494             Roo.each(box, function(b,kk){
31495                 
31496                 b.el.position('absolute');
31497                 
31498                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31499                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31500                 
31501                 if(b.size == 'md-left' || b.size == 'md-right'){
31502                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31503                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31504                 }
31505                 
31506                 b.el.setWidth(width);
31507                 b.el.setHeight(height);
31508                 // iframe?
31509                 b.el.select('iframe',true).setSize(width,height);
31510                 
31511             }, this);
31512             
31513             for (var i = 0; i < this.cols; i++){
31514                 
31515                 if(maxY[i] < maxY[col]){
31516                     col = i;
31517                     continue;
31518                 }
31519                 
31520                 col = Math.min(col, i);
31521                 
31522             }
31523             
31524             x = pos.x + col * (this.colWidth + this.padWidth);
31525             
31526             y = maxY[col];
31527             
31528             var positions = [];
31529             
31530             switch (box.length){
31531                 case 1 :
31532                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31533                     break;
31534                 case 2 :
31535                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31536                     break;
31537                 case 3 :
31538                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31539                     break;
31540                 case 4 :
31541                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31542                     break;
31543                 default :
31544                     break;
31545             }
31546             
31547             Roo.each(box, function(b,kk){
31548                 
31549                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31550                 
31551                 var sz = b.el.getSize();
31552                 
31553                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31554                 
31555             }, this);
31556             
31557         }, this);
31558         
31559         var mY = 0;
31560         
31561         for (var i = 0; i < this.cols; i++){
31562             mY = Math.max(mY, maxY[i]);
31563         }
31564         
31565         this.el.setHeight(mY - pos.y);
31566         
31567     },
31568     
31569 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31570 //    {
31571 //        var pos = this.el.getBox(true);
31572 //        var x = pos.x;
31573 //        var y = pos.y;
31574 //        var maxX = pos.right;
31575 //        
31576 //        var maxHeight = 0;
31577 //        
31578 //        Roo.each(items, function(item, k){
31579 //            
31580 //            var c = k % 2;
31581 //            
31582 //            item.el.position('absolute');
31583 //                
31584 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31585 //
31586 //            item.el.setWidth(width);
31587 //
31588 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31589 //
31590 //            item.el.setHeight(height);
31591 //            
31592 //            if(c == 0){
31593 //                item.el.setXY([x, y], isInstant ? false : true);
31594 //            } else {
31595 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31596 //            }
31597 //            
31598 //            y = y + height + this.alternativePadWidth;
31599 //            
31600 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31601 //            
31602 //        }, this);
31603 //        
31604 //        this.el.setHeight(maxHeight);
31605 //        
31606 //    },
31607     
31608     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31609     {
31610         var pos = this.el.getBox(true);
31611         
31612         var minX = pos.x;
31613         var minY = pos.y;
31614         
31615         var maxX = pos.right;
31616         
31617         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31618         
31619         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31620         
31621         Roo.each(queue, function(box, k){
31622             
31623             Roo.each(box, function(b, kk){
31624                 
31625                 b.el.position('absolute');
31626                 
31627                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31628                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31629                 
31630                 if(b.size == 'md-left' || b.size == 'md-right'){
31631                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31632                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31633                 }
31634                 
31635                 b.el.setWidth(width);
31636                 b.el.setHeight(height);
31637                 
31638             }, this);
31639             
31640             if(!box.length){
31641                 return;
31642             }
31643             
31644             var positions = [];
31645             
31646             switch (box.length){
31647                 case 1 :
31648                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31649                     break;
31650                 case 2 :
31651                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31652                     break;
31653                 case 3 :
31654                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31655                     break;
31656                 case 4 :
31657                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31658                     break;
31659                 default :
31660                     break;
31661             }
31662             
31663             Roo.each(box, function(b,kk){
31664                 
31665                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31666                 
31667                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31668                 
31669             }, this);
31670             
31671         }, this);
31672         
31673     },
31674     
31675     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31676     {
31677         Roo.each(eItems, function(b,k){
31678             
31679             b.size = (k == 0) ? 'sm' : 'xs';
31680             b.x = (k == 0) ? 2 : 1;
31681             b.y = (k == 0) ? 2 : 1;
31682             
31683             b.el.position('absolute');
31684             
31685             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31686                 
31687             b.el.setWidth(width);
31688             
31689             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31690             
31691             b.el.setHeight(height);
31692             
31693         }, this);
31694
31695         var positions = [];
31696         
31697         positions.push({
31698             x : maxX - this.unitWidth * 2 - this.gutter,
31699             y : minY
31700         });
31701         
31702         positions.push({
31703             x : maxX - this.unitWidth,
31704             y : minY + (this.unitWidth + this.gutter) * 2
31705         });
31706         
31707         positions.push({
31708             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31709             y : minY
31710         });
31711         
31712         Roo.each(eItems, function(b,k){
31713             
31714             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31715
31716         }, this);
31717         
31718     },
31719     
31720     getVerticalOneBoxColPositions : function(x, y, box)
31721     {
31722         var pos = [];
31723         
31724         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31725         
31726         if(box[0].size == 'md-left'){
31727             rand = 0;
31728         }
31729         
31730         if(box[0].size == 'md-right'){
31731             rand = 1;
31732         }
31733         
31734         pos.push({
31735             x : x + (this.unitWidth + this.gutter) * rand,
31736             y : y
31737         });
31738         
31739         return pos;
31740     },
31741     
31742     getVerticalTwoBoxColPositions : function(x, y, box)
31743     {
31744         var pos = [];
31745         
31746         if(box[0].size == 'xs'){
31747             
31748             pos.push({
31749                 x : x,
31750                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31751             });
31752
31753             pos.push({
31754                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31755                 y : y
31756             });
31757             
31758             return pos;
31759             
31760         }
31761         
31762         pos.push({
31763             x : x,
31764             y : y
31765         });
31766
31767         pos.push({
31768             x : x + (this.unitWidth + this.gutter) * 2,
31769             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31770         });
31771         
31772         return pos;
31773         
31774     },
31775     
31776     getVerticalThreeBoxColPositions : function(x, y, box)
31777     {
31778         var pos = [];
31779         
31780         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31781             
31782             pos.push({
31783                 x : x,
31784                 y : y
31785             });
31786
31787             pos.push({
31788                 x : x + (this.unitWidth + this.gutter) * 1,
31789                 y : y
31790             });
31791             
31792             pos.push({
31793                 x : x + (this.unitWidth + this.gutter) * 2,
31794                 y : y
31795             });
31796             
31797             return pos;
31798             
31799         }
31800         
31801         if(box[0].size == 'xs' && box[1].size == 'xs'){
31802             
31803             pos.push({
31804                 x : x,
31805                 y : y
31806             });
31807
31808             pos.push({
31809                 x : x,
31810                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31811             });
31812             
31813             pos.push({
31814                 x : x + (this.unitWidth + this.gutter) * 1,
31815                 y : y
31816             });
31817             
31818             return pos;
31819             
31820         }
31821         
31822         pos.push({
31823             x : x,
31824             y : y
31825         });
31826
31827         pos.push({
31828             x : x + (this.unitWidth + this.gutter) * 2,
31829             y : y
31830         });
31831
31832         pos.push({
31833             x : x + (this.unitWidth + this.gutter) * 2,
31834             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31835         });
31836             
31837         return pos;
31838         
31839     },
31840     
31841     getVerticalFourBoxColPositions : function(x, y, box)
31842     {
31843         var pos = [];
31844         
31845         if(box[0].size == 'xs'){
31846             
31847             pos.push({
31848                 x : x,
31849                 y : y
31850             });
31851
31852             pos.push({
31853                 x : x,
31854                 y : y + (this.unitHeight + this.gutter) * 1
31855             });
31856             
31857             pos.push({
31858                 x : x,
31859                 y : y + (this.unitHeight + this.gutter) * 2
31860             });
31861             
31862             pos.push({
31863                 x : x + (this.unitWidth + this.gutter) * 1,
31864                 y : y
31865             });
31866             
31867             return pos;
31868             
31869         }
31870         
31871         pos.push({
31872             x : x,
31873             y : y
31874         });
31875
31876         pos.push({
31877             x : x + (this.unitWidth + this.gutter) * 2,
31878             y : y
31879         });
31880
31881         pos.push({
31882             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31883             y : y + (this.unitHeight + this.gutter) * 1
31884         });
31885
31886         pos.push({
31887             x : x + (this.unitWidth + this.gutter) * 2,
31888             y : y + (this.unitWidth + this.gutter) * 2
31889         });
31890
31891         return pos;
31892         
31893     },
31894     
31895     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31896     {
31897         var pos = [];
31898         
31899         if(box[0].size == 'md-left'){
31900             pos.push({
31901                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31902                 y : minY
31903             });
31904             
31905             return pos;
31906         }
31907         
31908         if(box[0].size == 'md-right'){
31909             pos.push({
31910                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31911                 y : minY + (this.unitWidth + this.gutter) * 1
31912             });
31913             
31914             return pos;
31915         }
31916         
31917         var rand = Math.floor(Math.random() * (4 - box[0].y));
31918         
31919         pos.push({
31920             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31921             y : minY + (this.unitWidth + this.gutter) * rand
31922         });
31923         
31924         return pos;
31925         
31926     },
31927     
31928     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31929     {
31930         var pos = [];
31931         
31932         if(box[0].size == 'xs'){
31933             
31934             pos.push({
31935                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31936                 y : minY
31937             });
31938
31939             pos.push({
31940                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31941                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31942             });
31943             
31944             return pos;
31945             
31946         }
31947         
31948         pos.push({
31949             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31950             y : minY
31951         });
31952
31953         pos.push({
31954             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31955             y : minY + (this.unitWidth + this.gutter) * 2
31956         });
31957         
31958         return pos;
31959         
31960     },
31961     
31962     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31963     {
31964         var pos = [];
31965         
31966         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31967             
31968             pos.push({
31969                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31970                 y : minY
31971             });
31972
31973             pos.push({
31974                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31975                 y : minY + (this.unitWidth + this.gutter) * 1
31976             });
31977             
31978             pos.push({
31979                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31980                 y : minY + (this.unitWidth + this.gutter) * 2
31981             });
31982             
31983             return pos;
31984             
31985         }
31986         
31987         if(box[0].size == 'xs' && box[1].size == 'xs'){
31988             
31989             pos.push({
31990                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31991                 y : minY
31992             });
31993
31994             pos.push({
31995                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31996                 y : minY
31997             });
31998             
31999             pos.push({
32000                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32001                 y : minY + (this.unitWidth + this.gutter) * 1
32002             });
32003             
32004             return pos;
32005             
32006         }
32007         
32008         pos.push({
32009             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32010             y : minY
32011         });
32012
32013         pos.push({
32014             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32015             y : minY + (this.unitWidth + this.gutter) * 2
32016         });
32017
32018         pos.push({
32019             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32020             y : minY + (this.unitWidth + this.gutter) * 2
32021         });
32022             
32023         return pos;
32024         
32025     },
32026     
32027     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32028     {
32029         var pos = [];
32030         
32031         if(box[0].size == 'xs'){
32032             
32033             pos.push({
32034                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32035                 y : minY
32036             });
32037
32038             pos.push({
32039                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32040                 y : minY
32041             });
32042             
32043             pos.push({
32044                 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),
32045                 y : minY
32046             });
32047             
32048             pos.push({
32049                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32050                 y : minY + (this.unitWidth + this.gutter) * 1
32051             });
32052             
32053             return pos;
32054             
32055         }
32056         
32057         pos.push({
32058             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32059             y : minY
32060         });
32061         
32062         pos.push({
32063             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32064             y : minY + (this.unitWidth + this.gutter) * 2
32065         });
32066         
32067         pos.push({
32068             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32069             y : minY + (this.unitWidth + this.gutter) * 2
32070         });
32071         
32072         pos.push({
32073             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),
32074             y : minY + (this.unitWidth + this.gutter) * 2
32075         });
32076
32077         return pos;
32078         
32079     },
32080     
32081     /**
32082     * remove a Masonry Brick
32083     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32084     */
32085     removeBrick : function(brick_id)
32086     {
32087         if (!brick_id) {
32088             return;
32089         }
32090         
32091         for (var i = 0; i<this.bricks.length; i++) {
32092             if (this.bricks[i].id == brick_id) {
32093                 this.bricks.splice(i,1);
32094                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32095                 this.initial();
32096             }
32097         }
32098     },
32099     
32100     /**
32101     * adds a Masonry Brick
32102     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32103     */
32104     addBrick : function(cfg)
32105     {
32106         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32107         //this.register(cn);
32108         cn.parentId = this.id;
32109         cn.render(this.el);
32110         return cn;
32111     },
32112     
32113     /**
32114     * register a Masonry Brick
32115     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32116     */
32117     
32118     register : function(brick)
32119     {
32120         this.bricks.push(brick);
32121         brick.masonryId = this.id;
32122     },
32123     
32124     /**
32125     * clear all the Masonry Brick
32126     */
32127     clearAll : function()
32128     {
32129         this.bricks = [];
32130         //this.getChildContainer().dom.innerHTML = "";
32131         this.el.dom.innerHTML = '';
32132     },
32133     
32134     getSelected : function()
32135     {
32136         if (!this.selectedBrick) {
32137             return false;
32138         }
32139         
32140         return this.selectedBrick;
32141     }
32142 });
32143
32144 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32145     
32146     groups: {},
32147      /**
32148     * register a Masonry Layout
32149     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32150     */
32151     
32152     register : function(layout)
32153     {
32154         this.groups[layout.id] = layout;
32155     },
32156     /**
32157     * fetch a  Masonry Layout based on the masonry layout ID
32158     * @param {string} the masonry layout to add
32159     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32160     */
32161     
32162     get: function(layout_id) {
32163         if (typeof(this.groups[layout_id]) == 'undefined') {
32164             return false;
32165         }
32166         return this.groups[layout_id] ;
32167     }
32168     
32169     
32170     
32171 });
32172
32173  
32174
32175  /**
32176  *
32177  * This is based on 
32178  * http://masonry.desandro.com
32179  *
32180  * The idea is to render all the bricks based on vertical width...
32181  *
32182  * The original code extends 'outlayer' - we might need to use that....
32183  * 
32184  */
32185
32186
32187 /**
32188  * @class Roo.bootstrap.LayoutMasonryAuto
32189  * @extends Roo.bootstrap.Component
32190  * Bootstrap Layout Masonry class
32191  * 
32192  * @constructor
32193  * Create a new Element
32194  * @param {Object} config The config object
32195  */
32196
32197 Roo.bootstrap.LayoutMasonryAuto = function(config){
32198     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32199 };
32200
32201 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32202     
32203       /**
32204      * @cfg {Boolean} isFitWidth  - resize the width..
32205      */   
32206     isFitWidth : false,  // options..
32207     /**
32208      * @cfg {Boolean} isOriginLeft = left align?
32209      */   
32210     isOriginLeft : true,
32211     /**
32212      * @cfg {Boolean} isOriginTop = top align?
32213      */   
32214     isOriginTop : false,
32215     /**
32216      * @cfg {Boolean} isLayoutInstant = no animation?
32217      */   
32218     isLayoutInstant : false, // needed?
32219     /**
32220      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32221      */   
32222     isResizingContainer : true,
32223     /**
32224      * @cfg {Number} columnWidth  width of the columns 
32225      */   
32226     
32227     columnWidth : 0,
32228     
32229     /**
32230      * @cfg {Number} maxCols maximum number of columns
32231      */   
32232     
32233     maxCols: 0,
32234     /**
32235      * @cfg {Number} padHeight padding below box..
32236      */   
32237     
32238     padHeight : 10, 
32239     
32240     /**
32241      * @cfg {Boolean} isAutoInitial defalut true
32242      */   
32243     
32244     isAutoInitial : true, 
32245     
32246     // private?
32247     gutter : 0,
32248     
32249     containerWidth: 0,
32250     initialColumnWidth : 0,
32251     currentSize : null,
32252     
32253     colYs : null, // array.
32254     maxY : 0,
32255     padWidth: 10,
32256     
32257     
32258     tag: 'div',
32259     cls: '',
32260     bricks: null, //CompositeElement
32261     cols : 0, // array?
32262     // element : null, // wrapped now this.el
32263     _isLayoutInited : null, 
32264     
32265     
32266     getAutoCreate : function(){
32267         
32268         var cfg = {
32269             tag: this.tag,
32270             cls: 'blog-masonary-wrapper ' + this.cls,
32271             cn : {
32272                 cls : 'mas-boxes masonary'
32273             }
32274         };
32275         
32276         return cfg;
32277     },
32278     
32279     getChildContainer: function( )
32280     {
32281         if (this.boxesEl) {
32282             return this.boxesEl;
32283         }
32284         
32285         this.boxesEl = this.el.select('.mas-boxes').first();
32286         
32287         return this.boxesEl;
32288     },
32289     
32290     
32291     initEvents : function()
32292     {
32293         var _this = this;
32294         
32295         if(this.isAutoInitial){
32296             Roo.log('hook children rendered');
32297             this.on('childrenrendered', function() {
32298                 Roo.log('children rendered');
32299                 _this.initial();
32300             } ,this);
32301         }
32302         
32303     },
32304     
32305     initial : function()
32306     {
32307         this.reloadItems();
32308
32309         this.currentSize = this.el.getBox(true);
32310
32311         /// was window resize... - let's see if this works..
32312         Roo.EventManager.onWindowResize(this.resize, this); 
32313
32314         if(!this.isAutoInitial){
32315             this.layout();
32316             return;
32317         }
32318         
32319         this.layout.defer(500,this);
32320     },
32321     
32322     reloadItems: function()
32323     {
32324         this.bricks = this.el.select('.masonry-brick', true);
32325         
32326         this.bricks.each(function(b) {
32327             //Roo.log(b.getSize());
32328             if (!b.attr('originalwidth')) {
32329                 b.attr('originalwidth',  b.getSize().width);
32330             }
32331             
32332         });
32333         
32334         Roo.log(this.bricks.elements.length);
32335     },
32336     
32337     resize : function()
32338     {
32339         Roo.log('resize');
32340         var cs = this.el.getBox(true);
32341         
32342         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32343             Roo.log("no change in with or X");
32344             return;
32345         }
32346         this.currentSize = cs;
32347         this.layout();
32348     },
32349     
32350     layout : function()
32351     {
32352          Roo.log('layout');
32353         this._resetLayout();
32354         //this._manageStamps();
32355       
32356         // don't animate first layout
32357         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32358         this.layoutItems( isInstant );
32359       
32360         // flag for initalized
32361         this._isLayoutInited = true;
32362     },
32363     
32364     layoutItems : function( isInstant )
32365     {
32366         //var items = this._getItemsForLayout( this.items );
32367         // original code supports filtering layout items.. we just ignore it..
32368         
32369         this._layoutItems( this.bricks , isInstant );
32370       
32371         this._postLayout();
32372     },
32373     _layoutItems : function ( items , isInstant)
32374     {
32375        //this.fireEvent( 'layout', this, items );
32376     
32377
32378         if ( !items || !items.elements.length ) {
32379           // no items, emit event with empty array
32380             return;
32381         }
32382
32383         var queue = [];
32384         items.each(function(item) {
32385             Roo.log("layout item");
32386             Roo.log(item);
32387             // get x/y object from method
32388             var position = this._getItemLayoutPosition( item );
32389             // enqueue
32390             position.item = item;
32391             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32392             queue.push( position );
32393         }, this);
32394       
32395         this._processLayoutQueue( queue );
32396     },
32397     /** Sets position of item in DOM
32398     * @param {Element} item
32399     * @param {Number} x - horizontal position
32400     * @param {Number} y - vertical position
32401     * @param {Boolean} isInstant - disables transitions
32402     */
32403     _processLayoutQueue : function( queue )
32404     {
32405         for ( var i=0, len = queue.length; i < len; i++ ) {
32406             var obj = queue[i];
32407             obj.item.position('absolute');
32408             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32409         }
32410     },
32411       
32412     
32413     /**
32414     * Any logic you want to do after each layout,
32415     * i.e. size the container
32416     */
32417     _postLayout : function()
32418     {
32419         this.resizeContainer();
32420     },
32421     
32422     resizeContainer : function()
32423     {
32424         if ( !this.isResizingContainer ) {
32425             return;
32426         }
32427         var size = this._getContainerSize();
32428         if ( size ) {
32429             this.el.setSize(size.width,size.height);
32430             this.boxesEl.setSize(size.width,size.height);
32431         }
32432     },
32433     
32434     
32435     
32436     _resetLayout : function()
32437     {
32438         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32439         this.colWidth = this.el.getWidth();
32440         //this.gutter = this.el.getWidth(); 
32441         
32442         this.measureColumns();
32443
32444         // reset column Y
32445         var i = this.cols;
32446         this.colYs = [];
32447         while (i--) {
32448             this.colYs.push( 0 );
32449         }
32450     
32451         this.maxY = 0;
32452     },
32453
32454     measureColumns : function()
32455     {
32456         this.getContainerWidth();
32457       // if columnWidth is 0, default to outerWidth of first item
32458         if ( !this.columnWidth ) {
32459             var firstItem = this.bricks.first();
32460             Roo.log(firstItem);
32461             this.columnWidth  = this.containerWidth;
32462             if (firstItem && firstItem.attr('originalwidth') ) {
32463                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32464             }
32465             // columnWidth fall back to item of first element
32466             Roo.log("set column width?");
32467                         this.initialColumnWidth = this.columnWidth  ;
32468
32469             // if first elem has no width, default to size of container
32470             
32471         }
32472         
32473         
32474         if (this.initialColumnWidth) {
32475             this.columnWidth = this.initialColumnWidth;
32476         }
32477         
32478         
32479             
32480         // column width is fixed at the top - however if container width get's smaller we should
32481         // reduce it...
32482         
32483         // this bit calcs how man columns..
32484             
32485         var columnWidth = this.columnWidth += this.gutter;
32486       
32487         // calculate columns
32488         var containerWidth = this.containerWidth + this.gutter;
32489         
32490         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32491         // fix rounding errors, typically with gutters
32492         var excess = columnWidth - containerWidth % columnWidth;
32493         
32494         
32495         // if overshoot is less than a pixel, round up, otherwise floor it
32496         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32497         cols = Math[ mathMethod ]( cols );
32498         this.cols = Math.max( cols, 1 );
32499         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32500         
32501          // padding positioning..
32502         var totalColWidth = this.cols * this.columnWidth;
32503         var padavail = this.containerWidth - totalColWidth;
32504         // so for 2 columns - we need 3 'pads'
32505         
32506         var padNeeded = (1+this.cols) * this.padWidth;
32507         
32508         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32509         
32510         this.columnWidth += padExtra
32511         //this.padWidth = Math.floor(padavail /  ( this.cols));
32512         
32513         // adjust colum width so that padding is fixed??
32514         
32515         // we have 3 columns ... total = width * 3
32516         // we have X left over... that should be used by 
32517         
32518         //if (this.expandC) {
32519             
32520         //}
32521         
32522         
32523         
32524     },
32525     
32526     getContainerWidth : function()
32527     {
32528        /* // container is parent if fit width
32529         var container = this.isFitWidth ? this.element.parentNode : this.element;
32530         // check that this.size and size are there
32531         // IE8 triggers resize on body size change, so they might not be
32532         
32533         var size = getSize( container );  //FIXME
32534         this.containerWidth = size && size.innerWidth; //FIXME
32535         */
32536          
32537         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32538         
32539     },
32540     
32541     _getItemLayoutPosition : function( item )  // what is item?
32542     {
32543         // we resize the item to our columnWidth..
32544       
32545         item.setWidth(this.columnWidth);
32546         item.autoBoxAdjust  = false;
32547         
32548         var sz = item.getSize();
32549  
32550         // how many columns does this brick span
32551         var remainder = this.containerWidth % this.columnWidth;
32552         
32553         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32554         // round if off by 1 pixel, otherwise use ceil
32555         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32556         colSpan = Math.min( colSpan, this.cols );
32557         
32558         // normally this should be '1' as we dont' currently allow multi width columns..
32559         
32560         var colGroup = this._getColGroup( colSpan );
32561         // get the minimum Y value from the columns
32562         var minimumY = Math.min.apply( Math, colGroup );
32563         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32564         
32565         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32566          
32567         // position the brick
32568         var position = {
32569             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32570             y: this.currentSize.y + minimumY + this.padHeight
32571         };
32572         
32573         Roo.log(position);
32574         // apply setHeight to necessary columns
32575         var setHeight = minimumY + sz.height + this.padHeight;
32576         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32577         
32578         var setSpan = this.cols + 1 - colGroup.length;
32579         for ( var i = 0; i < setSpan; i++ ) {
32580           this.colYs[ shortColIndex + i ] = setHeight ;
32581         }
32582       
32583         return position;
32584     },
32585     
32586     /**
32587      * @param {Number} colSpan - number of columns the element spans
32588      * @returns {Array} colGroup
32589      */
32590     _getColGroup : function( colSpan )
32591     {
32592         if ( colSpan < 2 ) {
32593           // if brick spans only one column, use all the column Ys
32594           return this.colYs;
32595         }
32596       
32597         var colGroup = [];
32598         // how many different places could this brick fit horizontally
32599         var groupCount = this.cols + 1 - colSpan;
32600         // for each group potential horizontal position
32601         for ( var i = 0; i < groupCount; i++ ) {
32602           // make an array of colY values for that one group
32603           var groupColYs = this.colYs.slice( i, i + colSpan );
32604           // and get the max value of the array
32605           colGroup[i] = Math.max.apply( Math, groupColYs );
32606         }
32607         return colGroup;
32608     },
32609     /*
32610     _manageStamp : function( stamp )
32611     {
32612         var stampSize =  stamp.getSize();
32613         var offset = stamp.getBox();
32614         // get the columns that this stamp affects
32615         var firstX = this.isOriginLeft ? offset.x : offset.right;
32616         var lastX = firstX + stampSize.width;
32617         var firstCol = Math.floor( firstX / this.columnWidth );
32618         firstCol = Math.max( 0, firstCol );
32619         
32620         var lastCol = Math.floor( lastX / this.columnWidth );
32621         // lastCol should not go over if multiple of columnWidth #425
32622         lastCol -= lastX % this.columnWidth ? 0 : 1;
32623         lastCol = Math.min( this.cols - 1, lastCol );
32624         
32625         // set colYs to bottom of the stamp
32626         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32627             stampSize.height;
32628             
32629         for ( var i = firstCol; i <= lastCol; i++ ) {
32630           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32631         }
32632     },
32633     */
32634     
32635     _getContainerSize : function()
32636     {
32637         this.maxY = Math.max.apply( Math, this.colYs );
32638         var size = {
32639             height: this.maxY
32640         };
32641       
32642         if ( this.isFitWidth ) {
32643             size.width = this._getContainerFitWidth();
32644         }
32645       
32646         return size;
32647     },
32648     
32649     _getContainerFitWidth : function()
32650     {
32651         var unusedCols = 0;
32652         // count unused columns
32653         var i = this.cols;
32654         while ( --i ) {
32655           if ( this.colYs[i] !== 0 ) {
32656             break;
32657           }
32658           unusedCols++;
32659         }
32660         // fit container to columns that have been used
32661         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32662     },
32663     
32664     needsResizeLayout : function()
32665     {
32666         var previousWidth = this.containerWidth;
32667         this.getContainerWidth();
32668         return previousWidth !== this.containerWidth;
32669     }
32670  
32671 });
32672
32673  
32674
32675  /*
32676  * - LGPL
32677  *
32678  * element
32679  * 
32680  */
32681
32682 /**
32683  * @class Roo.bootstrap.MasonryBrick
32684  * @extends Roo.bootstrap.Component
32685  * Bootstrap MasonryBrick class
32686  * 
32687  * @constructor
32688  * Create a new MasonryBrick
32689  * @param {Object} config The config object
32690  */
32691
32692 Roo.bootstrap.MasonryBrick = function(config){
32693     
32694     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32695     
32696     Roo.bootstrap.MasonryBrick.register(this);
32697     
32698     this.addEvents({
32699         // raw events
32700         /**
32701          * @event click
32702          * When a MasonryBrick is clcik
32703          * @param {Roo.bootstrap.MasonryBrick} this
32704          * @param {Roo.EventObject} e
32705          */
32706         "click" : true
32707     });
32708 };
32709
32710 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32711     
32712     /**
32713      * @cfg {String} title
32714      */   
32715     title : '',
32716     /**
32717      * @cfg {String} html
32718      */   
32719     html : '',
32720     /**
32721      * @cfg {String} bgimage
32722      */   
32723     bgimage : '',
32724     /**
32725      * @cfg {String} videourl
32726      */   
32727     videourl : '',
32728     /**
32729      * @cfg {String} cls
32730      */   
32731     cls : '',
32732     /**
32733      * @cfg {String} href
32734      */   
32735     href : '',
32736     /**
32737      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32738      */   
32739     size : 'xs',
32740     
32741     /**
32742      * @cfg {String} placetitle (center|bottom)
32743      */   
32744     placetitle : '',
32745     
32746     /**
32747      * @cfg {Boolean} isFitContainer defalut true
32748      */   
32749     isFitContainer : true, 
32750     
32751     /**
32752      * @cfg {Boolean} preventDefault defalut false
32753      */   
32754     preventDefault : false, 
32755     
32756     /**
32757      * @cfg {Boolean} inverse defalut false
32758      */   
32759     maskInverse : false, 
32760     
32761     getAutoCreate : function()
32762     {
32763         if(!this.isFitContainer){
32764             return this.getSplitAutoCreate();
32765         }
32766         
32767         var cls = 'masonry-brick masonry-brick-full';
32768         
32769         if(this.href.length){
32770             cls += ' masonry-brick-link';
32771         }
32772         
32773         if(this.bgimage.length){
32774             cls += ' masonry-brick-image';
32775         }
32776         
32777         if(this.maskInverse){
32778             cls += ' mask-inverse';
32779         }
32780         
32781         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32782             cls += ' enable-mask';
32783         }
32784         
32785         if(this.size){
32786             cls += ' masonry-' + this.size + '-brick';
32787         }
32788         
32789         if(this.placetitle.length){
32790             
32791             switch (this.placetitle) {
32792                 case 'center' :
32793                     cls += ' masonry-center-title';
32794                     break;
32795                 case 'bottom' :
32796                     cls += ' masonry-bottom-title';
32797                     break;
32798                 default:
32799                     break;
32800             }
32801             
32802         } else {
32803             if(!this.html.length && !this.bgimage.length){
32804                 cls += ' masonry-center-title';
32805             }
32806
32807             if(!this.html.length && this.bgimage.length){
32808                 cls += ' masonry-bottom-title';
32809             }
32810         }
32811         
32812         if(this.cls){
32813             cls += ' ' + this.cls;
32814         }
32815         
32816         var cfg = {
32817             tag: (this.href.length) ? 'a' : 'div',
32818             cls: cls,
32819             cn: [
32820                 {
32821                     tag: 'div',
32822                     cls: 'masonry-brick-mask'
32823                 },
32824                 {
32825                     tag: 'div',
32826                     cls: 'masonry-brick-paragraph',
32827                     cn: []
32828                 }
32829             ]
32830         };
32831         
32832         if(this.href.length){
32833             cfg.href = this.href;
32834         }
32835         
32836         var cn = cfg.cn[1].cn;
32837         
32838         if(this.title.length){
32839             cn.push({
32840                 tag: 'h4',
32841                 cls: 'masonry-brick-title',
32842                 html: this.title
32843             });
32844         }
32845         
32846         if(this.html.length){
32847             cn.push({
32848                 tag: 'p',
32849                 cls: 'masonry-brick-text',
32850                 html: this.html
32851             });
32852         }
32853         
32854         if (!this.title.length && !this.html.length) {
32855             cfg.cn[1].cls += ' hide';
32856         }
32857         
32858         if(this.bgimage.length){
32859             cfg.cn.push({
32860                 tag: 'img',
32861                 cls: 'masonry-brick-image-view',
32862                 src: this.bgimage
32863             });
32864         }
32865         
32866         if(this.videourl.length){
32867             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32868             // youtube support only?
32869             cfg.cn.push({
32870                 tag: 'iframe',
32871                 cls: 'masonry-brick-image-view',
32872                 src: vurl,
32873                 frameborder : 0,
32874                 allowfullscreen : true
32875             });
32876         }
32877         
32878         return cfg;
32879         
32880     },
32881     
32882     getSplitAutoCreate : function()
32883     {
32884         var cls = 'masonry-brick masonry-brick-split';
32885         
32886         if(this.href.length){
32887             cls += ' masonry-brick-link';
32888         }
32889         
32890         if(this.bgimage.length){
32891             cls += ' masonry-brick-image';
32892         }
32893         
32894         if(this.size){
32895             cls += ' masonry-' + this.size + '-brick';
32896         }
32897         
32898         switch (this.placetitle) {
32899             case 'center' :
32900                 cls += ' masonry-center-title';
32901                 break;
32902             case 'bottom' :
32903                 cls += ' masonry-bottom-title';
32904                 break;
32905             default:
32906                 if(!this.bgimage.length){
32907                     cls += ' masonry-center-title';
32908                 }
32909
32910                 if(this.bgimage.length){
32911                     cls += ' masonry-bottom-title';
32912                 }
32913                 break;
32914         }
32915         
32916         if(this.cls){
32917             cls += ' ' + this.cls;
32918         }
32919         
32920         var cfg = {
32921             tag: (this.href.length) ? 'a' : 'div',
32922             cls: cls,
32923             cn: [
32924                 {
32925                     tag: 'div',
32926                     cls: 'masonry-brick-split-head',
32927                     cn: [
32928                         {
32929                             tag: 'div',
32930                             cls: 'masonry-brick-paragraph',
32931                             cn: []
32932                         }
32933                     ]
32934                 },
32935                 {
32936                     tag: 'div',
32937                     cls: 'masonry-brick-split-body',
32938                     cn: []
32939                 }
32940             ]
32941         };
32942         
32943         if(this.href.length){
32944             cfg.href = this.href;
32945         }
32946         
32947         if(this.title.length){
32948             cfg.cn[0].cn[0].cn.push({
32949                 tag: 'h4',
32950                 cls: 'masonry-brick-title',
32951                 html: this.title
32952             });
32953         }
32954         
32955         if(this.html.length){
32956             cfg.cn[1].cn.push({
32957                 tag: 'p',
32958                 cls: 'masonry-brick-text',
32959                 html: this.html
32960             });
32961         }
32962
32963         if(this.bgimage.length){
32964             cfg.cn[0].cn.push({
32965                 tag: 'img',
32966                 cls: 'masonry-brick-image-view',
32967                 src: this.bgimage
32968             });
32969         }
32970         
32971         if(this.videourl.length){
32972             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32973             // youtube support only?
32974             cfg.cn[0].cn.cn.push({
32975                 tag: 'iframe',
32976                 cls: 'masonry-brick-image-view',
32977                 src: vurl,
32978                 frameborder : 0,
32979                 allowfullscreen : true
32980             });
32981         }
32982         
32983         return cfg;
32984     },
32985     
32986     initEvents: function() 
32987     {
32988         switch (this.size) {
32989             case 'xs' :
32990                 this.x = 1;
32991                 this.y = 1;
32992                 break;
32993             case 'sm' :
32994                 this.x = 2;
32995                 this.y = 2;
32996                 break;
32997             case 'md' :
32998             case 'md-left' :
32999             case 'md-right' :
33000                 this.x = 3;
33001                 this.y = 3;
33002                 break;
33003             case 'tall' :
33004                 this.x = 2;
33005                 this.y = 3;
33006                 break;
33007             case 'wide' :
33008                 this.x = 3;
33009                 this.y = 2;
33010                 break;
33011             case 'wide-thin' :
33012                 this.x = 3;
33013                 this.y = 1;
33014                 break;
33015                         
33016             default :
33017                 break;
33018         }
33019         
33020         if(Roo.isTouch){
33021             this.el.on('touchstart', this.onTouchStart, this);
33022             this.el.on('touchmove', this.onTouchMove, this);
33023             this.el.on('touchend', this.onTouchEnd, this);
33024             this.el.on('contextmenu', this.onContextMenu, this);
33025         } else {
33026             this.el.on('mouseenter'  ,this.enter, this);
33027             this.el.on('mouseleave', this.leave, this);
33028             this.el.on('click', this.onClick, this);
33029         }
33030         
33031         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33032             this.parent().bricks.push(this);   
33033         }
33034         
33035     },
33036     
33037     onClick: function(e, el)
33038     {
33039         var time = this.endTimer - this.startTimer;
33040         // Roo.log(e.preventDefault());
33041         if(Roo.isTouch){
33042             if(time > 1000){
33043                 e.preventDefault();
33044                 return;
33045             }
33046         }
33047         
33048         if(!this.preventDefault){
33049             return;
33050         }
33051         
33052         e.preventDefault();
33053         
33054         if (this.activeClass != '') {
33055             this.selectBrick();
33056         }
33057         
33058         this.fireEvent('click', this, e);
33059     },
33060     
33061     enter: function(e, el)
33062     {
33063         e.preventDefault();
33064         
33065         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33066             return;
33067         }
33068         
33069         if(this.bgimage.length && this.html.length){
33070             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33071         }
33072     },
33073     
33074     leave: function(e, el)
33075     {
33076         e.preventDefault();
33077         
33078         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33079             return;
33080         }
33081         
33082         if(this.bgimage.length && this.html.length){
33083             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33084         }
33085     },
33086     
33087     onTouchStart: function(e, el)
33088     {
33089 //        e.preventDefault();
33090         
33091         this.touchmoved = false;
33092         
33093         if(!this.isFitContainer){
33094             return;
33095         }
33096         
33097         if(!this.bgimage.length || !this.html.length){
33098             return;
33099         }
33100         
33101         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33102         
33103         this.timer = new Date().getTime();
33104         
33105     },
33106     
33107     onTouchMove: function(e, el)
33108     {
33109         this.touchmoved = true;
33110     },
33111     
33112     onContextMenu : function(e,el)
33113     {
33114         e.preventDefault();
33115         e.stopPropagation();
33116         return false;
33117     },
33118     
33119     onTouchEnd: function(e, el)
33120     {
33121 //        e.preventDefault();
33122         
33123         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33124         
33125             this.leave(e,el);
33126             
33127             return;
33128         }
33129         
33130         if(!this.bgimage.length || !this.html.length){
33131             
33132             if(this.href.length){
33133                 window.location.href = this.href;
33134             }
33135             
33136             return;
33137         }
33138         
33139         if(!this.isFitContainer){
33140             return;
33141         }
33142         
33143         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33144         
33145         window.location.href = this.href;
33146     },
33147     
33148     //selection on single brick only
33149     selectBrick : function() {
33150         
33151         if (!this.parentId) {
33152             return;
33153         }
33154         
33155         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33156         var index = m.selectedBrick.indexOf(this.id);
33157         
33158         if ( index > -1) {
33159             m.selectedBrick.splice(index,1);
33160             this.el.removeClass(this.activeClass);
33161             return;
33162         }
33163         
33164         for(var i = 0; i < m.selectedBrick.length; i++) {
33165             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33166             b.el.removeClass(b.activeClass);
33167         }
33168         
33169         m.selectedBrick = [];
33170         
33171         m.selectedBrick.push(this.id);
33172         this.el.addClass(this.activeClass);
33173         return;
33174     },
33175     
33176     isSelected : function(){
33177         return this.el.hasClass(this.activeClass);
33178         
33179     }
33180 });
33181
33182 Roo.apply(Roo.bootstrap.MasonryBrick, {
33183     
33184     //groups: {},
33185     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33186      /**
33187     * register a Masonry Brick
33188     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33189     */
33190     
33191     register : function(brick)
33192     {
33193         //this.groups[brick.id] = brick;
33194         this.groups.add(brick.id, brick);
33195     },
33196     /**
33197     * fetch a  masonry brick based on the masonry brick ID
33198     * @param {string} the masonry brick to add
33199     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33200     */
33201     
33202     get: function(brick_id) 
33203     {
33204         // if (typeof(this.groups[brick_id]) == 'undefined') {
33205         //     return false;
33206         // }
33207         // return this.groups[brick_id] ;
33208         
33209         if(this.groups.key(brick_id)) {
33210             return this.groups.key(brick_id);
33211         }
33212         
33213         return false;
33214     }
33215     
33216     
33217     
33218 });
33219
33220  /*
33221  * - LGPL
33222  *
33223  * element
33224  * 
33225  */
33226
33227 /**
33228  * @class Roo.bootstrap.Brick
33229  * @extends Roo.bootstrap.Component
33230  * Bootstrap Brick class
33231  * 
33232  * @constructor
33233  * Create a new Brick
33234  * @param {Object} config The config object
33235  */
33236
33237 Roo.bootstrap.Brick = function(config){
33238     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33239     
33240     this.addEvents({
33241         // raw events
33242         /**
33243          * @event click
33244          * When a Brick is click
33245          * @param {Roo.bootstrap.Brick} this
33246          * @param {Roo.EventObject} e
33247          */
33248         "click" : true
33249     });
33250 };
33251
33252 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33253     
33254     /**
33255      * @cfg {String} title
33256      */   
33257     title : '',
33258     /**
33259      * @cfg {String} html
33260      */   
33261     html : '',
33262     /**
33263      * @cfg {String} bgimage
33264      */   
33265     bgimage : '',
33266     /**
33267      * @cfg {String} cls
33268      */   
33269     cls : '',
33270     /**
33271      * @cfg {String} href
33272      */   
33273     href : '',
33274     /**
33275      * @cfg {String} video
33276      */   
33277     video : '',
33278     /**
33279      * @cfg {Boolean} square
33280      */   
33281     square : true,
33282     
33283     getAutoCreate : function()
33284     {
33285         var cls = 'roo-brick';
33286         
33287         if(this.href.length){
33288             cls += ' roo-brick-link';
33289         }
33290         
33291         if(this.bgimage.length){
33292             cls += ' roo-brick-image';
33293         }
33294         
33295         if(!this.html.length && !this.bgimage.length){
33296             cls += ' roo-brick-center-title';
33297         }
33298         
33299         if(!this.html.length && this.bgimage.length){
33300             cls += ' roo-brick-bottom-title';
33301         }
33302         
33303         if(this.cls){
33304             cls += ' ' + this.cls;
33305         }
33306         
33307         var cfg = {
33308             tag: (this.href.length) ? 'a' : 'div',
33309             cls: cls,
33310             cn: [
33311                 {
33312                     tag: 'div',
33313                     cls: 'roo-brick-paragraph',
33314                     cn: []
33315                 }
33316             ]
33317         };
33318         
33319         if(this.href.length){
33320             cfg.href = this.href;
33321         }
33322         
33323         var cn = cfg.cn[0].cn;
33324         
33325         if(this.title.length){
33326             cn.push({
33327                 tag: 'h4',
33328                 cls: 'roo-brick-title',
33329                 html: this.title
33330             });
33331         }
33332         
33333         if(this.html.length){
33334             cn.push({
33335                 tag: 'p',
33336                 cls: 'roo-brick-text',
33337                 html: this.html
33338             });
33339         } else {
33340             cn.cls += ' hide';
33341         }
33342         
33343         if(this.bgimage.length){
33344             cfg.cn.push({
33345                 tag: 'img',
33346                 cls: 'roo-brick-image-view',
33347                 src: this.bgimage
33348             });
33349         }
33350         
33351         return cfg;
33352     },
33353     
33354     initEvents: function() 
33355     {
33356         if(this.title.length || this.html.length){
33357             this.el.on('mouseenter'  ,this.enter, this);
33358             this.el.on('mouseleave', this.leave, this);
33359         }
33360         
33361         Roo.EventManager.onWindowResize(this.resize, this); 
33362         
33363         if(this.bgimage.length){
33364             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33365             this.imageEl.on('load', this.onImageLoad, this);
33366             return;
33367         }
33368         
33369         this.resize();
33370     },
33371     
33372     onImageLoad : function()
33373     {
33374         this.resize();
33375     },
33376     
33377     resize : function()
33378     {
33379         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33380         
33381         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33382         
33383         if(this.bgimage.length){
33384             var image = this.el.select('.roo-brick-image-view', true).first();
33385             
33386             image.setWidth(paragraph.getWidth());
33387             
33388             if(this.square){
33389                 image.setHeight(paragraph.getWidth());
33390             }
33391             
33392             this.el.setHeight(image.getHeight());
33393             paragraph.setHeight(image.getHeight());
33394             
33395         }
33396         
33397     },
33398     
33399     enter: function(e, el)
33400     {
33401         e.preventDefault();
33402         
33403         if(this.bgimage.length){
33404             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33405             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33406         }
33407     },
33408     
33409     leave: function(e, el)
33410     {
33411         e.preventDefault();
33412         
33413         if(this.bgimage.length){
33414             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33415             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33416         }
33417     }
33418     
33419 });
33420
33421  
33422
33423  /*
33424  * - LGPL
33425  *
33426  * Number field 
33427  */
33428
33429 /**
33430  * @class Roo.bootstrap.NumberField
33431  * @extends Roo.bootstrap.Input
33432  * Bootstrap NumberField class
33433  * 
33434  * 
33435  * 
33436  * 
33437  * @constructor
33438  * Create a new NumberField
33439  * @param {Object} config The config object
33440  */
33441
33442 Roo.bootstrap.NumberField = function(config){
33443     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33444 };
33445
33446 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33447     
33448     /**
33449      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33450      */
33451     allowDecimals : true,
33452     /**
33453      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33454      */
33455     decimalSeparator : ".",
33456     /**
33457      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33458      */
33459     decimalPrecision : 2,
33460     /**
33461      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33462      */
33463     allowNegative : true,
33464     
33465     /**
33466      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33467      */
33468     allowZero: true,
33469     /**
33470      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33471      */
33472     minValue : Number.NEGATIVE_INFINITY,
33473     /**
33474      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33475      */
33476     maxValue : Number.MAX_VALUE,
33477     /**
33478      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33479      */
33480     minText : "The minimum value for this field is {0}",
33481     /**
33482      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33483      */
33484     maxText : "The maximum value for this field is {0}",
33485     /**
33486      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33487      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33488      */
33489     nanText : "{0} is not a valid number",
33490     /**
33491      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33492      */
33493     thousandsDelimiter : false,
33494     /**
33495      * @cfg {String} valueAlign alignment of value
33496      */
33497     valueAlign : "left",
33498
33499     getAutoCreate : function()
33500     {
33501         var hiddenInput = {
33502             tag: 'input',
33503             type: 'hidden',
33504             id: Roo.id(),
33505             cls: 'hidden-number-input'
33506         };
33507         
33508         if (this.name) {
33509             hiddenInput.name = this.name;
33510         }
33511         
33512         this.name = '';
33513         
33514         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33515         
33516         this.name = hiddenInput.name;
33517         
33518         if(cfg.cn.length > 0) {
33519             cfg.cn.push(hiddenInput);
33520         }
33521         
33522         return cfg;
33523     },
33524
33525     // private
33526     initEvents : function()
33527     {   
33528         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33529         
33530         var allowed = "0123456789";
33531         
33532         if(this.allowDecimals){
33533             allowed += this.decimalSeparator;
33534         }
33535         
33536         if(this.allowNegative){
33537             allowed += "-";
33538         }
33539         
33540         if(this.thousandsDelimiter) {
33541             allowed += ",";
33542         }
33543         
33544         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33545         
33546         var keyPress = function(e){
33547             
33548             var k = e.getKey();
33549             
33550             var c = e.getCharCode();
33551             
33552             if(
33553                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33554                     allowed.indexOf(String.fromCharCode(c)) === -1
33555             ){
33556                 e.stopEvent();
33557                 return;
33558             }
33559             
33560             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33561                 return;
33562             }
33563             
33564             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33565                 e.stopEvent();
33566             }
33567         };
33568         
33569         this.el.on("keypress", keyPress, this);
33570     },
33571     
33572     validateValue : function(value)
33573     {
33574         
33575         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33576             return false;
33577         }
33578         
33579         var num = this.parseValue(value);
33580         
33581         if(isNaN(num)){
33582             this.markInvalid(String.format(this.nanText, value));
33583             return false;
33584         }
33585         
33586         if(num < this.minValue){
33587             this.markInvalid(String.format(this.minText, this.minValue));
33588             return false;
33589         }
33590         
33591         if(num > this.maxValue){
33592             this.markInvalid(String.format(this.maxText, this.maxValue));
33593             return false;
33594         }
33595         
33596         return true;
33597     },
33598
33599     getValue : function()
33600     {
33601         var v = this.hiddenEl().getValue();
33602         
33603         return this.fixPrecision(this.parseValue(v));
33604     },
33605
33606     parseValue : function(value)
33607     {
33608         if(this.thousandsDelimiter) {
33609             value += "";
33610             r = new RegExp(",", "g");
33611             value = value.replace(r, "");
33612         }
33613         
33614         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33615         return isNaN(value) ? '' : value;
33616     },
33617
33618     fixPrecision : function(value)
33619     {
33620         if(this.thousandsDelimiter) {
33621             value += "";
33622             r = new RegExp(",", "g");
33623             value = value.replace(r, "");
33624         }
33625         
33626         var nan = isNaN(value);
33627         
33628         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33629             return nan ? '' : value;
33630         }
33631         return parseFloat(value).toFixed(this.decimalPrecision);
33632     },
33633
33634     setValue : function(v)
33635     {
33636         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33637         
33638         this.value = v;
33639         
33640         if(this.rendered){
33641             
33642             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33643             
33644             this.inputEl().dom.value = (v == '') ? '' :
33645                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33646             
33647             if(!this.allowZero && v === '0') {
33648                 this.hiddenEl().dom.value = '';
33649                 this.inputEl().dom.value = '';
33650             }
33651             
33652             this.validate();
33653         }
33654     },
33655
33656     decimalPrecisionFcn : function(v)
33657     {
33658         return Math.floor(v);
33659     },
33660
33661     beforeBlur : function()
33662     {
33663         var v = this.parseValue(this.getRawValue());
33664         
33665         if(v || v === 0 || v === ''){
33666             this.setValue(v);
33667         }
33668     },
33669     
33670     hiddenEl : function()
33671     {
33672         return this.el.select('input.hidden-number-input',true).first();
33673     }
33674     
33675 });
33676
33677  
33678
33679 /*
33680 * Licence: LGPL
33681 */
33682
33683 /**
33684  * @class Roo.bootstrap.DocumentSlider
33685  * @extends Roo.bootstrap.Component
33686  * Bootstrap DocumentSlider class
33687  * 
33688  * @constructor
33689  * Create a new DocumentViewer
33690  * @param {Object} config The config object
33691  */
33692
33693 Roo.bootstrap.DocumentSlider = function(config){
33694     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33695     
33696     this.files = [];
33697     
33698     this.addEvents({
33699         /**
33700          * @event initial
33701          * Fire after initEvent
33702          * @param {Roo.bootstrap.DocumentSlider} this
33703          */
33704         "initial" : true,
33705         /**
33706          * @event update
33707          * Fire after update
33708          * @param {Roo.bootstrap.DocumentSlider} this
33709          */
33710         "update" : true,
33711         /**
33712          * @event click
33713          * Fire after click
33714          * @param {Roo.bootstrap.DocumentSlider} this
33715          */
33716         "click" : true
33717     });
33718 };
33719
33720 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33721     
33722     files : false,
33723     
33724     indicator : 0,
33725     
33726     getAutoCreate : function()
33727     {
33728         var cfg = {
33729             tag : 'div',
33730             cls : 'roo-document-slider',
33731             cn : [
33732                 {
33733                     tag : 'div',
33734                     cls : 'roo-document-slider-header',
33735                     cn : [
33736                         {
33737                             tag : 'div',
33738                             cls : 'roo-document-slider-header-title'
33739                         }
33740                     ]
33741                 },
33742                 {
33743                     tag : 'div',
33744                     cls : 'roo-document-slider-body',
33745                     cn : [
33746                         {
33747                             tag : 'div',
33748                             cls : 'roo-document-slider-prev',
33749                             cn : [
33750                                 {
33751                                     tag : 'i',
33752                                     cls : 'fa fa-chevron-left'
33753                                 }
33754                             ]
33755                         },
33756                         {
33757                             tag : 'div',
33758                             cls : 'roo-document-slider-thumb',
33759                             cn : [
33760                                 {
33761                                     tag : 'img',
33762                                     cls : 'roo-document-slider-image'
33763                                 }
33764                             ]
33765                         },
33766                         {
33767                             tag : 'div',
33768                             cls : 'roo-document-slider-next',
33769                             cn : [
33770                                 {
33771                                     tag : 'i',
33772                                     cls : 'fa fa-chevron-right'
33773                                 }
33774                             ]
33775                         }
33776                     ]
33777                 }
33778             ]
33779         };
33780         
33781         return cfg;
33782     },
33783     
33784     initEvents : function()
33785     {
33786         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33787         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33788         
33789         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33790         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33791         
33792         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33793         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33794         
33795         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33796         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33797         
33798         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33799         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33800         
33801         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33802         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33803         
33804         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33805         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33806         
33807         this.thumbEl.on('click', this.onClick, this);
33808         
33809         this.prevIndicator.on('click', this.prev, this);
33810         
33811         this.nextIndicator.on('click', this.next, this);
33812         
33813     },
33814     
33815     initial : function()
33816     {
33817         if(this.files.length){
33818             this.indicator = 1;
33819             this.update()
33820         }
33821         
33822         this.fireEvent('initial', this);
33823     },
33824     
33825     update : function()
33826     {
33827         this.imageEl.attr('src', this.files[this.indicator - 1]);
33828         
33829         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33830         
33831         this.prevIndicator.show();
33832         
33833         if(this.indicator == 1){
33834             this.prevIndicator.hide();
33835         }
33836         
33837         this.nextIndicator.show();
33838         
33839         if(this.indicator == this.files.length){
33840             this.nextIndicator.hide();
33841         }
33842         
33843         this.thumbEl.scrollTo('top');
33844         
33845         this.fireEvent('update', this);
33846     },
33847     
33848     onClick : function(e)
33849     {
33850         e.preventDefault();
33851         
33852         this.fireEvent('click', this);
33853     },
33854     
33855     prev : function(e)
33856     {
33857         e.preventDefault();
33858         
33859         this.indicator = Math.max(1, this.indicator - 1);
33860         
33861         this.update();
33862     },
33863     
33864     next : function(e)
33865     {
33866         e.preventDefault();
33867         
33868         this.indicator = Math.min(this.files.length, this.indicator + 1);
33869         
33870         this.update();
33871     }
33872 });
33873 /*
33874  * - LGPL
33875  *
33876  * RadioSet
33877  *
33878  *
33879  */
33880
33881 /**
33882  * @class Roo.bootstrap.RadioSet
33883  * @extends Roo.bootstrap.Input
33884  * Bootstrap RadioSet class
33885  * @cfg {String} indicatorpos (left|right) default left
33886  * @cfg {Boolean} inline (true|false) inline the element (default true)
33887  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33888  * @constructor
33889  * Create a new RadioSet
33890  * @param {Object} config The config object
33891  */
33892
33893 Roo.bootstrap.RadioSet = function(config){
33894     
33895     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33896     
33897     this.radioes = [];
33898     
33899     Roo.bootstrap.RadioSet.register(this);
33900     
33901     this.addEvents({
33902         /**
33903         * @event check
33904         * Fires when the element is checked or unchecked.
33905         * @param {Roo.bootstrap.RadioSet} this This radio
33906         * @param {Roo.bootstrap.Radio} item The checked item
33907         */
33908        check : true,
33909        /**
33910         * @event click
33911         * Fires when the element is click.
33912         * @param {Roo.bootstrap.RadioSet} this This radio set
33913         * @param {Roo.bootstrap.Radio} item The checked item
33914         * @param {Roo.EventObject} e The event object
33915         */
33916        click : true
33917     });
33918     
33919 };
33920
33921 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33922
33923     radioes : false,
33924     
33925     inline : true,
33926     
33927     weight : '',
33928     
33929     indicatorpos : 'left',
33930     
33931     getAutoCreate : function()
33932     {
33933         var label = {
33934             tag : 'label',
33935             cls : 'roo-radio-set-label',
33936             cn : [
33937                 {
33938                     tag : 'span',
33939                     html : this.fieldLabel
33940                 }
33941             ]
33942         };
33943         
33944         if(this.indicatorpos == 'left'){
33945             label.cn.unshift({
33946                 tag : 'i',
33947                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33948                 tooltip : 'This field is required'
33949             });
33950         } else {
33951             label.cn.push({
33952                 tag : 'i',
33953                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33954                 tooltip : 'This field is required'
33955             });
33956         }
33957         
33958         var items = {
33959             tag : 'div',
33960             cls : 'roo-radio-set-items'
33961         };
33962         
33963         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33964         
33965         if (align === 'left' && this.fieldLabel.length) {
33966             
33967             items = {
33968                 cls : "roo-radio-set-right", 
33969                 cn: [
33970                     items
33971                 ]
33972             };
33973             
33974             if(this.labelWidth > 12){
33975                 label.style = "width: " + this.labelWidth + 'px';
33976             }
33977             
33978             if(this.labelWidth < 13 && this.labelmd == 0){
33979                 this.labelmd = this.labelWidth;
33980             }
33981             
33982             if(this.labellg > 0){
33983                 label.cls += ' col-lg-' + this.labellg;
33984                 items.cls += ' col-lg-' + (12 - this.labellg);
33985             }
33986             
33987             if(this.labelmd > 0){
33988                 label.cls += ' col-md-' + this.labelmd;
33989                 items.cls += ' col-md-' + (12 - this.labelmd);
33990             }
33991             
33992             if(this.labelsm > 0){
33993                 label.cls += ' col-sm-' + this.labelsm;
33994                 items.cls += ' col-sm-' + (12 - this.labelsm);
33995             }
33996             
33997             if(this.labelxs > 0){
33998                 label.cls += ' col-xs-' + this.labelxs;
33999                 items.cls += ' col-xs-' + (12 - this.labelxs);
34000             }
34001         }
34002         
34003         var cfg = {
34004             tag : 'div',
34005             cls : 'roo-radio-set',
34006             cn : [
34007                 {
34008                     tag : 'input',
34009                     cls : 'roo-radio-set-input',
34010                     type : 'hidden',
34011                     name : this.name,
34012                     value : this.value ? this.value :  ''
34013                 },
34014                 label,
34015                 items
34016             ]
34017         };
34018         
34019         if(this.weight.length){
34020             cfg.cls += ' roo-radio-' + this.weight;
34021         }
34022         
34023         if(this.inline) {
34024             cfg.cls += ' roo-radio-set-inline';
34025         }
34026         
34027         var settings=this;
34028         ['xs','sm','md','lg'].map(function(size){
34029             if (settings[size]) {
34030                 cfg.cls += ' col-' + size + '-' + settings[size];
34031             }
34032         });
34033         
34034         return cfg;
34035         
34036     },
34037
34038     initEvents : function()
34039     {
34040         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34041         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34042         
34043         if(!this.fieldLabel.length){
34044             this.labelEl.hide();
34045         }
34046         
34047         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34048         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34049         
34050         this.indicator = this.indicatorEl();
34051         
34052         if(this.indicator){
34053             this.indicator.addClass('invisible');
34054         }
34055         
34056         this.originalValue = this.getValue();
34057         
34058     },
34059     
34060     inputEl: function ()
34061     {
34062         return this.el.select('.roo-radio-set-input', true).first();
34063     },
34064     
34065     getChildContainer : function()
34066     {
34067         return this.itemsEl;
34068     },
34069     
34070     register : function(item)
34071     {
34072         this.radioes.push(item);
34073         
34074     },
34075     
34076     validate : function()
34077     {   
34078         if(this.getVisibilityEl().hasClass('hidden')){
34079             return true;
34080         }
34081         
34082         var valid = false;
34083         
34084         Roo.each(this.radioes, function(i){
34085             if(!i.checked){
34086                 return;
34087             }
34088             
34089             valid = true;
34090             return false;
34091         });
34092         
34093         if(this.allowBlank) {
34094             return true;
34095         }
34096         
34097         if(this.disabled || valid){
34098             this.markValid();
34099             return true;
34100         }
34101         
34102         this.markInvalid();
34103         return false;
34104         
34105     },
34106     
34107     markValid : function()
34108     {
34109         if(this.labelEl.isVisible(true)){
34110             this.indicatorEl().removeClass('visible');
34111             this.indicatorEl().addClass('invisible');
34112         }
34113         
34114         this.el.removeClass([this.invalidClass, this.validClass]);
34115         this.el.addClass(this.validClass);
34116         
34117         this.fireEvent('valid', this);
34118     },
34119     
34120     markInvalid : function(msg)
34121     {
34122         if(this.allowBlank || this.disabled){
34123             return;
34124         }
34125         
34126         if(this.labelEl.isVisible(true)){
34127             this.indicatorEl().removeClass('invisible');
34128             this.indicatorEl().addClass('visible');
34129         }
34130         
34131         this.el.removeClass([this.invalidClass, this.validClass]);
34132         this.el.addClass(this.invalidClass);
34133         
34134         this.fireEvent('invalid', this, msg);
34135         
34136     },
34137     
34138     setValue : function(v, suppressEvent)
34139     {   
34140         if(this.value === v){
34141             return;
34142         }
34143         
34144         this.value = v;
34145         
34146         if(this.rendered){
34147             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34148         }
34149         
34150         Roo.each(this.radioes, function(i){
34151             i.checked = false;
34152             i.el.removeClass('checked');
34153         });
34154         
34155         Roo.each(this.radioes, function(i){
34156             
34157             if(i.value === v || i.value.toString() === v.toString()){
34158                 i.checked = true;
34159                 i.el.addClass('checked');
34160                 
34161                 if(suppressEvent !== true){
34162                     this.fireEvent('check', this, i);
34163                 }
34164                 
34165                 return false;
34166             }
34167             
34168         }, this);
34169         
34170         this.validate();
34171     },
34172     
34173     clearInvalid : function(){
34174         
34175         if(!this.el || this.preventMark){
34176             return;
34177         }
34178         
34179         this.el.removeClass([this.invalidClass]);
34180         
34181         this.fireEvent('valid', this);
34182     }
34183     
34184 });
34185
34186 Roo.apply(Roo.bootstrap.RadioSet, {
34187     
34188     groups: {},
34189     
34190     register : function(set)
34191     {
34192         this.groups[set.name] = set;
34193     },
34194     
34195     get: function(name) 
34196     {
34197         if (typeof(this.groups[name]) == 'undefined') {
34198             return false;
34199         }
34200         
34201         return this.groups[name] ;
34202     }
34203     
34204 });
34205 /*
34206  * Based on:
34207  * Ext JS Library 1.1.1
34208  * Copyright(c) 2006-2007, Ext JS, LLC.
34209  *
34210  * Originally Released Under LGPL - original licence link has changed is not relivant.
34211  *
34212  * Fork - LGPL
34213  * <script type="text/javascript">
34214  */
34215
34216
34217 /**
34218  * @class Roo.bootstrap.SplitBar
34219  * @extends Roo.util.Observable
34220  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34221  * <br><br>
34222  * Usage:
34223  * <pre><code>
34224 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34225                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34226 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34227 split.minSize = 100;
34228 split.maxSize = 600;
34229 split.animate = true;
34230 split.on('moved', splitterMoved);
34231 </code></pre>
34232  * @constructor
34233  * Create a new SplitBar
34234  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34235  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34236  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34237  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34238                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34239                         position of the SplitBar).
34240  */
34241 Roo.bootstrap.SplitBar = function(cfg){
34242     
34243     /** @private */
34244     
34245     //{
34246     //  dragElement : elm
34247     //  resizingElement: el,
34248         // optional..
34249     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34250     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34251         // existingProxy ???
34252     //}
34253     
34254     this.el = Roo.get(cfg.dragElement, true);
34255     this.el.dom.unselectable = "on";
34256     /** @private */
34257     this.resizingEl = Roo.get(cfg.resizingElement, true);
34258
34259     /**
34260      * @private
34261      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34262      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34263      * @type Number
34264      */
34265     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34266     
34267     /**
34268      * The minimum size of the resizing element. (Defaults to 0)
34269      * @type Number
34270      */
34271     this.minSize = 0;
34272     
34273     /**
34274      * The maximum size of the resizing element. (Defaults to 2000)
34275      * @type Number
34276      */
34277     this.maxSize = 2000;
34278     
34279     /**
34280      * Whether to animate the transition to the new size
34281      * @type Boolean
34282      */
34283     this.animate = false;
34284     
34285     /**
34286      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34287      * @type Boolean
34288      */
34289     this.useShim = false;
34290     
34291     /** @private */
34292     this.shim = null;
34293     
34294     if(!cfg.existingProxy){
34295         /** @private */
34296         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34297     }else{
34298         this.proxy = Roo.get(cfg.existingProxy).dom;
34299     }
34300     /** @private */
34301     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34302     
34303     /** @private */
34304     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34305     
34306     /** @private */
34307     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34308     
34309     /** @private */
34310     this.dragSpecs = {};
34311     
34312     /**
34313      * @private The adapter to use to positon and resize elements
34314      */
34315     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34316     this.adapter.init(this);
34317     
34318     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34319         /** @private */
34320         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34321         this.el.addClass("roo-splitbar-h");
34322     }else{
34323         /** @private */
34324         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34325         this.el.addClass("roo-splitbar-v");
34326     }
34327     
34328     this.addEvents({
34329         /**
34330          * @event resize
34331          * Fires when the splitter is moved (alias for {@link #event-moved})
34332          * @param {Roo.bootstrap.SplitBar} this
34333          * @param {Number} newSize the new width or height
34334          */
34335         "resize" : true,
34336         /**
34337          * @event moved
34338          * Fires when the splitter is moved
34339          * @param {Roo.bootstrap.SplitBar} this
34340          * @param {Number} newSize the new width or height
34341          */
34342         "moved" : true,
34343         /**
34344          * @event beforeresize
34345          * Fires before the splitter is dragged
34346          * @param {Roo.bootstrap.SplitBar} this
34347          */
34348         "beforeresize" : true,
34349
34350         "beforeapply" : true
34351     });
34352
34353     Roo.util.Observable.call(this);
34354 };
34355
34356 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34357     onStartProxyDrag : function(x, y){
34358         this.fireEvent("beforeresize", this);
34359         if(!this.overlay){
34360             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34361             o.unselectable();
34362             o.enableDisplayMode("block");
34363             // all splitbars share the same overlay
34364             Roo.bootstrap.SplitBar.prototype.overlay = o;
34365         }
34366         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34367         this.overlay.show();
34368         Roo.get(this.proxy).setDisplayed("block");
34369         var size = this.adapter.getElementSize(this);
34370         this.activeMinSize = this.getMinimumSize();;
34371         this.activeMaxSize = this.getMaximumSize();;
34372         var c1 = size - this.activeMinSize;
34373         var c2 = Math.max(this.activeMaxSize - size, 0);
34374         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34375             this.dd.resetConstraints();
34376             this.dd.setXConstraint(
34377                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34378                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34379             );
34380             this.dd.setYConstraint(0, 0);
34381         }else{
34382             this.dd.resetConstraints();
34383             this.dd.setXConstraint(0, 0);
34384             this.dd.setYConstraint(
34385                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34386                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34387             );
34388          }
34389         this.dragSpecs.startSize = size;
34390         this.dragSpecs.startPoint = [x, y];
34391         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34392     },
34393     
34394     /** 
34395      * @private Called after the drag operation by the DDProxy
34396      */
34397     onEndProxyDrag : function(e){
34398         Roo.get(this.proxy).setDisplayed(false);
34399         var endPoint = Roo.lib.Event.getXY(e);
34400         if(this.overlay){
34401             this.overlay.hide();
34402         }
34403         var newSize;
34404         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34405             newSize = this.dragSpecs.startSize + 
34406                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34407                     endPoint[0] - this.dragSpecs.startPoint[0] :
34408                     this.dragSpecs.startPoint[0] - endPoint[0]
34409                 );
34410         }else{
34411             newSize = this.dragSpecs.startSize + 
34412                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34413                     endPoint[1] - this.dragSpecs.startPoint[1] :
34414                     this.dragSpecs.startPoint[1] - endPoint[1]
34415                 );
34416         }
34417         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34418         if(newSize != this.dragSpecs.startSize){
34419             if(this.fireEvent('beforeapply', this, newSize) !== false){
34420                 this.adapter.setElementSize(this, newSize);
34421                 this.fireEvent("moved", this, newSize);
34422                 this.fireEvent("resize", this, newSize);
34423             }
34424         }
34425     },
34426     
34427     /**
34428      * Get the adapter this SplitBar uses
34429      * @return The adapter object
34430      */
34431     getAdapter : function(){
34432         return this.adapter;
34433     },
34434     
34435     /**
34436      * Set the adapter this SplitBar uses
34437      * @param {Object} adapter A SplitBar adapter object
34438      */
34439     setAdapter : function(adapter){
34440         this.adapter = adapter;
34441         this.adapter.init(this);
34442     },
34443     
34444     /**
34445      * Gets the minimum size for the resizing element
34446      * @return {Number} The minimum size
34447      */
34448     getMinimumSize : function(){
34449         return this.minSize;
34450     },
34451     
34452     /**
34453      * Sets the minimum size for the resizing element
34454      * @param {Number} minSize The minimum size
34455      */
34456     setMinimumSize : function(minSize){
34457         this.minSize = minSize;
34458     },
34459     
34460     /**
34461      * Gets the maximum size for the resizing element
34462      * @return {Number} The maximum size
34463      */
34464     getMaximumSize : function(){
34465         return this.maxSize;
34466     },
34467     
34468     /**
34469      * Sets the maximum size for the resizing element
34470      * @param {Number} maxSize The maximum size
34471      */
34472     setMaximumSize : function(maxSize){
34473         this.maxSize = maxSize;
34474     },
34475     
34476     /**
34477      * Sets the initialize size for the resizing element
34478      * @param {Number} size The initial size
34479      */
34480     setCurrentSize : function(size){
34481         var oldAnimate = this.animate;
34482         this.animate = false;
34483         this.adapter.setElementSize(this, size);
34484         this.animate = oldAnimate;
34485     },
34486     
34487     /**
34488      * Destroy this splitbar. 
34489      * @param {Boolean} removeEl True to remove the element
34490      */
34491     destroy : function(removeEl){
34492         if(this.shim){
34493             this.shim.remove();
34494         }
34495         this.dd.unreg();
34496         this.proxy.parentNode.removeChild(this.proxy);
34497         if(removeEl){
34498             this.el.remove();
34499         }
34500     }
34501 });
34502
34503 /**
34504  * @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.
34505  */
34506 Roo.bootstrap.SplitBar.createProxy = function(dir){
34507     var proxy = new Roo.Element(document.createElement("div"));
34508     proxy.unselectable();
34509     var cls = 'roo-splitbar-proxy';
34510     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34511     document.body.appendChild(proxy.dom);
34512     return proxy.dom;
34513 };
34514
34515 /** 
34516  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34517  * Default Adapter. It assumes the splitter and resizing element are not positioned
34518  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34519  */
34520 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34521 };
34522
34523 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34524     // do nothing for now
34525     init : function(s){
34526     
34527     },
34528     /**
34529      * Called before drag operations to get the current size of the resizing element. 
34530      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34531      */
34532      getElementSize : function(s){
34533         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34534             return s.resizingEl.getWidth();
34535         }else{
34536             return s.resizingEl.getHeight();
34537         }
34538     },
34539     
34540     /**
34541      * Called after drag operations to set the size of the resizing element.
34542      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34543      * @param {Number} newSize The new size to set
34544      * @param {Function} onComplete A function to be invoked when resizing is complete
34545      */
34546     setElementSize : function(s, newSize, onComplete){
34547         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34548             if(!s.animate){
34549                 s.resizingEl.setWidth(newSize);
34550                 if(onComplete){
34551                     onComplete(s, newSize);
34552                 }
34553             }else{
34554                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34555             }
34556         }else{
34557             
34558             if(!s.animate){
34559                 s.resizingEl.setHeight(newSize);
34560                 if(onComplete){
34561                     onComplete(s, newSize);
34562                 }
34563             }else{
34564                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34565             }
34566         }
34567     }
34568 };
34569
34570 /** 
34571  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34572  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34573  * Adapter that  moves the splitter element to align with the resized sizing element. 
34574  * Used with an absolute positioned SplitBar.
34575  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34576  * document.body, make sure you assign an id to the body element.
34577  */
34578 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34579     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34580     this.container = Roo.get(container);
34581 };
34582
34583 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34584     init : function(s){
34585         this.basic.init(s);
34586     },
34587     
34588     getElementSize : function(s){
34589         return this.basic.getElementSize(s);
34590     },
34591     
34592     setElementSize : function(s, newSize, onComplete){
34593         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34594     },
34595     
34596     moveSplitter : function(s){
34597         var yes = Roo.bootstrap.SplitBar;
34598         switch(s.placement){
34599             case yes.LEFT:
34600                 s.el.setX(s.resizingEl.getRight());
34601                 break;
34602             case yes.RIGHT:
34603                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34604                 break;
34605             case yes.TOP:
34606                 s.el.setY(s.resizingEl.getBottom());
34607                 break;
34608             case yes.BOTTOM:
34609                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34610                 break;
34611         }
34612     }
34613 };
34614
34615 /**
34616  * Orientation constant - Create a vertical SplitBar
34617  * @static
34618  * @type Number
34619  */
34620 Roo.bootstrap.SplitBar.VERTICAL = 1;
34621
34622 /**
34623  * Orientation constant - Create a horizontal SplitBar
34624  * @static
34625  * @type Number
34626  */
34627 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34628
34629 /**
34630  * Placement constant - The resizing element is to the left of the splitter element
34631  * @static
34632  * @type Number
34633  */
34634 Roo.bootstrap.SplitBar.LEFT = 1;
34635
34636 /**
34637  * Placement constant - The resizing element is to the right of the splitter element
34638  * @static
34639  * @type Number
34640  */
34641 Roo.bootstrap.SplitBar.RIGHT = 2;
34642
34643 /**
34644  * Placement constant - The resizing element is positioned above the splitter element
34645  * @static
34646  * @type Number
34647  */
34648 Roo.bootstrap.SplitBar.TOP = 3;
34649
34650 /**
34651  * Placement constant - The resizing element is positioned under splitter element
34652  * @static
34653  * @type Number
34654  */
34655 Roo.bootstrap.SplitBar.BOTTOM = 4;
34656 Roo.namespace("Roo.bootstrap.layout");/*
34657  * Based on:
34658  * Ext JS Library 1.1.1
34659  * Copyright(c) 2006-2007, Ext JS, LLC.
34660  *
34661  * Originally Released Under LGPL - original licence link has changed is not relivant.
34662  *
34663  * Fork - LGPL
34664  * <script type="text/javascript">
34665  */
34666
34667 /**
34668  * @class Roo.bootstrap.layout.Manager
34669  * @extends Roo.bootstrap.Component
34670  * Base class for layout managers.
34671  */
34672 Roo.bootstrap.layout.Manager = function(config)
34673 {
34674     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34675
34676
34677
34678
34679
34680     /** false to disable window resize monitoring @type Boolean */
34681     this.monitorWindowResize = true;
34682     this.regions = {};
34683     this.addEvents({
34684         /**
34685          * @event layout
34686          * Fires when a layout is performed.
34687          * @param {Roo.LayoutManager} this
34688          */
34689         "layout" : true,
34690         /**
34691          * @event regionresized
34692          * Fires when the user resizes a region.
34693          * @param {Roo.LayoutRegion} region The resized region
34694          * @param {Number} newSize The new size (width for east/west, height for north/south)
34695          */
34696         "regionresized" : true,
34697         /**
34698          * @event regioncollapsed
34699          * Fires when a region is collapsed.
34700          * @param {Roo.LayoutRegion} region The collapsed region
34701          */
34702         "regioncollapsed" : true,
34703         /**
34704          * @event regionexpanded
34705          * Fires when a region is expanded.
34706          * @param {Roo.LayoutRegion} region The expanded region
34707          */
34708         "regionexpanded" : true
34709     });
34710     this.updating = false;
34711
34712     if (config.el) {
34713         this.el = Roo.get(config.el);
34714         this.initEvents();
34715     }
34716
34717 };
34718
34719 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34720
34721
34722     regions : null,
34723
34724     monitorWindowResize : true,
34725
34726
34727     updating : false,
34728
34729
34730     onRender : function(ct, position)
34731     {
34732         if(!this.el){
34733             this.el = Roo.get(ct);
34734             this.initEvents();
34735         }
34736         //this.fireEvent('render',this);
34737     },
34738
34739
34740     initEvents: function()
34741     {
34742
34743
34744         // ie scrollbar fix
34745         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34746             document.body.scroll = "no";
34747         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34748             this.el.position('relative');
34749         }
34750         this.id = this.el.id;
34751         this.el.addClass("roo-layout-container");
34752         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34753         if(this.el.dom != document.body ) {
34754             this.el.on('resize', this.layout,this);
34755             this.el.on('show', this.layout,this);
34756         }
34757
34758     },
34759
34760     /**
34761      * Returns true if this layout is currently being updated
34762      * @return {Boolean}
34763      */
34764     isUpdating : function(){
34765         return this.updating;
34766     },
34767
34768     /**
34769      * Suspend the LayoutManager from doing auto-layouts while
34770      * making multiple add or remove calls
34771      */
34772     beginUpdate : function(){
34773         this.updating = true;
34774     },
34775
34776     /**
34777      * Restore auto-layouts and optionally disable the manager from performing a layout
34778      * @param {Boolean} noLayout true to disable a layout update
34779      */
34780     endUpdate : function(noLayout){
34781         this.updating = false;
34782         if(!noLayout){
34783             this.layout();
34784         }
34785     },
34786
34787     layout: function(){
34788         // abstract...
34789     },
34790
34791     onRegionResized : function(region, newSize){
34792         this.fireEvent("regionresized", region, newSize);
34793         this.layout();
34794     },
34795
34796     onRegionCollapsed : function(region){
34797         this.fireEvent("regioncollapsed", region);
34798     },
34799
34800     onRegionExpanded : function(region){
34801         this.fireEvent("regionexpanded", region);
34802     },
34803
34804     /**
34805      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34806      * performs box-model adjustments.
34807      * @return {Object} The size as an object {width: (the width), height: (the height)}
34808      */
34809     getViewSize : function()
34810     {
34811         var size;
34812         if(this.el.dom != document.body){
34813             size = this.el.getSize();
34814         }else{
34815             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34816         }
34817         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34818         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34819         return size;
34820     },
34821
34822     /**
34823      * Returns the Element this layout is bound to.
34824      * @return {Roo.Element}
34825      */
34826     getEl : function(){
34827         return this.el;
34828     },
34829
34830     /**
34831      * Returns the specified region.
34832      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34833      * @return {Roo.LayoutRegion}
34834      */
34835     getRegion : function(target){
34836         return this.regions[target.toLowerCase()];
34837     },
34838
34839     onWindowResize : function(){
34840         if(this.monitorWindowResize){
34841             this.layout();
34842         }
34843     }
34844 });
34845 /*
34846  * Based on:
34847  * Ext JS Library 1.1.1
34848  * Copyright(c) 2006-2007, Ext JS, LLC.
34849  *
34850  * Originally Released Under LGPL - original licence link has changed is not relivant.
34851  *
34852  * Fork - LGPL
34853  * <script type="text/javascript">
34854  */
34855 /**
34856  * @class Roo.bootstrap.layout.Border
34857  * @extends Roo.bootstrap.layout.Manager
34858  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34859  * please see: examples/bootstrap/nested.html<br><br>
34860  
34861 <b>The container the layout is rendered into can be either the body element or any other element.
34862 If it is not the body element, the container needs to either be an absolute positioned element,
34863 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34864 the container size if it is not the body element.</b>
34865
34866 * @constructor
34867 * Create a new Border
34868 * @param {Object} config Configuration options
34869  */
34870 Roo.bootstrap.layout.Border = function(config){
34871     config = config || {};
34872     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34873     
34874     
34875     
34876     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34877         if(config[region]){
34878             config[region].region = region;
34879             this.addRegion(config[region]);
34880         }
34881     },this);
34882     
34883 };
34884
34885 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34886
34887 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34888     /**
34889      * Creates and adds a new region if it doesn't already exist.
34890      * @param {String} target The target region key (north, south, east, west or center).
34891      * @param {Object} config The regions config object
34892      * @return {BorderLayoutRegion} The new region
34893      */
34894     addRegion : function(config)
34895     {
34896         if(!this.regions[config.region]){
34897             var r = this.factory(config);
34898             this.bindRegion(r);
34899         }
34900         return this.regions[config.region];
34901     },
34902
34903     // private (kinda)
34904     bindRegion : function(r){
34905         this.regions[r.config.region] = r;
34906         
34907         r.on("visibilitychange",    this.layout, this);
34908         r.on("paneladded",          this.layout, this);
34909         r.on("panelremoved",        this.layout, this);
34910         r.on("invalidated",         this.layout, this);
34911         r.on("resized",             this.onRegionResized, this);
34912         r.on("collapsed",           this.onRegionCollapsed, this);
34913         r.on("expanded",            this.onRegionExpanded, this);
34914     },
34915
34916     /**
34917      * Performs a layout update.
34918      */
34919     layout : function()
34920     {
34921         if(this.updating) {
34922             return;
34923         }
34924         
34925         // render all the rebions if they have not been done alreayd?
34926         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34927             if(this.regions[region] && !this.regions[region].bodyEl){
34928                 this.regions[region].onRender(this.el)
34929             }
34930         },this);
34931         
34932         var size = this.getViewSize();
34933         var w = size.width;
34934         var h = size.height;
34935         var centerW = w;
34936         var centerH = h;
34937         var centerY = 0;
34938         var centerX = 0;
34939         //var x = 0, y = 0;
34940
34941         var rs = this.regions;
34942         var north = rs["north"];
34943         var south = rs["south"]; 
34944         var west = rs["west"];
34945         var east = rs["east"];
34946         var center = rs["center"];
34947         //if(this.hideOnLayout){ // not supported anymore
34948             //c.el.setStyle("display", "none");
34949         //}
34950         if(north && north.isVisible()){
34951             var b = north.getBox();
34952             var m = north.getMargins();
34953             b.width = w - (m.left+m.right);
34954             b.x = m.left;
34955             b.y = m.top;
34956             centerY = b.height + b.y + m.bottom;
34957             centerH -= centerY;
34958             north.updateBox(this.safeBox(b));
34959         }
34960         if(south && south.isVisible()){
34961             var b = south.getBox();
34962             var m = south.getMargins();
34963             b.width = w - (m.left+m.right);
34964             b.x = m.left;
34965             var totalHeight = (b.height + m.top + m.bottom);
34966             b.y = h - totalHeight + m.top;
34967             centerH -= totalHeight;
34968             south.updateBox(this.safeBox(b));
34969         }
34970         if(west && west.isVisible()){
34971             var b = west.getBox();
34972             var m = west.getMargins();
34973             b.height = centerH - (m.top+m.bottom);
34974             b.x = m.left;
34975             b.y = centerY + m.top;
34976             var totalWidth = (b.width + m.left + m.right);
34977             centerX += totalWidth;
34978             centerW -= totalWidth;
34979             west.updateBox(this.safeBox(b));
34980         }
34981         if(east && east.isVisible()){
34982             var b = east.getBox();
34983             var m = east.getMargins();
34984             b.height = centerH - (m.top+m.bottom);
34985             var totalWidth = (b.width + m.left + m.right);
34986             b.x = w - totalWidth + m.left;
34987             b.y = centerY + m.top;
34988             centerW -= totalWidth;
34989             east.updateBox(this.safeBox(b));
34990         }
34991         if(center){
34992             var m = center.getMargins();
34993             var centerBox = {
34994                 x: centerX + m.left,
34995                 y: centerY + m.top,
34996                 width: centerW - (m.left+m.right),
34997                 height: centerH - (m.top+m.bottom)
34998             };
34999             //if(this.hideOnLayout){
35000                 //center.el.setStyle("display", "block");
35001             //}
35002             center.updateBox(this.safeBox(centerBox));
35003         }
35004         this.el.repaint();
35005         this.fireEvent("layout", this);
35006     },
35007
35008     // private
35009     safeBox : function(box){
35010         box.width = Math.max(0, box.width);
35011         box.height = Math.max(0, box.height);
35012         return box;
35013     },
35014
35015     /**
35016      * Adds a ContentPanel (or subclass) to this layout.
35017      * @param {String} target The target region key (north, south, east, west or center).
35018      * @param {Roo.ContentPanel} panel The panel to add
35019      * @return {Roo.ContentPanel} The added panel
35020      */
35021     add : function(target, panel){
35022          
35023         target = target.toLowerCase();
35024         return this.regions[target].add(panel);
35025     },
35026
35027     /**
35028      * Remove a ContentPanel (or subclass) to this layout.
35029      * @param {String} target The target region key (north, south, east, west or center).
35030      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35031      * @return {Roo.ContentPanel} The removed panel
35032      */
35033     remove : function(target, panel){
35034         target = target.toLowerCase();
35035         return this.regions[target].remove(panel);
35036     },
35037
35038     /**
35039      * Searches all regions for a panel with the specified id
35040      * @param {String} panelId
35041      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35042      */
35043     findPanel : function(panelId){
35044         var rs = this.regions;
35045         for(var target in rs){
35046             if(typeof rs[target] != "function"){
35047                 var p = rs[target].getPanel(panelId);
35048                 if(p){
35049                     return p;
35050                 }
35051             }
35052         }
35053         return null;
35054     },
35055
35056     /**
35057      * Searches all regions for a panel with the specified id and activates (shows) it.
35058      * @param {String/ContentPanel} panelId The panels id or the panel itself
35059      * @return {Roo.ContentPanel} The shown panel or null
35060      */
35061     showPanel : function(panelId) {
35062       var rs = this.regions;
35063       for(var target in rs){
35064          var r = rs[target];
35065          if(typeof r != "function"){
35066             if(r.hasPanel(panelId)){
35067                return r.showPanel(panelId);
35068             }
35069          }
35070       }
35071       return null;
35072    },
35073
35074    /**
35075      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35076      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35077      */
35078    /*
35079     restoreState : function(provider){
35080         if(!provider){
35081             provider = Roo.state.Manager;
35082         }
35083         var sm = new Roo.LayoutStateManager();
35084         sm.init(this, provider);
35085     },
35086 */
35087  
35088  
35089     /**
35090      * Adds a xtype elements to the layout.
35091      * <pre><code>
35092
35093 layout.addxtype({
35094        xtype : 'ContentPanel',
35095        region: 'west',
35096        items: [ .... ]
35097    }
35098 );
35099
35100 layout.addxtype({
35101         xtype : 'NestedLayoutPanel',
35102         region: 'west',
35103         layout: {
35104            center: { },
35105            west: { }   
35106         },
35107         items : [ ... list of content panels or nested layout panels.. ]
35108    }
35109 );
35110 </code></pre>
35111      * @param {Object} cfg Xtype definition of item to add.
35112      */
35113     addxtype : function(cfg)
35114     {
35115         // basically accepts a pannel...
35116         // can accept a layout region..!?!?
35117         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35118         
35119         
35120         // theory?  children can only be panels??
35121         
35122         //if (!cfg.xtype.match(/Panel$/)) {
35123         //    return false;
35124         //}
35125         var ret = false;
35126         
35127         if (typeof(cfg.region) == 'undefined') {
35128             Roo.log("Failed to add Panel, region was not set");
35129             Roo.log(cfg);
35130             return false;
35131         }
35132         var region = cfg.region;
35133         delete cfg.region;
35134         
35135           
35136         var xitems = [];
35137         if (cfg.items) {
35138             xitems = cfg.items;
35139             delete cfg.items;
35140         }
35141         var nb = false;
35142         
35143         switch(cfg.xtype) 
35144         {
35145             case 'Content':  // ContentPanel (el, cfg)
35146             case 'Scroll':  // ContentPanel (el, cfg)
35147             case 'View': 
35148                 cfg.autoCreate = true;
35149                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35150                 //} else {
35151                 //    var el = this.el.createChild();
35152                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35153                 //}
35154                 
35155                 this.add(region, ret);
35156                 break;
35157             
35158             /*
35159             case 'TreePanel': // our new panel!
35160                 cfg.el = this.el.createChild();
35161                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35162                 this.add(region, ret);
35163                 break;
35164             */
35165             
35166             case 'Nest': 
35167                 // create a new Layout (which is  a Border Layout...
35168                 
35169                 var clayout = cfg.layout;
35170                 clayout.el  = this.el.createChild();
35171                 clayout.items   = clayout.items  || [];
35172                 
35173                 delete cfg.layout;
35174                 
35175                 // replace this exitems with the clayout ones..
35176                 xitems = clayout.items;
35177                  
35178                 // force background off if it's in center...
35179                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35180                     cfg.background = false;
35181                 }
35182                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35183                 
35184                 
35185                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35186                 //console.log('adding nested layout panel '  + cfg.toSource());
35187                 this.add(region, ret);
35188                 nb = {}; /// find first...
35189                 break;
35190             
35191             case 'Grid':
35192                 
35193                 // needs grid and region
35194                 
35195                 //var el = this.getRegion(region).el.createChild();
35196                 /*
35197                  *var el = this.el.createChild();
35198                 // create the grid first...
35199                 cfg.grid.container = el;
35200                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35201                 */
35202                 
35203                 if (region == 'center' && this.active ) {
35204                     cfg.background = false;
35205                 }
35206                 
35207                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35208                 
35209                 this.add(region, ret);
35210                 /*
35211                 if (cfg.background) {
35212                     // render grid on panel activation (if panel background)
35213                     ret.on('activate', function(gp) {
35214                         if (!gp.grid.rendered) {
35215                     //        gp.grid.render(el);
35216                         }
35217                     });
35218                 } else {
35219                   //  cfg.grid.render(el);
35220                 }
35221                 */
35222                 break;
35223            
35224            
35225             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35226                 // it was the old xcomponent building that caused this before.
35227                 // espeically if border is the top element in the tree.
35228                 ret = this;
35229                 break; 
35230                 
35231                     
35232                 
35233                 
35234                 
35235             default:
35236                 /*
35237                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35238                     
35239                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35240                     this.add(region, ret);
35241                 } else {
35242                 */
35243                     Roo.log(cfg);
35244                     throw "Can not add '" + cfg.xtype + "' to Border";
35245                     return null;
35246              
35247                                 
35248              
35249         }
35250         this.beginUpdate();
35251         // add children..
35252         var region = '';
35253         var abn = {};
35254         Roo.each(xitems, function(i)  {
35255             region = nb && i.region ? i.region : false;
35256             
35257             var add = ret.addxtype(i);
35258            
35259             if (region) {
35260                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35261                 if (!i.background) {
35262                     abn[region] = nb[region] ;
35263                 }
35264             }
35265             
35266         });
35267         this.endUpdate();
35268
35269         // make the last non-background panel active..
35270         //if (nb) { Roo.log(abn); }
35271         if (nb) {
35272             
35273             for(var r in abn) {
35274                 region = this.getRegion(r);
35275                 if (region) {
35276                     // tried using nb[r], but it does not work..
35277                      
35278                     region.showPanel(abn[r]);
35279                    
35280                 }
35281             }
35282         }
35283         return ret;
35284         
35285     },
35286     
35287     
35288 // private
35289     factory : function(cfg)
35290     {
35291         
35292         var validRegions = Roo.bootstrap.layout.Border.regions;
35293
35294         var target = cfg.region;
35295         cfg.mgr = this;
35296         
35297         var r = Roo.bootstrap.layout;
35298         Roo.log(target);
35299         switch(target){
35300             case "north":
35301                 return new r.North(cfg);
35302             case "south":
35303                 return new r.South(cfg);
35304             case "east":
35305                 return new r.East(cfg);
35306             case "west":
35307                 return new r.West(cfg);
35308             case "center":
35309                 return new r.Center(cfg);
35310         }
35311         throw 'Layout region "'+target+'" not supported.';
35312     }
35313     
35314     
35315 });
35316  /*
35317  * Based on:
35318  * Ext JS Library 1.1.1
35319  * Copyright(c) 2006-2007, Ext JS, LLC.
35320  *
35321  * Originally Released Under LGPL - original licence link has changed is not relivant.
35322  *
35323  * Fork - LGPL
35324  * <script type="text/javascript">
35325  */
35326  
35327 /**
35328  * @class Roo.bootstrap.layout.Basic
35329  * @extends Roo.util.Observable
35330  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35331  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35332  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35333  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35334  * @cfg {string}   region  the region that it inhabits..
35335  * @cfg {bool}   skipConfig skip config?
35336  * 
35337
35338  */
35339 Roo.bootstrap.layout.Basic = function(config){
35340     
35341     this.mgr = config.mgr;
35342     
35343     this.position = config.region;
35344     
35345     var skipConfig = config.skipConfig;
35346     
35347     this.events = {
35348         /**
35349          * @scope Roo.BasicLayoutRegion
35350          */
35351         
35352         /**
35353          * @event beforeremove
35354          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35355          * @param {Roo.LayoutRegion} this
35356          * @param {Roo.ContentPanel} panel The panel
35357          * @param {Object} e The cancel event object
35358          */
35359         "beforeremove" : true,
35360         /**
35361          * @event invalidated
35362          * Fires when the layout for this region is changed.
35363          * @param {Roo.LayoutRegion} this
35364          */
35365         "invalidated" : true,
35366         /**
35367          * @event visibilitychange
35368          * Fires when this region is shown or hidden 
35369          * @param {Roo.LayoutRegion} this
35370          * @param {Boolean} visibility true or false
35371          */
35372         "visibilitychange" : true,
35373         /**
35374          * @event paneladded
35375          * Fires when a panel is added. 
35376          * @param {Roo.LayoutRegion} this
35377          * @param {Roo.ContentPanel} panel The panel
35378          */
35379         "paneladded" : true,
35380         /**
35381          * @event panelremoved
35382          * Fires when a panel is removed. 
35383          * @param {Roo.LayoutRegion} this
35384          * @param {Roo.ContentPanel} panel The panel
35385          */
35386         "panelremoved" : true,
35387         /**
35388          * @event beforecollapse
35389          * Fires when this region before collapse.
35390          * @param {Roo.LayoutRegion} this
35391          */
35392         "beforecollapse" : true,
35393         /**
35394          * @event collapsed
35395          * Fires when this region is collapsed.
35396          * @param {Roo.LayoutRegion} this
35397          */
35398         "collapsed" : true,
35399         /**
35400          * @event expanded
35401          * Fires when this region is expanded.
35402          * @param {Roo.LayoutRegion} this
35403          */
35404         "expanded" : true,
35405         /**
35406          * @event slideshow
35407          * Fires when this region is slid into view.
35408          * @param {Roo.LayoutRegion} this
35409          */
35410         "slideshow" : true,
35411         /**
35412          * @event slidehide
35413          * Fires when this region slides out of view. 
35414          * @param {Roo.LayoutRegion} this
35415          */
35416         "slidehide" : true,
35417         /**
35418          * @event panelactivated
35419          * Fires when a panel is activated. 
35420          * @param {Roo.LayoutRegion} this
35421          * @param {Roo.ContentPanel} panel The activated panel
35422          */
35423         "panelactivated" : true,
35424         /**
35425          * @event resized
35426          * Fires when the user resizes this region. 
35427          * @param {Roo.LayoutRegion} this
35428          * @param {Number} newSize The new size (width for east/west, height for north/south)
35429          */
35430         "resized" : true
35431     };
35432     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35433     this.panels = new Roo.util.MixedCollection();
35434     this.panels.getKey = this.getPanelId.createDelegate(this);
35435     this.box = null;
35436     this.activePanel = null;
35437     // ensure listeners are added...
35438     
35439     if (config.listeners || config.events) {
35440         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35441             listeners : config.listeners || {},
35442             events : config.events || {}
35443         });
35444     }
35445     
35446     if(skipConfig !== true){
35447         this.applyConfig(config);
35448     }
35449 };
35450
35451 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35452 {
35453     getPanelId : function(p){
35454         return p.getId();
35455     },
35456     
35457     applyConfig : function(config){
35458         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35459         this.config = config;
35460         
35461     },
35462     
35463     /**
35464      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35465      * the width, for horizontal (north, south) the height.
35466      * @param {Number} newSize The new width or height
35467      */
35468     resizeTo : function(newSize){
35469         var el = this.el ? this.el :
35470                  (this.activePanel ? this.activePanel.getEl() : null);
35471         if(el){
35472             switch(this.position){
35473                 case "east":
35474                 case "west":
35475                     el.setWidth(newSize);
35476                     this.fireEvent("resized", this, newSize);
35477                 break;
35478                 case "north":
35479                 case "south":
35480                     el.setHeight(newSize);
35481                     this.fireEvent("resized", this, newSize);
35482                 break;                
35483             }
35484         }
35485     },
35486     
35487     getBox : function(){
35488         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35489     },
35490     
35491     getMargins : function(){
35492         return this.margins;
35493     },
35494     
35495     updateBox : function(box){
35496         this.box = box;
35497         var el = this.activePanel.getEl();
35498         el.dom.style.left = box.x + "px";
35499         el.dom.style.top = box.y + "px";
35500         this.activePanel.setSize(box.width, box.height);
35501     },
35502     
35503     /**
35504      * Returns the container element for this region.
35505      * @return {Roo.Element}
35506      */
35507     getEl : function(){
35508         return this.activePanel;
35509     },
35510     
35511     /**
35512      * Returns true if this region is currently visible.
35513      * @return {Boolean}
35514      */
35515     isVisible : function(){
35516         return this.activePanel ? true : false;
35517     },
35518     
35519     setActivePanel : function(panel){
35520         panel = this.getPanel(panel);
35521         if(this.activePanel && this.activePanel != panel){
35522             this.activePanel.setActiveState(false);
35523             this.activePanel.getEl().setLeftTop(-10000,-10000);
35524         }
35525         this.activePanel = panel;
35526         panel.setActiveState(true);
35527         if(this.box){
35528             panel.setSize(this.box.width, this.box.height);
35529         }
35530         this.fireEvent("panelactivated", this, panel);
35531         this.fireEvent("invalidated");
35532     },
35533     
35534     /**
35535      * Show the specified panel.
35536      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35537      * @return {Roo.ContentPanel} The shown panel or null
35538      */
35539     showPanel : function(panel){
35540         panel = this.getPanel(panel);
35541         if(panel){
35542             this.setActivePanel(panel);
35543         }
35544         return panel;
35545     },
35546     
35547     /**
35548      * Get the active panel for this region.
35549      * @return {Roo.ContentPanel} The active panel or null
35550      */
35551     getActivePanel : function(){
35552         return this.activePanel;
35553     },
35554     
35555     /**
35556      * Add the passed ContentPanel(s)
35557      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35558      * @return {Roo.ContentPanel} The panel added (if only one was added)
35559      */
35560     add : function(panel){
35561         if(arguments.length > 1){
35562             for(var i = 0, len = arguments.length; i < len; i++) {
35563                 this.add(arguments[i]);
35564             }
35565             return null;
35566         }
35567         if(this.hasPanel(panel)){
35568             this.showPanel(panel);
35569             return panel;
35570         }
35571         var el = panel.getEl();
35572         if(el.dom.parentNode != this.mgr.el.dom){
35573             this.mgr.el.dom.appendChild(el.dom);
35574         }
35575         if(panel.setRegion){
35576             panel.setRegion(this);
35577         }
35578         this.panels.add(panel);
35579         el.setStyle("position", "absolute");
35580         if(!panel.background){
35581             this.setActivePanel(panel);
35582             if(this.config.initialSize && this.panels.getCount()==1){
35583                 this.resizeTo(this.config.initialSize);
35584             }
35585         }
35586         this.fireEvent("paneladded", this, panel);
35587         return panel;
35588     },
35589     
35590     /**
35591      * Returns true if the panel is in this region.
35592      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35593      * @return {Boolean}
35594      */
35595     hasPanel : function(panel){
35596         if(typeof panel == "object"){ // must be panel obj
35597             panel = panel.getId();
35598         }
35599         return this.getPanel(panel) ? true : false;
35600     },
35601     
35602     /**
35603      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35604      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35605      * @param {Boolean} preservePanel Overrides the config preservePanel option
35606      * @return {Roo.ContentPanel} The panel that was removed
35607      */
35608     remove : function(panel, preservePanel){
35609         panel = this.getPanel(panel);
35610         if(!panel){
35611             return null;
35612         }
35613         var e = {};
35614         this.fireEvent("beforeremove", this, panel, e);
35615         if(e.cancel === true){
35616             return null;
35617         }
35618         var panelId = panel.getId();
35619         this.panels.removeKey(panelId);
35620         return panel;
35621     },
35622     
35623     /**
35624      * Returns the panel specified or null if it's not in this region.
35625      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35626      * @return {Roo.ContentPanel}
35627      */
35628     getPanel : function(id){
35629         if(typeof id == "object"){ // must be panel obj
35630             return id;
35631         }
35632         return this.panels.get(id);
35633     },
35634     
35635     /**
35636      * Returns this regions position (north/south/east/west/center).
35637      * @return {String} 
35638      */
35639     getPosition: function(){
35640         return this.position;    
35641     }
35642 });/*
35643  * Based on:
35644  * Ext JS Library 1.1.1
35645  * Copyright(c) 2006-2007, Ext JS, LLC.
35646  *
35647  * Originally Released Under LGPL - original licence link has changed is not relivant.
35648  *
35649  * Fork - LGPL
35650  * <script type="text/javascript">
35651  */
35652  
35653 /**
35654  * @class Roo.bootstrap.layout.Region
35655  * @extends Roo.bootstrap.layout.Basic
35656  * This class represents a region in a layout manager.
35657  
35658  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35659  * @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})
35660  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35661  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35662  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35663  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35664  * @cfg {String}    title           The title for the region (overrides panel titles)
35665  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35666  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35667  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35668  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35669  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35670  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35671  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35672  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35673  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35674  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35675
35676  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35677  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35678  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35679  * @cfg {Number}    width           For East/West panels
35680  * @cfg {Number}    height          For North/South panels
35681  * @cfg {Boolean}   split           To show the splitter
35682  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35683  * 
35684  * @cfg {string}   cls             Extra CSS classes to add to region
35685  * 
35686  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35687  * @cfg {string}   region  the region that it inhabits..
35688  *
35689
35690  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35691  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35692
35693  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35694  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35695  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35696  */
35697 Roo.bootstrap.layout.Region = function(config)
35698 {
35699     this.applyConfig(config);
35700
35701     var mgr = config.mgr;
35702     var pos = config.region;
35703     config.skipConfig = true;
35704     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35705     
35706     if (mgr.el) {
35707         this.onRender(mgr.el);   
35708     }
35709      
35710     this.visible = true;
35711     this.collapsed = false;
35712     this.unrendered_panels = [];
35713 };
35714
35715 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35716
35717     position: '', // set by wrapper (eg. north/south etc..)
35718     unrendered_panels : null,  // unrendered panels.
35719     createBody : function(){
35720         /** This region's body element 
35721         * @type Roo.Element */
35722         this.bodyEl = this.el.createChild({
35723                 tag: "div",
35724                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35725         });
35726     },
35727
35728     onRender: function(ctr, pos)
35729     {
35730         var dh = Roo.DomHelper;
35731         /** This region's container element 
35732         * @type Roo.Element */
35733         this.el = dh.append(ctr.dom, {
35734                 tag: "div",
35735                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35736             }, true);
35737         /** This region's title element 
35738         * @type Roo.Element */
35739     
35740         this.titleEl = dh.append(this.el.dom,
35741             {
35742                     tag: "div",
35743                     unselectable: "on",
35744                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35745                     children:[
35746                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35747                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35748                     ]}, true);
35749         
35750         this.titleEl.enableDisplayMode();
35751         /** This region's title text element 
35752         * @type HTMLElement */
35753         this.titleTextEl = this.titleEl.dom.firstChild;
35754         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35755         /*
35756         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35757         this.closeBtn.enableDisplayMode();
35758         this.closeBtn.on("click", this.closeClicked, this);
35759         this.closeBtn.hide();
35760     */
35761         this.createBody(this.config);
35762         if(this.config.hideWhenEmpty){
35763             this.hide();
35764             this.on("paneladded", this.validateVisibility, this);
35765             this.on("panelremoved", this.validateVisibility, this);
35766         }
35767         if(this.autoScroll){
35768             this.bodyEl.setStyle("overflow", "auto");
35769         }else{
35770             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35771         }
35772         //if(c.titlebar !== false){
35773             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35774                 this.titleEl.hide();
35775             }else{
35776                 this.titleEl.show();
35777                 if(this.config.title){
35778                     this.titleTextEl.innerHTML = this.config.title;
35779                 }
35780             }
35781         //}
35782         if(this.config.collapsed){
35783             this.collapse(true);
35784         }
35785         if(this.config.hidden){
35786             this.hide();
35787         }
35788         
35789         if (this.unrendered_panels && this.unrendered_panels.length) {
35790             for (var i =0;i< this.unrendered_panels.length; i++) {
35791                 this.add(this.unrendered_panels[i]);
35792             }
35793             this.unrendered_panels = null;
35794             
35795         }
35796         
35797     },
35798     
35799     applyConfig : function(c)
35800     {
35801         /*
35802          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35803             var dh = Roo.DomHelper;
35804             if(c.titlebar !== false){
35805                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35806                 this.collapseBtn.on("click", this.collapse, this);
35807                 this.collapseBtn.enableDisplayMode();
35808                 /*
35809                 if(c.showPin === true || this.showPin){
35810                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35811                     this.stickBtn.enableDisplayMode();
35812                     this.stickBtn.on("click", this.expand, this);
35813                     this.stickBtn.hide();
35814                 }
35815                 
35816             }
35817             */
35818             /** This region's collapsed element
35819             * @type Roo.Element */
35820             /*
35821              *
35822             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35823                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35824             ]}, true);
35825             
35826             if(c.floatable !== false){
35827                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35828                this.collapsedEl.on("click", this.collapseClick, this);
35829             }
35830
35831             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35832                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35833                    id: "message", unselectable: "on", style:{"float":"left"}});
35834                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35835              }
35836             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35837             this.expandBtn.on("click", this.expand, this);
35838             
35839         }
35840         
35841         if(this.collapseBtn){
35842             this.collapseBtn.setVisible(c.collapsible == true);
35843         }
35844         
35845         this.cmargins = c.cmargins || this.cmargins ||
35846                          (this.position == "west" || this.position == "east" ?
35847                              {top: 0, left: 2, right:2, bottom: 0} :
35848                              {top: 2, left: 0, right:0, bottom: 2});
35849         */
35850         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35851         
35852         
35853         this.bottomTabs = c.tabPosition != "top";
35854         
35855         this.autoScroll = c.autoScroll || false;
35856         
35857         
35858        
35859         
35860         this.duration = c.duration || .30;
35861         this.slideDuration = c.slideDuration || .45;
35862         this.config = c;
35863        
35864     },
35865     /**
35866      * Returns true if this region is currently visible.
35867      * @return {Boolean}
35868      */
35869     isVisible : function(){
35870         return this.visible;
35871     },
35872
35873     /**
35874      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35875      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35876      */
35877     //setCollapsedTitle : function(title){
35878     //    title = title || "&#160;";
35879      //   if(this.collapsedTitleTextEl){
35880       //      this.collapsedTitleTextEl.innerHTML = title;
35881        // }
35882     //},
35883
35884     getBox : function(){
35885         var b;
35886       //  if(!this.collapsed){
35887             b = this.el.getBox(false, true);
35888        // }else{
35889           //  b = this.collapsedEl.getBox(false, true);
35890         //}
35891         return b;
35892     },
35893
35894     getMargins : function(){
35895         return this.margins;
35896         //return this.collapsed ? this.cmargins : this.margins;
35897     },
35898 /*
35899     highlight : function(){
35900         this.el.addClass("x-layout-panel-dragover");
35901     },
35902
35903     unhighlight : function(){
35904         this.el.removeClass("x-layout-panel-dragover");
35905     },
35906 */
35907     updateBox : function(box)
35908     {
35909         if (!this.bodyEl) {
35910             return; // not rendered yet..
35911         }
35912         
35913         this.box = box;
35914         if(!this.collapsed){
35915             this.el.dom.style.left = box.x + "px";
35916             this.el.dom.style.top = box.y + "px";
35917             this.updateBody(box.width, box.height);
35918         }else{
35919             this.collapsedEl.dom.style.left = box.x + "px";
35920             this.collapsedEl.dom.style.top = box.y + "px";
35921             this.collapsedEl.setSize(box.width, box.height);
35922         }
35923         if(this.tabs){
35924             this.tabs.autoSizeTabs();
35925         }
35926     },
35927
35928     updateBody : function(w, h)
35929     {
35930         if(w !== null){
35931             this.el.setWidth(w);
35932             w -= this.el.getBorderWidth("rl");
35933             if(this.config.adjustments){
35934                 w += this.config.adjustments[0];
35935             }
35936         }
35937         if(h !== null && h > 0){
35938             this.el.setHeight(h);
35939             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35940             h -= this.el.getBorderWidth("tb");
35941             if(this.config.adjustments){
35942                 h += this.config.adjustments[1];
35943             }
35944             this.bodyEl.setHeight(h);
35945             if(this.tabs){
35946                 h = this.tabs.syncHeight(h);
35947             }
35948         }
35949         if(this.panelSize){
35950             w = w !== null ? w : this.panelSize.width;
35951             h = h !== null ? h : this.panelSize.height;
35952         }
35953         if(this.activePanel){
35954             var el = this.activePanel.getEl();
35955             w = w !== null ? w : el.getWidth();
35956             h = h !== null ? h : el.getHeight();
35957             this.panelSize = {width: w, height: h};
35958             this.activePanel.setSize(w, h);
35959         }
35960         if(Roo.isIE && this.tabs){
35961             this.tabs.el.repaint();
35962         }
35963     },
35964
35965     /**
35966      * Returns the container element for this region.
35967      * @return {Roo.Element}
35968      */
35969     getEl : function(){
35970         return this.el;
35971     },
35972
35973     /**
35974      * Hides this region.
35975      */
35976     hide : function(){
35977         //if(!this.collapsed){
35978             this.el.dom.style.left = "-2000px";
35979             this.el.hide();
35980         //}else{
35981          //   this.collapsedEl.dom.style.left = "-2000px";
35982          //   this.collapsedEl.hide();
35983        // }
35984         this.visible = false;
35985         this.fireEvent("visibilitychange", this, false);
35986     },
35987
35988     /**
35989      * Shows this region if it was previously hidden.
35990      */
35991     show : function(){
35992         //if(!this.collapsed){
35993             this.el.show();
35994         //}else{
35995         //    this.collapsedEl.show();
35996        // }
35997         this.visible = true;
35998         this.fireEvent("visibilitychange", this, true);
35999     },
36000 /*
36001     closeClicked : function(){
36002         if(this.activePanel){
36003             this.remove(this.activePanel);
36004         }
36005     },
36006
36007     collapseClick : function(e){
36008         if(this.isSlid){
36009            e.stopPropagation();
36010            this.slideIn();
36011         }else{
36012            e.stopPropagation();
36013            this.slideOut();
36014         }
36015     },
36016 */
36017     /**
36018      * Collapses this region.
36019      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36020      */
36021     /*
36022     collapse : function(skipAnim, skipCheck = false){
36023         if(this.collapsed) {
36024             return;
36025         }
36026         
36027         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36028             
36029             this.collapsed = true;
36030             if(this.split){
36031                 this.split.el.hide();
36032             }
36033             if(this.config.animate && skipAnim !== true){
36034                 this.fireEvent("invalidated", this);
36035                 this.animateCollapse();
36036             }else{
36037                 this.el.setLocation(-20000,-20000);
36038                 this.el.hide();
36039                 this.collapsedEl.show();
36040                 this.fireEvent("collapsed", this);
36041                 this.fireEvent("invalidated", this);
36042             }
36043         }
36044         
36045     },
36046 */
36047     animateCollapse : function(){
36048         // overridden
36049     },
36050
36051     /**
36052      * Expands this region if it was previously collapsed.
36053      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36054      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36055      */
36056     /*
36057     expand : function(e, skipAnim){
36058         if(e) {
36059             e.stopPropagation();
36060         }
36061         if(!this.collapsed || this.el.hasActiveFx()) {
36062             return;
36063         }
36064         if(this.isSlid){
36065             this.afterSlideIn();
36066             skipAnim = true;
36067         }
36068         this.collapsed = false;
36069         if(this.config.animate && skipAnim !== true){
36070             this.animateExpand();
36071         }else{
36072             this.el.show();
36073             if(this.split){
36074                 this.split.el.show();
36075             }
36076             this.collapsedEl.setLocation(-2000,-2000);
36077             this.collapsedEl.hide();
36078             this.fireEvent("invalidated", this);
36079             this.fireEvent("expanded", this);
36080         }
36081     },
36082 */
36083     animateExpand : function(){
36084         // overridden
36085     },
36086
36087     initTabs : function()
36088     {
36089         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36090         
36091         var ts = new Roo.bootstrap.panel.Tabs({
36092                 el: this.bodyEl.dom,
36093                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36094                 disableTooltips: this.config.disableTabTips,
36095                 toolbar : this.config.toolbar
36096             });
36097         
36098         if(this.config.hideTabs){
36099             ts.stripWrap.setDisplayed(false);
36100         }
36101         this.tabs = ts;
36102         ts.resizeTabs = this.config.resizeTabs === true;
36103         ts.minTabWidth = this.config.minTabWidth || 40;
36104         ts.maxTabWidth = this.config.maxTabWidth || 250;
36105         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36106         ts.monitorResize = false;
36107         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36108         ts.bodyEl.addClass('roo-layout-tabs-body');
36109         this.panels.each(this.initPanelAsTab, this);
36110     },
36111
36112     initPanelAsTab : function(panel){
36113         var ti = this.tabs.addTab(
36114             panel.getEl().id,
36115             panel.getTitle(),
36116             null,
36117             this.config.closeOnTab && panel.isClosable(),
36118             panel.tpl
36119         );
36120         if(panel.tabTip !== undefined){
36121             ti.setTooltip(panel.tabTip);
36122         }
36123         ti.on("activate", function(){
36124               this.setActivePanel(panel);
36125         }, this);
36126         
36127         if(this.config.closeOnTab){
36128             ti.on("beforeclose", function(t, e){
36129                 e.cancel = true;
36130                 this.remove(panel);
36131             }, this);
36132         }
36133         
36134         panel.tabItem = ti;
36135         
36136         return ti;
36137     },
36138
36139     updatePanelTitle : function(panel, title)
36140     {
36141         if(this.activePanel == panel){
36142             this.updateTitle(title);
36143         }
36144         if(this.tabs){
36145             var ti = this.tabs.getTab(panel.getEl().id);
36146             ti.setText(title);
36147             if(panel.tabTip !== undefined){
36148                 ti.setTooltip(panel.tabTip);
36149             }
36150         }
36151     },
36152
36153     updateTitle : function(title){
36154         if(this.titleTextEl && !this.config.title){
36155             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36156         }
36157     },
36158
36159     setActivePanel : function(panel)
36160     {
36161         panel = this.getPanel(panel);
36162         if(this.activePanel && this.activePanel != panel){
36163             if(this.activePanel.setActiveState(false) === false){
36164                 return;
36165             }
36166         }
36167         this.activePanel = panel;
36168         panel.setActiveState(true);
36169         if(this.panelSize){
36170             panel.setSize(this.panelSize.width, this.panelSize.height);
36171         }
36172         if(this.closeBtn){
36173             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36174         }
36175         this.updateTitle(panel.getTitle());
36176         if(this.tabs){
36177             this.fireEvent("invalidated", this);
36178         }
36179         this.fireEvent("panelactivated", this, panel);
36180     },
36181
36182     /**
36183      * Shows the specified panel.
36184      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36185      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36186      */
36187     showPanel : function(panel)
36188     {
36189         panel = this.getPanel(panel);
36190         if(panel){
36191             if(this.tabs){
36192                 var tab = this.tabs.getTab(panel.getEl().id);
36193                 if(tab.isHidden()){
36194                     this.tabs.unhideTab(tab.id);
36195                 }
36196                 tab.activate();
36197             }else{
36198                 this.setActivePanel(panel);
36199             }
36200         }
36201         return panel;
36202     },
36203
36204     /**
36205      * Get the active panel for this region.
36206      * @return {Roo.ContentPanel} The active panel or null
36207      */
36208     getActivePanel : function(){
36209         return this.activePanel;
36210     },
36211
36212     validateVisibility : function(){
36213         if(this.panels.getCount() < 1){
36214             this.updateTitle("&#160;");
36215             this.closeBtn.hide();
36216             this.hide();
36217         }else{
36218             if(!this.isVisible()){
36219                 this.show();
36220             }
36221         }
36222     },
36223
36224     /**
36225      * Adds the passed ContentPanel(s) to this region.
36226      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36227      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36228      */
36229     add : function(panel)
36230     {
36231         if(arguments.length > 1){
36232             for(var i = 0, len = arguments.length; i < len; i++) {
36233                 this.add(arguments[i]);
36234             }
36235             return null;
36236         }
36237         
36238         // if we have not been rendered yet, then we can not really do much of this..
36239         if (!this.bodyEl) {
36240             this.unrendered_panels.push(panel);
36241             return panel;
36242         }
36243         
36244         
36245         
36246         
36247         if(this.hasPanel(panel)){
36248             this.showPanel(panel);
36249             return panel;
36250         }
36251         panel.setRegion(this);
36252         this.panels.add(panel);
36253        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36254             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36255             // and hide them... ???
36256             this.bodyEl.dom.appendChild(panel.getEl().dom);
36257             if(panel.background !== true){
36258                 this.setActivePanel(panel);
36259             }
36260             this.fireEvent("paneladded", this, panel);
36261             return panel;
36262         }
36263         */
36264         if(!this.tabs){
36265             this.initTabs();
36266         }else{
36267             this.initPanelAsTab(panel);
36268         }
36269         
36270         
36271         if(panel.background !== true){
36272             this.tabs.activate(panel.getEl().id);
36273         }
36274         this.fireEvent("paneladded", this, panel);
36275         return panel;
36276     },
36277
36278     /**
36279      * Hides the tab for the specified panel.
36280      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36281      */
36282     hidePanel : function(panel){
36283         if(this.tabs && (panel = this.getPanel(panel))){
36284             this.tabs.hideTab(panel.getEl().id);
36285         }
36286     },
36287
36288     /**
36289      * Unhides the tab for a previously hidden panel.
36290      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36291      */
36292     unhidePanel : function(panel){
36293         if(this.tabs && (panel = this.getPanel(panel))){
36294             this.tabs.unhideTab(panel.getEl().id);
36295         }
36296     },
36297
36298     clearPanels : function(){
36299         while(this.panels.getCount() > 0){
36300              this.remove(this.panels.first());
36301         }
36302     },
36303
36304     /**
36305      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36306      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36307      * @param {Boolean} preservePanel Overrides the config preservePanel option
36308      * @return {Roo.ContentPanel} The panel that was removed
36309      */
36310     remove : function(panel, preservePanel)
36311     {
36312         panel = this.getPanel(panel);
36313         if(!panel){
36314             return null;
36315         }
36316         var e = {};
36317         this.fireEvent("beforeremove", this, panel, e);
36318         if(e.cancel === true){
36319             return null;
36320         }
36321         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36322         var panelId = panel.getId();
36323         this.panels.removeKey(panelId);
36324         if(preservePanel){
36325             document.body.appendChild(panel.getEl().dom);
36326         }
36327         if(this.tabs){
36328             this.tabs.removeTab(panel.getEl().id);
36329         }else if (!preservePanel){
36330             this.bodyEl.dom.removeChild(panel.getEl().dom);
36331         }
36332         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36333             var p = this.panels.first();
36334             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36335             tempEl.appendChild(p.getEl().dom);
36336             this.bodyEl.update("");
36337             this.bodyEl.dom.appendChild(p.getEl().dom);
36338             tempEl = null;
36339             this.updateTitle(p.getTitle());
36340             this.tabs = null;
36341             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36342             this.setActivePanel(p);
36343         }
36344         panel.setRegion(null);
36345         if(this.activePanel == panel){
36346             this.activePanel = null;
36347         }
36348         if(this.config.autoDestroy !== false && preservePanel !== true){
36349             try{panel.destroy();}catch(e){}
36350         }
36351         this.fireEvent("panelremoved", this, panel);
36352         return panel;
36353     },
36354
36355     /**
36356      * Returns the TabPanel component used by this region
36357      * @return {Roo.TabPanel}
36358      */
36359     getTabs : function(){
36360         return this.tabs;
36361     },
36362
36363     createTool : function(parentEl, className){
36364         var btn = Roo.DomHelper.append(parentEl, {
36365             tag: "div",
36366             cls: "x-layout-tools-button",
36367             children: [ {
36368                 tag: "div",
36369                 cls: "roo-layout-tools-button-inner " + className,
36370                 html: "&#160;"
36371             }]
36372         }, true);
36373         btn.addClassOnOver("roo-layout-tools-button-over");
36374         return btn;
36375     }
36376 });/*
36377  * Based on:
36378  * Ext JS Library 1.1.1
36379  * Copyright(c) 2006-2007, Ext JS, LLC.
36380  *
36381  * Originally Released Under LGPL - original licence link has changed is not relivant.
36382  *
36383  * Fork - LGPL
36384  * <script type="text/javascript">
36385  */
36386  
36387
36388
36389 /**
36390  * @class Roo.SplitLayoutRegion
36391  * @extends Roo.LayoutRegion
36392  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36393  */
36394 Roo.bootstrap.layout.Split = function(config){
36395     this.cursor = config.cursor;
36396     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36397 };
36398
36399 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36400 {
36401     splitTip : "Drag to resize.",
36402     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36403     useSplitTips : false,
36404
36405     applyConfig : function(config){
36406         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36407     },
36408     
36409     onRender : function(ctr,pos) {
36410         
36411         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36412         if(!this.config.split){
36413             return;
36414         }
36415         if(!this.split){
36416             
36417             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36418                             tag: "div",
36419                             id: this.el.id + "-split",
36420                             cls: "roo-layout-split roo-layout-split-"+this.position,
36421                             html: "&#160;"
36422             });
36423             /** The SplitBar for this region 
36424             * @type Roo.SplitBar */
36425             // does not exist yet...
36426             Roo.log([this.position, this.orientation]);
36427             
36428             this.split = new Roo.bootstrap.SplitBar({
36429                 dragElement : splitEl,
36430                 resizingElement: this.el,
36431                 orientation : this.orientation
36432             });
36433             
36434             this.split.on("moved", this.onSplitMove, this);
36435             this.split.useShim = this.config.useShim === true;
36436             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36437             if(this.useSplitTips){
36438                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36439             }
36440             //if(config.collapsible){
36441             //    this.split.el.on("dblclick", this.collapse,  this);
36442             //}
36443         }
36444         if(typeof this.config.minSize != "undefined"){
36445             this.split.minSize = this.config.minSize;
36446         }
36447         if(typeof this.config.maxSize != "undefined"){
36448             this.split.maxSize = this.config.maxSize;
36449         }
36450         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36451             this.hideSplitter();
36452         }
36453         
36454     },
36455
36456     getHMaxSize : function(){
36457          var cmax = this.config.maxSize || 10000;
36458          var center = this.mgr.getRegion("center");
36459          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36460     },
36461
36462     getVMaxSize : function(){
36463          var cmax = this.config.maxSize || 10000;
36464          var center = this.mgr.getRegion("center");
36465          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36466     },
36467
36468     onSplitMove : function(split, newSize){
36469         this.fireEvent("resized", this, newSize);
36470     },
36471     
36472     /** 
36473      * Returns the {@link Roo.SplitBar} for this region.
36474      * @return {Roo.SplitBar}
36475      */
36476     getSplitBar : function(){
36477         return this.split;
36478     },
36479     
36480     hide : function(){
36481         this.hideSplitter();
36482         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36483     },
36484
36485     hideSplitter : function(){
36486         if(this.split){
36487             this.split.el.setLocation(-2000,-2000);
36488             this.split.el.hide();
36489         }
36490     },
36491
36492     show : function(){
36493         if(this.split){
36494             this.split.el.show();
36495         }
36496         Roo.bootstrap.layout.Split.superclass.show.call(this);
36497     },
36498     
36499     beforeSlide: function(){
36500         if(Roo.isGecko){// firefox overflow auto bug workaround
36501             this.bodyEl.clip();
36502             if(this.tabs) {
36503                 this.tabs.bodyEl.clip();
36504             }
36505             if(this.activePanel){
36506                 this.activePanel.getEl().clip();
36507                 
36508                 if(this.activePanel.beforeSlide){
36509                     this.activePanel.beforeSlide();
36510                 }
36511             }
36512         }
36513     },
36514     
36515     afterSlide : function(){
36516         if(Roo.isGecko){// firefox overflow auto bug workaround
36517             this.bodyEl.unclip();
36518             if(this.tabs) {
36519                 this.tabs.bodyEl.unclip();
36520             }
36521             if(this.activePanel){
36522                 this.activePanel.getEl().unclip();
36523                 if(this.activePanel.afterSlide){
36524                     this.activePanel.afterSlide();
36525                 }
36526             }
36527         }
36528     },
36529
36530     initAutoHide : function(){
36531         if(this.autoHide !== false){
36532             if(!this.autoHideHd){
36533                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36534                 this.autoHideHd = {
36535                     "mouseout": function(e){
36536                         if(!e.within(this.el, true)){
36537                             st.delay(500);
36538                         }
36539                     },
36540                     "mouseover" : function(e){
36541                         st.cancel();
36542                     },
36543                     scope : this
36544                 };
36545             }
36546             this.el.on(this.autoHideHd);
36547         }
36548     },
36549
36550     clearAutoHide : function(){
36551         if(this.autoHide !== false){
36552             this.el.un("mouseout", this.autoHideHd.mouseout);
36553             this.el.un("mouseover", this.autoHideHd.mouseover);
36554         }
36555     },
36556
36557     clearMonitor : function(){
36558         Roo.get(document).un("click", this.slideInIf, this);
36559     },
36560
36561     // these names are backwards but not changed for compat
36562     slideOut : function(){
36563         if(this.isSlid || this.el.hasActiveFx()){
36564             return;
36565         }
36566         this.isSlid = true;
36567         if(this.collapseBtn){
36568             this.collapseBtn.hide();
36569         }
36570         this.closeBtnState = this.closeBtn.getStyle('display');
36571         this.closeBtn.hide();
36572         if(this.stickBtn){
36573             this.stickBtn.show();
36574         }
36575         this.el.show();
36576         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36577         this.beforeSlide();
36578         this.el.setStyle("z-index", 10001);
36579         this.el.slideIn(this.getSlideAnchor(), {
36580             callback: function(){
36581                 this.afterSlide();
36582                 this.initAutoHide();
36583                 Roo.get(document).on("click", this.slideInIf, this);
36584                 this.fireEvent("slideshow", this);
36585             },
36586             scope: this,
36587             block: true
36588         });
36589     },
36590
36591     afterSlideIn : function(){
36592         this.clearAutoHide();
36593         this.isSlid = false;
36594         this.clearMonitor();
36595         this.el.setStyle("z-index", "");
36596         if(this.collapseBtn){
36597             this.collapseBtn.show();
36598         }
36599         this.closeBtn.setStyle('display', this.closeBtnState);
36600         if(this.stickBtn){
36601             this.stickBtn.hide();
36602         }
36603         this.fireEvent("slidehide", this);
36604     },
36605
36606     slideIn : function(cb){
36607         if(!this.isSlid || this.el.hasActiveFx()){
36608             Roo.callback(cb);
36609             return;
36610         }
36611         this.isSlid = false;
36612         this.beforeSlide();
36613         this.el.slideOut(this.getSlideAnchor(), {
36614             callback: function(){
36615                 this.el.setLeftTop(-10000, -10000);
36616                 this.afterSlide();
36617                 this.afterSlideIn();
36618                 Roo.callback(cb);
36619             },
36620             scope: this,
36621             block: true
36622         });
36623     },
36624     
36625     slideInIf : function(e){
36626         if(!e.within(this.el)){
36627             this.slideIn();
36628         }
36629     },
36630
36631     animateCollapse : function(){
36632         this.beforeSlide();
36633         this.el.setStyle("z-index", 20000);
36634         var anchor = this.getSlideAnchor();
36635         this.el.slideOut(anchor, {
36636             callback : function(){
36637                 this.el.setStyle("z-index", "");
36638                 this.collapsedEl.slideIn(anchor, {duration:.3});
36639                 this.afterSlide();
36640                 this.el.setLocation(-10000,-10000);
36641                 this.el.hide();
36642                 this.fireEvent("collapsed", this);
36643             },
36644             scope: this,
36645             block: true
36646         });
36647     },
36648
36649     animateExpand : function(){
36650         this.beforeSlide();
36651         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36652         this.el.setStyle("z-index", 20000);
36653         this.collapsedEl.hide({
36654             duration:.1
36655         });
36656         this.el.slideIn(this.getSlideAnchor(), {
36657             callback : function(){
36658                 this.el.setStyle("z-index", "");
36659                 this.afterSlide();
36660                 if(this.split){
36661                     this.split.el.show();
36662                 }
36663                 this.fireEvent("invalidated", this);
36664                 this.fireEvent("expanded", this);
36665             },
36666             scope: this,
36667             block: true
36668         });
36669     },
36670
36671     anchors : {
36672         "west" : "left",
36673         "east" : "right",
36674         "north" : "top",
36675         "south" : "bottom"
36676     },
36677
36678     sanchors : {
36679         "west" : "l",
36680         "east" : "r",
36681         "north" : "t",
36682         "south" : "b"
36683     },
36684
36685     canchors : {
36686         "west" : "tl-tr",
36687         "east" : "tr-tl",
36688         "north" : "tl-bl",
36689         "south" : "bl-tl"
36690     },
36691
36692     getAnchor : function(){
36693         return this.anchors[this.position];
36694     },
36695
36696     getCollapseAnchor : function(){
36697         return this.canchors[this.position];
36698     },
36699
36700     getSlideAnchor : function(){
36701         return this.sanchors[this.position];
36702     },
36703
36704     getAlignAdj : function(){
36705         var cm = this.cmargins;
36706         switch(this.position){
36707             case "west":
36708                 return [0, 0];
36709             break;
36710             case "east":
36711                 return [0, 0];
36712             break;
36713             case "north":
36714                 return [0, 0];
36715             break;
36716             case "south":
36717                 return [0, 0];
36718             break;
36719         }
36720     },
36721
36722     getExpandAdj : function(){
36723         var c = this.collapsedEl, cm = this.cmargins;
36724         switch(this.position){
36725             case "west":
36726                 return [-(cm.right+c.getWidth()+cm.left), 0];
36727             break;
36728             case "east":
36729                 return [cm.right+c.getWidth()+cm.left, 0];
36730             break;
36731             case "north":
36732                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36733             break;
36734             case "south":
36735                 return [0, cm.top+cm.bottom+c.getHeight()];
36736             break;
36737         }
36738     }
36739 });/*
36740  * Based on:
36741  * Ext JS Library 1.1.1
36742  * Copyright(c) 2006-2007, Ext JS, LLC.
36743  *
36744  * Originally Released Under LGPL - original licence link has changed is not relivant.
36745  *
36746  * Fork - LGPL
36747  * <script type="text/javascript">
36748  */
36749 /*
36750  * These classes are private internal classes
36751  */
36752 Roo.bootstrap.layout.Center = function(config){
36753     config.region = "center";
36754     Roo.bootstrap.layout.Region.call(this, config);
36755     this.visible = true;
36756     this.minWidth = config.minWidth || 20;
36757     this.minHeight = config.minHeight || 20;
36758 };
36759
36760 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36761     hide : function(){
36762         // center panel can't be hidden
36763     },
36764     
36765     show : function(){
36766         // center panel can't be hidden
36767     },
36768     
36769     getMinWidth: function(){
36770         return this.minWidth;
36771     },
36772     
36773     getMinHeight: function(){
36774         return this.minHeight;
36775     }
36776 });
36777
36778
36779
36780
36781  
36782
36783
36784
36785
36786
36787 Roo.bootstrap.layout.North = function(config)
36788 {
36789     config.region = 'north';
36790     config.cursor = 'n-resize';
36791     
36792     Roo.bootstrap.layout.Split.call(this, config);
36793     
36794     
36795     if(this.split){
36796         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36797         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36798         this.split.el.addClass("roo-layout-split-v");
36799     }
36800     var size = config.initialSize || config.height;
36801     if(typeof size != "undefined"){
36802         this.el.setHeight(size);
36803     }
36804 };
36805 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36806 {
36807     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36808     
36809     
36810     
36811     getBox : function(){
36812         if(this.collapsed){
36813             return this.collapsedEl.getBox();
36814         }
36815         var box = this.el.getBox();
36816         if(this.split){
36817             box.height += this.split.el.getHeight();
36818         }
36819         return box;
36820     },
36821     
36822     updateBox : function(box){
36823         if(this.split && !this.collapsed){
36824             box.height -= this.split.el.getHeight();
36825             this.split.el.setLeft(box.x);
36826             this.split.el.setTop(box.y+box.height);
36827             this.split.el.setWidth(box.width);
36828         }
36829         if(this.collapsed){
36830             this.updateBody(box.width, null);
36831         }
36832         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36833     }
36834 });
36835
36836
36837
36838
36839
36840 Roo.bootstrap.layout.South = function(config){
36841     config.region = 'south';
36842     config.cursor = 's-resize';
36843     Roo.bootstrap.layout.Split.call(this, config);
36844     if(this.split){
36845         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36846         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36847         this.split.el.addClass("roo-layout-split-v");
36848     }
36849     var size = config.initialSize || config.height;
36850     if(typeof size != "undefined"){
36851         this.el.setHeight(size);
36852     }
36853 };
36854
36855 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36856     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36857     getBox : function(){
36858         if(this.collapsed){
36859             return this.collapsedEl.getBox();
36860         }
36861         var box = this.el.getBox();
36862         if(this.split){
36863             var sh = this.split.el.getHeight();
36864             box.height += sh;
36865             box.y -= sh;
36866         }
36867         return box;
36868     },
36869     
36870     updateBox : function(box){
36871         if(this.split && !this.collapsed){
36872             var sh = this.split.el.getHeight();
36873             box.height -= sh;
36874             box.y += sh;
36875             this.split.el.setLeft(box.x);
36876             this.split.el.setTop(box.y-sh);
36877             this.split.el.setWidth(box.width);
36878         }
36879         if(this.collapsed){
36880             this.updateBody(box.width, null);
36881         }
36882         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36883     }
36884 });
36885
36886 Roo.bootstrap.layout.East = function(config){
36887     config.region = "east";
36888     config.cursor = "e-resize";
36889     Roo.bootstrap.layout.Split.call(this, config);
36890     if(this.split){
36891         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36892         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36893         this.split.el.addClass("roo-layout-split-h");
36894     }
36895     var size = config.initialSize || config.width;
36896     if(typeof size != "undefined"){
36897         this.el.setWidth(size);
36898     }
36899 };
36900 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36901     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36902     getBox : function(){
36903         if(this.collapsed){
36904             return this.collapsedEl.getBox();
36905         }
36906         var box = this.el.getBox();
36907         if(this.split){
36908             var sw = this.split.el.getWidth();
36909             box.width += sw;
36910             box.x -= sw;
36911         }
36912         return box;
36913     },
36914
36915     updateBox : function(box){
36916         if(this.split && !this.collapsed){
36917             var sw = this.split.el.getWidth();
36918             box.width -= sw;
36919             this.split.el.setLeft(box.x);
36920             this.split.el.setTop(box.y);
36921             this.split.el.setHeight(box.height);
36922             box.x += sw;
36923         }
36924         if(this.collapsed){
36925             this.updateBody(null, box.height);
36926         }
36927         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36928     }
36929 });
36930
36931 Roo.bootstrap.layout.West = function(config){
36932     config.region = "west";
36933     config.cursor = "w-resize";
36934     
36935     Roo.bootstrap.layout.Split.call(this, config);
36936     if(this.split){
36937         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36938         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36939         this.split.el.addClass("roo-layout-split-h");
36940     }
36941     
36942 };
36943 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36944     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36945     
36946     onRender: function(ctr, pos)
36947     {
36948         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36949         var size = this.config.initialSize || this.config.width;
36950         if(typeof size != "undefined"){
36951             this.el.setWidth(size);
36952         }
36953     },
36954     
36955     getBox : function(){
36956         if(this.collapsed){
36957             return this.collapsedEl.getBox();
36958         }
36959         var box = this.el.getBox();
36960         if(this.split){
36961             box.width += this.split.el.getWidth();
36962         }
36963         return box;
36964     },
36965     
36966     updateBox : function(box){
36967         if(this.split && !this.collapsed){
36968             var sw = this.split.el.getWidth();
36969             box.width -= sw;
36970             this.split.el.setLeft(box.x+box.width);
36971             this.split.el.setTop(box.y);
36972             this.split.el.setHeight(box.height);
36973         }
36974         if(this.collapsed){
36975             this.updateBody(null, box.height);
36976         }
36977         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36978     }
36979 });
36980 Roo.namespace("Roo.bootstrap.panel");/*
36981  * Based on:
36982  * Ext JS Library 1.1.1
36983  * Copyright(c) 2006-2007, Ext JS, LLC.
36984  *
36985  * Originally Released Under LGPL - original licence link has changed is not relivant.
36986  *
36987  * Fork - LGPL
36988  * <script type="text/javascript">
36989  */
36990 /**
36991  * @class Roo.ContentPanel
36992  * @extends Roo.util.Observable
36993  * A basic ContentPanel element.
36994  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36995  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36996  * @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
36997  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36998  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36999  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37000  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37001  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37002  * @cfg {String} title          The title for this panel
37003  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37004  * @cfg {String} url            Calls {@link #setUrl} with this value
37005  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37006  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37007  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37008  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37009  * @cfg {Boolean} badges render the badges
37010
37011  * @constructor
37012  * Create a new ContentPanel.
37013  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37014  * @param {String/Object} config A string to set only the title or a config object
37015  * @param {String} content (optional) Set the HTML content for this panel
37016  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37017  */
37018 Roo.bootstrap.panel.Content = function( config){
37019     
37020     this.tpl = config.tpl || false;
37021     
37022     var el = config.el;
37023     var content = config.content;
37024
37025     if(config.autoCreate){ // xtype is available if this is called from factory
37026         el = Roo.id();
37027     }
37028     this.el = Roo.get(el);
37029     if(!this.el && config && config.autoCreate){
37030         if(typeof config.autoCreate == "object"){
37031             if(!config.autoCreate.id){
37032                 config.autoCreate.id = config.id||el;
37033             }
37034             this.el = Roo.DomHelper.append(document.body,
37035                         config.autoCreate, true);
37036         }else{
37037             var elcfg =  {   tag: "div",
37038                             cls: "roo-layout-inactive-content",
37039                             id: config.id||el
37040                             };
37041             if (config.html) {
37042                 elcfg.html = config.html;
37043                 
37044             }
37045                         
37046             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37047         }
37048     } 
37049     this.closable = false;
37050     this.loaded = false;
37051     this.active = false;
37052    
37053       
37054     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37055         
37056         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37057         
37058         this.wrapEl = this.el; //this.el.wrap();
37059         var ti = [];
37060         if (config.toolbar.items) {
37061             ti = config.toolbar.items ;
37062             delete config.toolbar.items ;
37063         }
37064         
37065         var nitems = [];
37066         this.toolbar.render(this.wrapEl, 'before');
37067         for(var i =0;i < ti.length;i++) {
37068           //  Roo.log(['add child', items[i]]);
37069             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37070         }
37071         this.toolbar.items = nitems;
37072         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37073         delete config.toolbar;
37074         
37075     }
37076     /*
37077     // xtype created footer. - not sure if will work as we normally have to render first..
37078     if (this.footer && !this.footer.el && this.footer.xtype) {
37079         if (!this.wrapEl) {
37080             this.wrapEl = this.el.wrap();
37081         }
37082     
37083         this.footer.container = this.wrapEl.createChild();
37084          
37085         this.footer = Roo.factory(this.footer, Roo);
37086         
37087     }
37088     */
37089     
37090      if(typeof config == "string"){
37091         this.title = config;
37092     }else{
37093         Roo.apply(this, config);
37094     }
37095     
37096     if(this.resizeEl){
37097         this.resizeEl = Roo.get(this.resizeEl, true);
37098     }else{
37099         this.resizeEl = this.el;
37100     }
37101     // handle view.xtype
37102     
37103  
37104     
37105     
37106     this.addEvents({
37107         /**
37108          * @event activate
37109          * Fires when this panel is activated. 
37110          * @param {Roo.ContentPanel} this
37111          */
37112         "activate" : true,
37113         /**
37114          * @event deactivate
37115          * Fires when this panel is activated. 
37116          * @param {Roo.ContentPanel} this
37117          */
37118         "deactivate" : true,
37119
37120         /**
37121          * @event resize
37122          * Fires when this panel is resized if fitToFrame is true.
37123          * @param {Roo.ContentPanel} this
37124          * @param {Number} width The width after any component adjustments
37125          * @param {Number} height The height after any component adjustments
37126          */
37127         "resize" : true,
37128         
37129          /**
37130          * @event render
37131          * Fires when this tab is created
37132          * @param {Roo.ContentPanel} this
37133          */
37134         "render" : true
37135         
37136         
37137         
37138     });
37139     
37140
37141     
37142     
37143     if(this.autoScroll){
37144         this.resizeEl.setStyle("overflow", "auto");
37145     } else {
37146         // fix randome scrolling
37147         //this.el.on('scroll', function() {
37148         //    Roo.log('fix random scolling');
37149         //    this.scrollTo('top',0); 
37150         //});
37151     }
37152     content = content || this.content;
37153     if(content){
37154         this.setContent(content);
37155     }
37156     if(config && config.url){
37157         this.setUrl(this.url, this.params, this.loadOnce);
37158     }
37159     
37160     
37161     
37162     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37163     
37164     if (this.view && typeof(this.view.xtype) != 'undefined') {
37165         this.view.el = this.el.appendChild(document.createElement("div"));
37166         this.view = Roo.factory(this.view); 
37167         this.view.render  &&  this.view.render(false, '');  
37168     }
37169     
37170     
37171     this.fireEvent('render', this);
37172 };
37173
37174 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37175     
37176     tabTip : '',
37177     
37178     setRegion : function(region){
37179         this.region = region;
37180         this.setActiveClass(region && !this.background);
37181     },
37182     
37183     
37184     setActiveClass: function(state)
37185     {
37186         if(state){
37187            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37188            this.el.setStyle('position','relative');
37189         }else{
37190            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37191            this.el.setStyle('position', 'absolute');
37192         } 
37193     },
37194     
37195     /**
37196      * Returns the toolbar for this Panel if one was configured. 
37197      * @return {Roo.Toolbar} 
37198      */
37199     getToolbar : function(){
37200         return this.toolbar;
37201     },
37202     
37203     setActiveState : function(active)
37204     {
37205         this.active = active;
37206         this.setActiveClass(active);
37207         if(!active){
37208             if(this.fireEvent("deactivate", this) === false){
37209                 return false;
37210             }
37211             return true;
37212         }
37213         this.fireEvent("activate", this);
37214         return true;
37215     },
37216     /**
37217      * Updates this panel's element
37218      * @param {String} content The new content
37219      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37220     */
37221     setContent : function(content, loadScripts){
37222         this.el.update(content, loadScripts);
37223     },
37224
37225     ignoreResize : function(w, h){
37226         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37227             return true;
37228         }else{
37229             this.lastSize = {width: w, height: h};
37230             return false;
37231         }
37232     },
37233     /**
37234      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37235      * @return {Roo.UpdateManager} The UpdateManager
37236      */
37237     getUpdateManager : function(){
37238         return this.el.getUpdateManager();
37239     },
37240      /**
37241      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37242      * @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:
37243 <pre><code>
37244 panel.load({
37245     url: "your-url.php",
37246     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37247     callback: yourFunction,
37248     scope: yourObject, //(optional scope)
37249     discardUrl: false,
37250     nocache: false,
37251     text: "Loading...",
37252     timeout: 30,
37253     scripts: false
37254 });
37255 </code></pre>
37256      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37257      * 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.
37258      * @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}
37259      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37260      * @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.
37261      * @return {Roo.ContentPanel} this
37262      */
37263     load : function(){
37264         var um = this.el.getUpdateManager();
37265         um.update.apply(um, arguments);
37266         return this;
37267     },
37268
37269
37270     /**
37271      * 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.
37272      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37273      * @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)
37274      * @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)
37275      * @return {Roo.UpdateManager} The UpdateManager
37276      */
37277     setUrl : function(url, params, loadOnce){
37278         if(this.refreshDelegate){
37279             this.removeListener("activate", this.refreshDelegate);
37280         }
37281         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37282         this.on("activate", this.refreshDelegate);
37283         return this.el.getUpdateManager();
37284     },
37285     
37286     _handleRefresh : function(url, params, loadOnce){
37287         if(!loadOnce || !this.loaded){
37288             var updater = this.el.getUpdateManager();
37289             updater.update(url, params, this._setLoaded.createDelegate(this));
37290         }
37291     },
37292     
37293     _setLoaded : function(){
37294         this.loaded = true;
37295     }, 
37296     
37297     /**
37298      * Returns this panel's id
37299      * @return {String} 
37300      */
37301     getId : function(){
37302         return this.el.id;
37303     },
37304     
37305     /** 
37306      * Returns this panel's element - used by regiosn to add.
37307      * @return {Roo.Element} 
37308      */
37309     getEl : function(){
37310         return this.wrapEl || this.el;
37311     },
37312     
37313    
37314     
37315     adjustForComponents : function(width, height)
37316     {
37317         //Roo.log('adjustForComponents ');
37318         if(this.resizeEl != this.el){
37319             width -= this.el.getFrameWidth('lr');
37320             height -= this.el.getFrameWidth('tb');
37321         }
37322         if(this.toolbar){
37323             var te = this.toolbar.getEl();
37324             te.setWidth(width);
37325             height -= te.getHeight();
37326         }
37327         if(this.footer){
37328             var te = this.footer.getEl();
37329             te.setWidth(width);
37330             height -= te.getHeight();
37331         }
37332         
37333         
37334         if(this.adjustments){
37335             width += this.adjustments[0];
37336             height += this.adjustments[1];
37337         }
37338         return {"width": width, "height": height};
37339     },
37340     
37341     setSize : function(width, height){
37342         if(this.fitToFrame && !this.ignoreResize(width, height)){
37343             if(this.fitContainer && this.resizeEl != this.el){
37344                 this.el.setSize(width, height);
37345             }
37346             var size = this.adjustForComponents(width, height);
37347             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37348             this.fireEvent('resize', this, size.width, size.height);
37349         }
37350     },
37351     
37352     /**
37353      * Returns this panel's title
37354      * @return {String} 
37355      */
37356     getTitle : function(){
37357         
37358         if (typeof(this.title) != 'object') {
37359             return this.title;
37360         }
37361         
37362         var t = '';
37363         for (var k in this.title) {
37364             if (!this.title.hasOwnProperty(k)) {
37365                 continue;
37366             }
37367             
37368             if (k.indexOf('-') >= 0) {
37369                 var s = k.split('-');
37370                 for (var i = 0; i<s.length; i++) {
37371                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37372                 }
37373             } else {
37374                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37375             }
37376         }
37377         return t;
37378     },
37379     
37380     /**
37381      * Set this panel's title
37382      * @param {String} title
37383      */
37384     setTitle : function(title){
37385         this.title = title;
37386         if(this.region){
37387             this.region.updatePanelTitle(this, title);
37388         }
37389     },
37390     
37391     /**
37392      * Returns true is this panel was configured to be closable
37393      * @return {Boolean} 
37394      */
37395     isClosable : function(){
37396         return this.closable;
37397     },
37398     
37399     beforeSlide : function(){
37400         this.el.clip();
37401         this.resizeEl.clip();
37402     },
37403     
37404     afterSlide : function(){
37405         this.el.unclip();
37406         this.resizeEl.unclip();
37407     },
37408     
37409     /**
37410      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37411      *   Will fail silently if the {@link #setUrl} method has not been called.
37412      *   This does not activate the panel, just updates its content.
37413      */
37414     refresh : function(){
37415         if(this.refreshDelegate){
37416            this.loaded = false;
37417            this.refreshDelegate();
37418         }
37419     },
37420     
37421     /**
37422      * Destroys this panel
37423      */
37424     destroy : function(){
37425         this.el.removeAllListeners();
37426         var tempEl = document.createElement("span");
37427         tempEl.appendChild(this.el.dom);
37428         tempEl.innerHTML = "";
37429         this.el.remove();
37430         this.el = null;
37431     },
37432     
37433     /**
37434      * form - if the content panel contains a form - this is a reference to it.
37435      * @type {Roo.form.Form}
37436      */
37437     form : false,
37438     /**
37439      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37440      *    This contains a reference to it.
37441      * @type {Roo.View}
37442      */
37443     view : false,
37444     
37445       /**
37446      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37447      * <pre><code>
37448
37449 layout.addxtype({
37450        xtype : 'Form',
37451        items: [ .... ]
37452    }
37453 );
37454
37455 </code></pre>
37456      * @param {Object} cfg Xtype definition of item to add.
37457      */
37458     
37459     
37460     getChildContainer: function () {
37461         return this.getEl();
37462     }
37463     
37464     
37465     /*
37466         var  ret = new Roo.factory(cfg);
37467         return ret;
37468         
37469         
37470         // add form..
37471         if (cfg.xtype.match(/^Form$/)) {
37472             
37473             var el;
37474             //if (this.footer) {
37475             //    el = this.footer.container.insertSibling(false, 'before');
37476             //} else {
37477                 el = this.el.createChild();
37478             //}
37479
37480             this.form = new  Roo.form.Form(cfg);
37481             
37482             
37483             if ( this.form.allItems.length) {
37484                 this.form.render(el.dom);
37485             }
37486             return this.form;
37487         }
37488         // should only have one of theses..
37489         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37490             // views.. should not be just added - used named prop 'view''
37491             
37492             cfg.el = this.el.appendChild(document.createElement("div"));
37493             // factory?
37494             
37495             var ret = new Roo.factory(cfg);
37496              
37497              ret.render && ret.render(false, ''); // render blank..
37498             this.view = ret;
37499             return ret;
37500         }
37501         return false;
37502     }
37503     \*/
37504 });
37505  
37506 /**
37507  * @class Roo.bootstrap.panel.Grid
37508  * @extends Roo.bootstrap.panel.Content
37509  * @constructor
37510  * Create a new GridPanel.
37511  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37512  * @param {Object} config A the config object
37513   
37514  */
37515
37516
37517
37518 Roo.bootstrap.panel.Grid = function(config)
37519 {
37520     
37521       
37522     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37523         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37524
37525     config.el = this.wrapper;
37526     //this.el = this.wrapper;
37527     
37528       if (config.container) {
37529         // ctor'ed from a Border/panel.grid
37530         
37531         
37532         this.wrapper.setStyle("overflow", "hidden");
37533         this.wrapper.addClass('roo-grid-container');
37534
37535     }
37536     
37537     
37538     if(config.toolbar){
37539         var tool_el = this.wrapper.createChild();    
37540         this.toolbar = Roo.factory(config.toolbar);
37541         var ti = [];
37542         if (config.toolbar.items) {
37543             ti = config.toolbar.items ;
37544             delete config.toolbar.items ;
37545         }
37546         
37547         var nitems = [];
37548         this.toolbar.render(tool_el);
37549         for(var i =0;i < ti.length;i++) {
37550           //  Roo.log(['add child', items[i]]);
37551             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37552         }
37553         this.toolbar.items = nitems;
37554         
37555         delete config.toolbar;
37556     }
37557     
37558     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37559     config.grid.scrollBody = true;;
37560     config.grid.monitorWindowResize = false; // turn off autosizing
37561     config.grid.autoHeight = false;
37562     config.grid.autoWidth = false;
37563     
37564     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37565     
37566     if (config.background) {
37567         // render grid on panel activation (if panel background)
37568         this.on('activate', function(gp) {
37569             if (!gp.grid.rendered) {
37570                 gp.grid.render(this.wrapper);
37571                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37572             }
37573         });
37574             
37575     } else {
37576         this.grid.render(this.wrapper);
37577         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37578
37579     }
37580     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37581     // ??? needed ??? config.el = this.wrapper;
37582     
37583     
37584     
37585   
37586     // xtype created footer. - not sure if will work as we normally have to render first..
37587     if (this.footer && !this.footer.el && this.footer.xtype) {
37588         
37589         var ctr = this.grid.getView().getFooterPanel(true);
37590         this.footer.dataSource = this.grid.dataSource;
37591         this.footer = Roo.factory(this.footer, Roo);
37592         this.footer.render(ctr);
37593         
37594     }
37595     
37596     
37597     
37598     
37599      
37600 };
37601
37602 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37603     getId : function(){
37604         return this.grid.id;
37605     },
37606     
37607     /**
37608      * Returns the grid for this panel
37609      * @return {Roo.bootstrap.Table} 
37610      */
37611     getGrid : function(){
37612         return this.grid;    
37613     },
37614     
37615     setSize : function(width, height){
37616         if(!this.ignoreResize(width, height)){
37617             var grid = this.grid;
37618             var size = this.adjustForComponents(width, height);
37619             var gridel = grid.getGridEl();
37620             gridel.setSize(size.width, size.height);
37621             /*
37622             var thd = grid.getGridEl().select('thead',true).first();
37623             var tbd = grid.getGridEl().select('tbody', true).first();
37624             if (tbd) {
37625                 tbd.setSize(width, height - thd.getHeight());
37626             }
37627             */
37628             grid.autoSize();
37629         }
37630     },
37631      
37632     
37633     
37634     beforeSlide : function(){
37635         this.grid.getView().scroller.clip();
37636     },
37637     
37638     afterSlide : function(){
37639         this.grid.getView().scroller.unclip();
37640     },
37641     
37642     destroy : function(){
37643         this.grid.destroy();
37644         delete this.grid;
37645         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37646     }
37647 });
37648
37649 /**
37650  * @class Roo.bootstrap.panel.Nest
37651  * @extends Roo.bootstrap.panel.Content
37652  * @constructor
37653  * Create a new Panel, that can contain a layout.Border.
37654  * 
37655  * 
37656  * @param {Roo.BorderLayout} layout The layout for this panel
37657  * @param {String/Object} config A string to set only the title or a config object
37658  */
37659 Roo.bootstrap.panel.Nest = function(config)
37660 {
37661     // construct with only one argument..
37662     /* FIXME - implement nicer consturctors
37663     if (layout.layout) {
37664         config = layout;
37665         layout = config.layout;
37666         delete config.layout;
37667     }
37668     if (layout.xtype && !layout.getEl) {
37669         // then layout needs constructing..
37670         layout = Roo.factory(layout, Roo);
37671     }
37672     */
37673     
37674     config.el =  config.layout.getEl();
37675     
37676     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37677     
37678     config.layout.monitorWindowResize = false; // turn off autosizing
37679     this.layout = config.layout;
37680     this.layout.getEl().addClass("roo-layout-nested-layout");
37681     
37682     
37683     
37684     
37685 };
37686
37687 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37688
37689     setSize : function(width, height){
37690         if(!this.ignoreResize(width, height)){
37691             var size = this.adjustForComponents(width, height);
37692             var el = this.layout.getEl();
37693             if (size.height < 1) {
37694                 el.setWidth(size.width);   
37695             } else {
37696                 el.setSize(size.width, size.height);
37697             }
37698             var touch = el.dom.offsetWidth;
37699             this.layout.layout();
37700             // ie requires a double layout on the first pass
37701             if(Roo.isIE && !this.initialized){
37702                 this.initialized = true;
37703                 this.layout.layout();
37704             }
37705         }
37706     },
37707     
37708     // activate all subpanels if not currently active..
37709     
37710     setActiveState : function(active){
37711         this.active = active;
37712         this.setActiveClass(active);
37713         
37714         if(!active){
37715             this.fireEvent("deactivate", this);
37716             return;
37717         }
37718         
37719         this.fireEvent("activate", this);
37720         // not sure if this should happen before or after..
37721         if (!this.layout) {
37722             return; // should not happen..
37723         }
37724         var reg = false;
37725         for (var r in this.layout.regions) {
37726             reg = this.layout.getRegion(r);
37727             if (reg.getActivePanel()) {
37728                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37729                 reg.setActivePanel(reg.getActivePanel());
37730                 continue;
37731             }
37732             if (!reg.panels.length) {
37733                 continue;
37734             }
37735             reg.showPanel(reg.getPanel(0));
37736         }
37737         
37738         
37739         
37740         
37741     },
37742     
37743     /**
37744      * Returns the nested BorderLayout for this panel
37745      * @return {Roo.BorderLayout} 
37746      */
37747     getLayout : function(){
37748         return this.layout;
37749     },
37750     
37751      /**
37752      * Adds a xtype elements to the layout of the nested panel
37753      * <pre><code>
37754
37755 panel.addxtype({
37756        xtype : 'ContentPanel',
37757        region: 'west',
37758        items: [ .... ]
37759    }
37760 );
37761
37762 panel.addxtype({
37763         xtype : 'NestedLayoutPanel',
37764         region: 'west',
37765         layout: {
37766            center: { },
37767            west: { }   
37768         },
37769         items : [ ... list of content panels or nested layout panels.. ]
37770    }
37771 );
37772 </code></pre>
37773      * @param {Object} cfg Xtype definition of item to add.
37774      */
37775     addxtype : function(cfg) {
37776         return this.layout.addxtype(cfg);
37777     
37778     }
37779 });        /*
37780  * Based on:
37781  * Ext JS Library 1.1.1
37782  * Copyright(c) 2006-2007, Ext JS, LLC.
37783  *
37784  * Originally Released Under LGPL - original licence link has changed is not relivant.
37785  *
37786  * Fork - LGPL
37787  * <script type="text/javascript">
37788  */
37789 /**
37790  * @class Roo.TabPanel
37791  * @extends Roo.util.Observable
37792  * A lightweight tab container.
37793  * <br><br>
37794  * Usage:
37795  * <pre><code>
37796 // basic tabs 1, built from existing content
37797 var tabs = new Roo.TabPanel("tabs1");
37798 tabs.addTab("script", "View Script");
37799 tabs.addTab("markup", "View Markup");
37800 tabs.activate("script");
37801
37802 // more advanced tabs, built from javascript
37803 var jtabs = new Roo.TabPanel("jtabs");
37804 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37805
37806 // set up the UpdateManager
37807 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37808 var updater = tab2.getUpdateManager();
37809 updater.setDefaultUrl("ajax1.htm");
37810 tab2.on('activate', updater.refresh, updater, true);
37811
37812 // Use setUrl for Ajax loading
37813 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37814 tab3.setUrl("ajax2.htm", null, true);
37815
37816 // Disabled tab
37817 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37818 tab4.disable();
37819
37820 jtabs.activate("jtabs-1");
37821  * </code></pre>
37822  * @constructor
37823  * Create a new TabPanel.
37824  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37825  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37826  */
37827 Roo.bootstrap.panel.Tabs = function(config){
37828     /**
37829     * The container element for this TabPanel.
37830     * @type Roo.Element
37831     */
37832     this.el = Roo.get(config.el);
37833     delete config.el;
37834     if(config){
37835         if(typeof config == "boolean"){
37836             this.tabPosition = config ? "bottom" : "top";
37837         }else{
37838             Roo.apply(this, config);
37839         }
37840     }
37841     
37842     if(this.tabPosition == "bottom"){
37843         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37844         this.el.addClass("roo-tabs-bottom");
37845     }
37846     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37847     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37848     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37849     if(Roo.isIE){
37850         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37851     }
37852     if(this.tabPosition != "bottom"){
37853         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37854          * @type Roo.Element
37855          */
37856         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37857         this.el.addClass("roo-tabs-top");
37858     }
37859     this.items = [];
37860
37861     this.bodyEl.setStyle("position", "relative");
37862
37863     this.active = null;
37864     this.activateDelegate = this.activate.createDelegate(this);
37865
37866     this.addEvents({
37867         /**
37868          * @event tabchange
37869          * Fires when the active tab changes
37870          * @param {Roo.TabPanel} this
37871          * @param {Roo.TabPanelItem} activePanel The new active tab
37872          */
37873         "tabchange": true,
37874         /**
37875          * @event beforetabchange
37876          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37877          * @param {Roo.TabPanel} this
37878          * @param {Object} e Set cancel to true on this object to cancel the tab change
37879          * @param {Roo.TabPanelItem} tab The tab being changed to
37880          */
37881         "beforetabchange" : true
37882     });
37883
37884     Roo.EventManager.onWindowResize(this.onResize, this);
37885     this.cpad = this.el.getPadding("lr");
37886     this.hiddenCount = 0;
37887
37888
37889     // toolbar on the tabbar support...
37890     if (this.toolbar) {
37891         alert("no toolbar support yet");
37892         this.toolbar  = false;
37893         /*
37894         var tcfg = this.toolbar;
37895         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37896         this.toolbar = new Roo.Toolbar(tcfg);
37897         if (Roo.isSafari) {
37898             var tbl = tcfg.container.child('table', true);
37899             tbl.setAttribute('width', '100%');
37900         }
37901         */
37902         
37903     }
37904    
37905
37906
37907     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37908 };
37909
37910 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37911     /*
37912      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37913      */
37914     tabPosition : "top",
37915     /*
37916      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37917      */
37918     currentTabWidth : 0,
37919     /*
37920      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37921      */
37922     minTabWidth : 40,
37923     /*
37924      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37925      */
37926     maxTabWidth : 250,
37927     /*
37928      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37929      */
37930     preferredTabWidth : 175,
37931     /*
37932      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37933      */
37934     resizeTabs : false,
37935     /*
37936      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37937      */
37938     monitorResize : true,
37939     /*
37940      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37941      */
37942     toolbar : false,
37943
37944     /**
37945      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37946      * @param {String} id The id of the div to use <b>or create</b>
37947      * @param {String} text The text for the tab
37948      * @param {String} content (optional) Content to put in the TabPanelItem body
37949      * @param {Boolean} closable (optional) True to create a close icon on the tab
37950      * @return {Roo.TabPanelItem} The created TabPanelItem
37951      */
37952     addTab : function(id, text, content, closable, tpl)
37953     {
37954         var item = new Roo.bootstrap.panel.TabItem({
37955             panel: this,
37956             id : id,
37957             text : text,
37958             closable : closable,
37959             tpl : tpl
37960         });
37961         this.addTabItem(item);
37962         if(content){
37963             item.setContent(content);
37964         }
37965         return item;
37966     },
37967
37968     /**
37969      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37970      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37971      * @return {Roo.TabPanelItem}
37972      */
37973     getTab : function(id){
37974         return this.items[id];
37975     },
37976
37977     /**
37978      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37979      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37980      */
37981     hideTab : function(id){
37982         var t = this.items[id];
37983         if(!t.isHidden()){
37984            t.setHidden(true);
37985            this.hiddenCount++;
37986            this.autoSizeTabs();
37987         }
37988     },
37989
37990     /**
37991      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37992      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37993      */
37994     unhideTab : function(id){
37995         var t = this.items[id];
37996         if(t.isHidden()){
37997            t.setHidden(false);
37998            this.hiddenCount--;
37999            this.autoSizeTabs();
38000         }
38001     },
38002
38003     /**
38004      * Adds an existing {@link Roo.TabPanelItem}.
38005      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38006      */
38007     addTabItem : function(item){
38008         this.items[item.id] = item;
38009         this.items.push(item);
38010       //  if(this.resizeTabs){
38011     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38012   //         this.autoSizeTabs();
38013 //        }else{
38014 //            item.autoSize();
38015        // }
38016     },
38017
38018     /**
38019      * Removes a {@link Roo.TabPanelItem}.
38020      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38021      */
38022     removeTab : function(id){
38023         var items = this.items;
38024         var tab = items[id];
38025         if(!tab) { return; }
38026         var index = items.indexOf(tab);
38027         if(this.active == tab && items.length > 1){
38028             var newTab = this.getNextAvailable(index);
38029             if(newTab) {
38030                 newTab.activate();
38031             }
38032         }
38033         this.stripEl.dom.removeChild(tab.pnode.dom);
38034         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38035             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38036         }
38037         items.splice(index, 1);
38038         delete this.items[tab.id];
38039         tab.fireEvent("close", tab);
38040         tab.purgeListeners();
38041         this.autoSizeTabs();
38042     },
38043
38044     getNextAvailable : function(start){
38045         var items = this.items;
38046         var index = start;
38047         // look for a next tab that will slide over to
38048         // replace the one being removed
38049         while(index < items.length){
38050             var item = items[++index];
38051             if(item && !item.isHidden()){
38052                 return item;
38053             }
38054         }
38055         // if one isn't found select the previous tab (on the left)
38056         index = start;
38057         while(index >= 0){
38058             var item = items[--index];
38059             if(item && !item.isHidden()){
38060                 return item;
38061             }
38062         }
38063         return null;
38064     },
38065
38066     /**
38067      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38068      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38069      */
38070     disableTab : function(id){
38071         var tab = this.items[id];
38072         if(tab && this.active != tab){
38073             tab.disable();
38074         }
38075     },
38076
38077     /**
38078      * Enables a {@link Roo.TabPanelItem} that is disabled.
38079      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38080      */
38081     enableTab : function(id){
38082         var tab = this.items[id];
38083         tab.enable();
38084     },
38085
38086     /**
38087      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38088      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38089      * @return {Roo.TabPanelItem} The TabPanelItem.
38090      */
38091     activate : function(id){
38092         var tab = this.items[id];
38093         if(!tab){
38094             return null;
38095         }
38096         if(tab == this.active || tab.disabled){
38097             return tab;
38098         }
38099         var e = {};
38100         this.fireEvent("beforetabchange", this, e, tab);
38101         if(e.cancel !== true && !tab.disabled){
38102             if(this.active){
38103                 this.active.hide();
38104             }
38105             this.active = this.items[id];
38106             this.active.show();
38107             this.fireEvent("tabchange", this, this.active);
38108         }
38109         return tab;
38110     },
38111
38112     /**
38113      * Gets the active {@link Roo.TabPanelItem}.
38114      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38115      */
38116     getActiveTab : function(){
38117         return this.active;
38118     },
38119
38120     /**
38121      * Updates the tab body element to fit the height of the container element
38122      * for overflow scrolling
38123      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38124      */
38125     syncHeight : function(targetHeight){
38126         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38127         var bm = this.bodyEl.getMargins();
38128         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38129         this.bodyEl.setHeight(newHeight);
38130         return newHeight;
38131     },
38132
38133     onResize : function(){
38134         if(this.monitorResize){
38135             this.autoSizeTabs();
38136         }
38137     },
38138
38139     /**
38140      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38141      */
38142     beginUpdate : function(){
38143         this.updating = true;
38144     },
38145
38146     /**
38147      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38148      */
38149     endUpdate : function(){
38150         this.updating = false;
38151         this.autoSizeTabs();
38152     },
38153
38154     /**
38155      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38156      */
38157     autoSizeTabs : function(){
38158         var count = this.items.length;
38159         var vcount = count - this.hiddenCount;
38160         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38161             return;
38162         }
38163         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38164         var availWidth = Math.floor(w / vcount);
38165         var b = this.stripBody;
38166         if(b.getWidth() > w){
38167             var tabs = this.items;
38168             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38169             if(availWidth < this.minTabWidth){
38170                 /*if(!this.sleft){    // incomplete scrolling code
38171                     this.createScrollButtons();
38172                 }
38173                 this.showScroll();
38174                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38175             }
38176         }else{
38177             if(this.currentTabWidth < this.preferredTabWidth){
38178                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38179             }
38180         }
38181     },
38182
38183     /**
38184      * Returns the number of tabs in this TabPanel.
38185      * @return {Number}
38186      */
38187      getCount : function(){
38188          return this.items.length;
38189      },
38190
38191     /**
38192      * Resizes all the tabs to the passed width
38193      * @param {Number} The new width
38194      */
38195     setTabWidth : function(width){
38196         this.currentTabWidth = width;
38197         for(var i = 0, len = this.items.length; i < len; i++) {
38198                 if(!this.items[i].isHidden()) {
38199                 this.items[i].setWidth(width);
38200             }
38201         }
38202     },
38203
38204     /**
38205      * Destroys this TabPanel
38206      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38207      */
38208     destroy : function(removeEl){
38209         Roo.EventManager.removeResizeListener(this.onResize, this);
38210         for(var i = 0, len = this.items.length; i < len; i++){
38211             this.items[i].purgeListeners();
38212         }
38213         if(removeEl === true){
38214             this.el.update("");
38215             this.el.remove();
38216         }
38217     },
38218     
38219     createStrip : function(container)
38220     {
38221         var strip = document.createElement("nav");
38222         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38223         container.appendChild(strip);
38224         return strip;
38225     },
38226     
38227     createStripList : function(strip)
38228     {
38229         // div wrapper for retard IE
38230         // returns the "tr" element.
38231         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38232         //'<div class="x-tabs-strip-wrap">'+
38233           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38234           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38235         return strip.firstChild; //.firstChild.firstChild.firstChild;
38236     },
38237     createBody : function(container)
38238     {
38239         var body = document.createElement("div");
38240         Roo.id(body, "tab-body");
38241         //Roo.fly(body).addClass("x-tabs-body");
38242         Roo.fly(body).addClass("tab-content");
38243         container.appendChild(body);
38244         return body;
38245     },
38246     createItemBody :function(bodyEl, id){
38247         var body = Roo.getDom(id);
38248         if(!body){
38249             body = document.createElement("div");
38250             body.id = id;
38251         }
38252         //Roo.fly(body).addClass("x-tabs-item-body");
38253         Roo.fly(body).addClass("tab-pane");
38254          bodyEl.insertBefore(body, bodyEl.firstChild);
38255         return body;
38256     },
38257     /** @private */
38258     createStripElements :  function(stripEl, text, closable, tpl)
38259     {
38260         var td = document.createElement("li"); // was td..
38261         
38262         
38263         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38264         
38265         
38266         stripEl.appendChild(td);
38267         /*if(closable){
38268             td.className = "x-tabs-closable";
38269             if(!this.closeTpl){
38270                 this.closeTpl = new Roo.Template(
38271                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38272                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38273                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38274                 );
38275             }
38276             var el = this.closeTpl.overwrite(td, {"text": text});
38277             var close = el.getElementsByTagName("div")[0];
38278             var inner = el.getElementsByTagName("em")[0];
38279             return {"el": el, "close": close, "inner": inner};
38280         } else {
38281         */
38282         // not sure what this is..
38283 //            if(!this.tabTpl){
38284                 //this.tabTpl = new Roo.Template(
38285                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38286                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38287                 //);
38288 //                this.tabTpl = new Roo.Template(
38289 //                   '<a href="#">' +
38290 //                   '<span unselectable="on"' +
38291 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38292 //                            ' >{text}</span></a>'
38293 //                );
38294 //                
38295 //            }
38296
38297
38298             var template = tpl || this.tabTpl || false;
38299             
38300             if(!template){
38301                 
38302                 template = new Roo.Template(
38303                    '<a href="#">' +
38304                    '<span unselectable="on"' +
38305                             (this.disableTooltips ? '' : ' title="{text}"') +
38306                             ' >{text}</span></a>'
38307                 );
38308             }
38309             
38310             switch (typeof(template)) {
38311                 case 'object' :
38312                     break;
38313                 case 'string' :
38314                     template = new Roo.Template(template);
38315                     break;
38316                 default :
38317                     break;
38318             }
38319             
38320             var el = template.overwrite(td, {"text": text});
38321             
38322             var inner = el.getElementsByTagName("span")[0];
38323             
38324             return {"el": el, "inner": inner};
38325             
38326     }
38327         
38328     
38329 });
38330
38331 /**
38332  * @class Roo.TabPanelItem
38333  * @extends Roo.util.Observable
38334  * Represents an individual item (tab plus body) in a TabPanel.
38335  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38336  * @param {String} id The id of this TabPanelItem
38337  * @param {String} text The text for the tab of this TabPanelItem
38338  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38339  */
38340 Roo.bootstrap.panel.TabItem = function(config){
38341     /**
38342      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38343      * @type Roo.TabPanel
38344      */
38345     this.tabPanel = config.panel;
38346     /**
38347      * The id for this TabPanelItem
38348      * @type String
38349      */
38350     this.id = config.id;
38351     /** @private */
38352     this.disabled = false;
38353     /** @private */
38354     this.text = config.text;
38355     /** @private */
38356     this.loaded = false;
38357     this.closable = config.closable;
38358
38359     /**
38360      * The body element for this TabPanelItem.
38361      * @type Roo.Element
38362      */
38363     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38364     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38365     this.bodyEl.setStyle("display", "block");
38366     this.bodyEl.setStyle("zoom", "1");
38367     //this.hideAction();
38368
38369     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38370     /** @private */
38371     this.el = Roo.get(els.el);
38372     this.inner = Roo.get(els.inner, true);
38373     this.textEl = Roo.get(this.el.dom.firstChild, true);
38374     this.pnode = Roo.get(els.el.parentNode, true);
38375 //    this.el.on("mousedown", this.onTabMouseDown, this);
38376     this.el.on("click", this.onTabClick, this);
38377     /** @private */
38378     if(config.closable){
38379         var c = Roo.get(els.close, true);
38380         c.dom.title = this.closeText;
38381         c.addClassOnOver("close-over");
38382         c.on("click", this.closeClick, this);
38383      }
38384
38385     this.addEvents({
38386          /**
38387          * @event activate
38388          * Fires when this tab becomes the active tab.
38389          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38390          * @param {Roo.TabPanelItem} this
38391          */
38392         "activate": true,
38393         /**
38394          * @event beforeclose
38395          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38396          * @param {Roo.TabPanelItem} this
38397          * @param {Object} e Set cancel to true on this object to cancel the close.
38398          */
38399         "beforeclose": true,
38400         /**
38401          * @event close
38402          * Fires when this tab is closed.
38403          * @param {Roo.TabPanelItem} this
38404          */
38405          "close": true,
38406         /**
38407          * @event deactivate
38408          * Fires when this tab is no longer the active tab.
38409          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38410          * @param {Roo.TabPanelItem} this
38411          */
38412          "deactivate" : true
38413     });
38414     this.hidden = false;
38415
38416     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38417 };
38418
38419 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38420            {
38421     purgeListeners : function(){
38422        Roo.util.Observable.prototype.purgeListeners.call(this);
38423        this.el.removeAllListeners();
38424     },
38425     /**
38426      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38427      */
38428     show : function(){
38429         this.pnode.addClass("active");
38430         this.showAction();
38431         if(Roo.isOpera){
38432             this.tabPanel.stripWrap.repaint();
38433         }
38434         this.fireEvent("activate", this.tabPanel, this);
38435     },
38436
38437     /**
38438      * Returns true if this tab is the active tab.
38439      * @return {Boolean}
38440      */
38441     isActive : function(){
38442         return this.tabPanel.getActiveTab() == this;
38443     },
38444
38445     /**
38446      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38447      */
38448     hide : function(){
38449         this.pnode.removeClass("active");
38450         this.hideAction();
38451         this.fireEvent("deactivate", this.tabPanel, this);
38452     },
38453
38454     hideAction : function(){
38455         this.bodyEl.hide();
38456         this.bodyEl.setStyle("position", "absolute");
38457         this.bodyEl.setLeft("-20000px");
38458         this.bodyEl.setTop("-20000px");
38459     },
38460
38461     showAction : function(){
38462         this.bodyEl.setStyle("position", "relative");
38463         this.bodyEl.setTop("");
38464         this.bodyEl.setLeft("");
38465         this.bodyEl.show();
38466     },
38467
38468     /**
38469      * Set the tooltip for the tab.
38470      * @param {String} tooltip The tab's tooltip
38471      */
38472     setTooltip : function(text){
38473         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38474             this.textEl.dom.qtip = text;
38475             this.textEl.dom.removeAttribute('title');
38476         }else{
38477             this.textEl.dom.title = text;
38478         }
38479     },
38480
38481     onTabClick : function(e){
38482         e.preventDefault();
38483         this.tabPanel.activate(this.id);
38484     },
38485
38486     onTabMouseDown : function(e){
38487         e.preventDefault();
38488         this.tabPanel.activate(this.id);
38489     },
38490 /*
38491     getWidth : function(){
38492         return this.inner.getWidth();
38493     },
38494
38495     setWidth : function(width){
38496         var iwidth = width - this.pnode.getPadding("lr");
38497         this.inner.setWidth(iwidth);
38498         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38499         this.pnode.setWidth(width);
38500     },
38501 */
38502     /**
38503      * Show or hide the tab
38504      * @param {Boolean} hidden True to hide or false to show.
38505      */
38506     setHidden : function(hidden){
38507         this.hidden = hidden;
38508         this.pnode.setStyle("display", hidden ? "none" : "");
38509     },
38510
38511     /**
38512      * Returns true if this tab is "hidden"
38513      * @return {Boolean}
38514      */
38515     isHidden : function(){
38516         return this.hidden;
38517     },
38518
38519     /**
38520      * Returns the text for this tab
38521      * @return {String}
38522      */
38523     getText : function(){
38524         return this.text;
38525     },
38526     /*
38527     autoSize : function(){
38528         //this.el.beginMeasure();
38529         this.textEl.setWidth(1);
38530         /*
38531          *  #2804 [new] Tabs in Roojs
38532          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38533          */
38534         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38535         //this.el.endMeasure();
38536     //},
38537
38538     /**
38539      * Sets the text for the tab (Note: this also sets the tooltip text)
38540      * @param {String} text The tab's text and tooltip
38541      */
38542     setText : function(text){
38543         this.text = text;
38544         this.textEl.update(text);
38545         this.setTooltip(text);
38546         //if(!this.tabPanel.resizeTabs){
38547         //    this.autoSize();
38548         //}
38549     },
38550     /**
38551      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38552      */
38553     activate : function(){
38554         this.tabPanel.activate(this.id);
38555     },
38556
38557     /**
38558      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38559      */
38560     disable : function(){
38561         if(this.tabPanel.active != this){
38562             this.disabled = true;
38563             this.pnode.addClass("disabled");
38564         }
38565     },
38566
38567     /**
38568      * Enables this TabPanelItem if it was previously disabled.
38569      */
38570     enable : function(){
38571         this.disabled = false;
38572         this.pnode.removeClass("disabled");
38573     },
38574
38575     /**
38576      * Sets the content for this TabPanelItem.
38577      * @param {String} content The content
38578      * @param {Boolean} loadScripts true to look for and load scripts
38579      */
38580     setContent : function(content, loadScripts){
38581         this.bodyEl.update(content, loadScripts);
38582     },
38583
38584     /**
38585      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38586      * @return {Roo.UpdateManager} The UpdateManager
38587      */
38588     getUpdateManager : function(){
38589         return this.bodyEl.getUpdateManager();
38590     },
38591
38592     /**
38593      * Set a URL to be used to load the content for this TabPanelItem.
38594      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38595      * @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)
38596      * @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)
38597      * @return {Roo.UpdateManager} The UpdateManager
38598      */
38599     setUrl : function(url, params, loadOnce){
38600         if(this.refreshDelegate){
38601             this.un('activate', this.refreshDelegate);
38602         }
38603         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38604         this.on("activate", this.refreshDelegate);
38605         return this.bodyEl.getUpdateManager();
38606     },
38607
38608     /** @private */
38609     _handleRefresh : function(url, params, loadOnce){
38610         if(!loadOnce || !this.loaded){
38611             var updater = this.bodyEl.getUpdateManager();
38612             updater.update(url, params, this._setLoaded.createDelegate(this));
38613         }
38614     },
38615
38616     /**
38617      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38618      *   Will fail silently if the setUrl method has not been called.
38619      *   This does not activate the panel, just updates its content.
38620      */
38621     refresh : function(){
38622         if(this.refreshDelegate){
38623            this.loaded = false;
38624            this.refreshDelegate();
38625         }
38626     },
38627
38628     /** @private */
38629     _setLoaded : function(){
38630         this.loaded = true;
38631     },
38632
38633     /** @private */
38634     closeClick : function(e){
38635         var o = {};
38636         e.stopEvent();
38637         this.fireEvent("beforeclose", this, o);
38638         if(o.cancel !== true){
38639             this.tabPanel.removeTab(this.id);
38640         }
38641     },
38642     /**
38643      * The text displayed in the tooltip for the close icon.
38644      * @type String
38645      */
38646     closeText : "Close this tab"
38647 });
38648 /**
38649 *    This script refer to:
38650 *    Title: International Telephone Input
38651 *    Author: Jack O'Connor
38652 *    Code version:  v12.1.12
38653 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38654 **/
38655
38656 Roo.bootstrap.PhoneInputData = function() {
38657     var d = [
38658       [
38659         "Afghanistan (‫افغانستان‬‎)",
38660         "af",
38661         "93"
38662       ],
38663       [
38664         "Albania (Shqipëri)",
38665         "al",
38666         "355"
38667       ],
38668       [
38669         "Algeria (‫الجزائر‬‎)",
38670         "dz",
38671         "213"
38672       ],
38673       [
38674         "American Samoa",
38675         "as",
38676         "1684"
38677       ],
38678       [
38679         "Andorra",
38680         "ad",
38681         "376"
38682       ],
38683       [
38684         "Angola",
38685         "ao",
38686         "244"
38687       ],
38688       [
38689         "Anguilla",
38690         "ai",
38691         "1264"
38692       ],
38693       [
38694         "Antigua and Barbuda",
38695         "ag",
38696         "1268"
38697       ],
38698       [
38699         "Argentina",
38700         "ar",
38701         "54"
38702       ],
38703       [
38704         "Armenia (Հայաստան)",
38705         "am",
38706         "374"
38707       ],
38708       [
38709         "Aruba",
38710         "aw",
38711         "297"
38712       ],
38713       [
38714         "Australia",
38715         "au",
38716         "61",
38717         0
38718       ],
38719       [
38720         "Austria (Österreich)",
38721         "at",
38722         "43"
38723       ],
38724       [
38725         "Azerbaijan (Azərbaycan)",
38726         "az",
38727         "994"
38728       ],
38729       [
38730         "Bahamas",
38731         "bs",
38732         "1242"
38733       ],
38734       [
38735         "Bahrain (‫البحرين‬‎)",
38736         "bh",
38737         "973"
38738       ],
38739       [
38740         "Bangladesh (বাংলাদেশ)",
38741         "bd",
38742         "880"
38743       ],
38744       [
38745         "Barbados",
38746         "bb",
38747         "1246"
38748       ],
38749       [
38750         "Belarus (Беларусь)",
38751         "by",
38752         "375"
38753       ],
38754       [
38755         "Belgium (België)",
38756         "be",
38757         "32"
38758       ],
38759       [
38760         "Belize",
38761         "bz",
38762         "501"
38763       ],
38764       [
38765         "Benin (Bénin)",
38766         "bj",
38767         "229"
38768       ],
38769       [
38770         "Bermuda",
38771         "bm",
38772         "1441"
38773       ],
38774       [
38775         "Bhutan (འབྲུག)",
38776         "bt",
38777         "975"
38778       ],
38779       [
38780         "Bolivia",
38781         "bo",
38782         "591"
38783       ],
38784       [
38785         "Bosnia and Herzegovina (Босна и Херцеговина)",
38786         "ba",
38787         "387"
38788       ],
38789       [
38790         "Botswana",
38791         "bw",
38792         "267"
38793       ],
38794       [
38795         "Brazil (Brasil)",
38796         "br",
38797         "55"
38798       ],
38799       [
38800         "British Indian Ocean Territory",
38801         "io",
38802         "246"
38803       ],
38804       [
38805         "British Virgin Islands",
38806         "vg",
38807         "1284"
38808       ],
38809       [
38810         "Brunei",
38811         "bn",
38812         "673"
38813       ],
38814       [
38815         "Bulgaria (България)",
38816         "bg",
38817         "359"
38818       ],
38819       [
38820         "Burkina Faso",
38821         "bf",
38822         "226"
38823       ],
38824       [
38825         "Burundi (Uburundi)",
38826         "bi",
38827         "257"
38828       ],
38829       [
38830         "Cambodia (កម្ពុជា)",
38831         "kh",
38832         "855"
38833       ],
38834       [
38835         "Cameroon (Cameroun)",
38836         "cm",
38837         "237"
38838       ],
38839       [
38840         "Canada",
38841         "ca",
38842         "1",
38843         1,
38844         ["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"]
38845       ],
38846       [
38847         "Cape Verde (Kabu Verdi)",
38848         "cv",
38849         "238"
38850       ],
38851       [
38852         "Caribbean Netherlands",
38853         "bq",
38854         "599",
38855         1
38856       ],
38857       [
38858         "Cayman Islands",
38859         "ky",
38860         "1345"
38861       ],
38862       [
38863         "Central African Republic (République centrafricaine)",
38864         "cf",
38865         "236"
38866       ],
38867       [
38868         "Chad (Tchad)",
38869         "td",
38870         "235"
38871       ],
38872       [
38873         "Chile",
38874         "cl",
38875         "56"
38876       ],
38877       [
38878         "China (中国)",
38879         "cn",
38880         "86"
38881       ],
38882       [
38883         "Christmas Island",
38884         "cx",
38885         "61",
38886         2
38887       ],
38888       [
38889         "Cocos (Keeling) Islands",
38890         "cc",
38891         "61",
38892         1
38893       ],
38894       [
38895         "Colombia",
38896         "co",
38897         "57"
38898       ],
38899       [
38900         "Comoros (‫جزر القمر‬‎)",
38901         "km",
38902         "269"
38903       ],
38904       [
38905         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38906         "cd",
38907         "243"
38908       ],
38909       [
38910         "Congo (Republic) (Congo-Brazzaville)",
38911         "cg",
38912         "242"
38913       ],
38914       [
38915         "Cook Islands",
38916         "ck",
38917         "682"
38918       ],
38919       [
38920         "Costa Rica",
38921         "cr",
38922         "506"
38923       ],
38924       [
38925         "Côte d’Ivoire",
38926         "ci",
38927         "225"
38928       ],
38929       [
38930         "Croatia (Hrvatska)",
38931         "hr",
38932         "385"
38933       ],
38934       [
38935         "Cuba",
38936         "cu",
38937         "53"
38938       ],
38939       [
38940         "Curaçao",
38941         "cw",
38942         "599",
38943         0
38944       ],
38945       [
38946         "Cyprus (Κύπρος)",
38947         "cy",
38948         "357"
38949       ],
38950       [
38951         "Czech Republic (Česká republika)",
38952         "cz",
38953         "420"
38954       ],
38955       [
38956         "Denmark (Danmark)",
38957         "dk",
38958         "45"
38959       ],
38960       [
38961         "Djibouti",
38962         "dj",
38963         "253"
38964       ],
38965       [
38966         "Dominica",
38967         "dm",
38968         "1767"
38969       ],
38970       [
38971         "Dominican Republic (República Dominicana)",
38972         "do",
38973         "1",
38974         2,
38975         ["809", "829", "849"]
38976       ],
38977       [
38978         "Ecuador",
38979         "ec",
38980         "593"
38981       ],
38982       [
38983         "Egypt (‫مصر‬‎)",
38984         "eg",
38985         "20"
38986       ],
38987       [
38988         "El Salvador",
38989         "sv",
38990         "503"
38991       ],
38992       [
38993         "Equatorial Guinea (Guinea Ecuatorial)",
38994         "gq",
38995         "240"
38996       ],
38997       [
38998         "Eritrea",
38999         "er",
39000         "291"
39001       ],
39002       [
39003         "Estonia (Eesti)",
39004         "ee",
39005         "372"
39006       ],
39007       [
39008         "Ethiopia",
39009         "et",
39010         "251"
39011       ],
39012       [
39013         "Falkland Islands (Islas Malvinas)",
39014         "fk",
39015         "500"
39016       ],
39017       [
39018         "Faroe Islands (Føroyar)",
39019         "fo",
39020         "298"
39021       ],
39022       [
39023         "Fiji",
39024         "fj",
39025         "679"
39026       ],
39027       [
39028         "Finland (Suomi)",
39029         "fi",
39030         "358",
39031         0
39032       ],
39033       [
39034         "France",
39035         "fr",
39036         "33"
39037       ],
39038       [
39039         "French Guiana (Guyane française)",
39040         "gf",
39041         "594"
39042       ],
39043       [
39044         "French Polynesia (Polynésie française)",
39045         "pf",
39046         "689"
39047       ],
39048       [
39049         "Gabon",
39050         "ga",
39051         "241"
39052       ],
39053       [
39054         "Gambia",
39055         "gm",
39056         "220"
39057       ],
39058       [
39059         "Georgia (საქართველო)",
39060         "ge",
39061         "995"
39062       ],
39063       [
39064         "Germany (Deutschland)",
39065         "de",
39066         "49"
39067       ],
39068       [
39069         "Ghana (Gaana)",
39070         "gh",
39071         "233"
39072       ],
39073       [
39074         "Gibraltar",
39075         "gi",
39076         "350"
39077       ],
39078       [
39079         "Greece (Ελλάδα)",
39080         "gr",
39081         "30"
39082       ],
39083       [
39084         "Greenland (Kalaallit Nunaat)",
39085         "gl",
39086         "299"
39087       ],
39088       [
39089         "Grenada",
39090         "gd",
39091         "1473"
39092       ],
39093       [
39094         "Guadeloupe",
39095         "gp",
39096         "590",
39097         0
39098       ],
39099       [
39100         "Guam",
39101         "gu",
39102         "1671"
39103       ],
39104       [
39105         "Guatemala",
39106         "gt",
39107         "502"
39108       ],
39109       [
39110         "Guernsey",
39111         "gg",
39112         "44",
39113         1
39114       ],
39115       [
39116         "Guinea (Guinée)",
39117         "gn",
39118         "224"
39119       ],
39120       [
39121         "Guinea-Bissau (Guiné Bissau)",
39122         "gw",
39123         "245"
39124       ],
39125       [
39126         "Guyana",
39127         "gy",
39128         "592"
39129       ],
39130       [
39131         "Haiti",
39132         "ht",
39133         "509"
39134       ],
39135       [
39136         "Honduras",
39137         "hn",
39138         "504"
39139       ],
39140       [
39141         "Hong Kong (香港)",
39142         "hk",
39143         "852"
39144       ],
39145       [
39146         "Hungary (Magyarország)",
39147         "hu",
39148         "36"
39149       ],
39150       [
39151         "Iceland (Ísland)",
39152         "is",
39153         "354"
39154       ],
39155       [
39156         "India (भारत)",
39157         "in",
39158         "91"
39159       ],
39160       [
39161         "Indonesia",
39162         "id",
39163         "62"
39164       ],
39165       [
39166         "Iran (‫ایران‬‎)",
39167         "ir",
39168         "98"
39169       ],
39170       [
39171         "Iraq (‫العراق‬‎)",
39172         "iq",
39173         "964"
39174       ],
39175       [
39176         "Ireland",
39177         "ie",
39178         "353"
39179       ],
39180       [
39181         "Isle of Man",
39182         "im",
39183         "44",
39184         2
39185       ],
39186       [
39187         "Israel (‫ישראל‬‎)",
39188         "il",
39189         "972"
39190       ],
39191       [
39192         "Italy (Italia)",
39193         "it",
39194         "39",
39195         0
39196       ],
39197       [
39198         "Jamaica",
39199         "jm",
39200         "1876"
39201       ],
39202       [
39203         "Japan (日本)",
39204         "jp",
39205         "81"
39206       ],
39207       [
39208         "Jersey",
39209         "je",
39210         "44",
39211         3
39212       ],
39213       [
39214         "Jordan (‫الأردن‬‎)",
39215         "jo",
39216         "962"
39217       ],
39218       [
39219         "Kazakhstan (Казахстан)",
39220         "kz",
39221         "7",
39222         1
39223       ],
39224       [
39225         "Kenya",
39226         "ke",
39227         "254"
39228       ],
39229       [
39230         "Kiribati",
39231         "ki",
39232         "686"
39233       ],
39234       [
39235         "Kosovo",
39236         "xk",
39237         "383"
39238       ],
39239       [
39240         "Kuwait (‫الكويت‬‎)",
39241         "kw",
39242         "965"
39243       ],
39244       [
39245         "Kyrgyzstan (Кыргызстан)",
39246         "kg",
39247         "996"
39248       ],
39249       [
39250         "Laos (ລາວ)",
39251         "la",
39252         "856"
39253       ],
39254       [
39255         "Latvia (Latvija)",
39256         "lv",
39257         "371"
39258       ],
39259       [
39260         "Lebanon (‫لبنان‬‎)",
39261         "lb",
39262         "961"
39263       ],
39264       [
39265         "Lesotho",
39266         "ls",
39267         "266"
39268       ],
39269       [
39270         "Liberia",
39271         "lr",
39272         "231"
39273       ],
39274       [
39275         "Libya (‫ليبيا‬‎)",
39276         "ly",
39277         "218"
39278       ],
39279       [
39280         "Liechtenstein",
39281         "li",
39282         "423"
39283       ],
39284       [
39285         "Lithuania (Lietuva)",
39286         "lt",
39287         "370"
39288       ],
39289       [
39290         "Luxembourg",
39291         "lu",
39292         "352"
39293       ],
39294       [
39295         "Macau (澳門)",
39296         "mo",
39297         "853"
39298       ],
39299       [
39300         "Macedonia (FYROM) (Македонија)",
39301         "mk",
39302         "389"
39303       ],
39304       [
39305         "Madagascar (Madagasikara)",
39306         "mg",
39307         "261"
39308       ],
39309       [
39310         "Malawi",
39311         "mw",
39312         "265"
39313       ],
39314       [
39315         "Malaysia",
39316         "my",
39317         "60"
39318       ],
39319       [
39320         "Maldives",
39321         "mv",
39322         "960"
39323       ],
39324       [
39325         "Mali",
39326         "ml",
39327         "223"
39328       ],
39329       [
39330         "Malta",
39331         "mt",
39332         "356"
39333       ],
39334       [
39335         "Marshall Islands",
39336         "mh",
39337         "692"
39338       ],
39339       [
39340         "Martinique",
39341         "mq",
39342         "596"
39343       ],
39344       [
39345         "Mauritania (‫موريتانيا‬‎)",
39346         "mr",
39347         "222"
39348       ],
39349       [
39350         "Mauritius (Moris)",
39351         "mu",
39352         "230"
39353       ],
39354       [
39355         "Mayotte",
39356         "yt",
39357         "262",
39358         1
39359       ],
39360       [
39361         "Mexico (México)",
39362         "mx",
39363         "52"
39364       ],
39365       [
39366         "Micronesia",
39367         "fm",
39368         "691"
39369       ],
39370       [
39371         "Moldova (Republica Moldova)",
39372         "md",
39373         "373"
39374       ],
39375       [
39376         "Monaco",
39377         "mc",
39378         "377"
39379       ],
39380       [
39381         "Mongolia (Монгол)",
39382         "mn",
39383         "976"
39384       ],
39385       [
39386         "Montenegro (Crna Gora)",
39387         "me",
39388         "382"
39389       ],
39390       [
39391         "Montserrat",
39392         "ms",
39393         "1664"
39394       ],
39395       [
39396         "Morocco (‫المغرب‬‎)",
39397         "ma",
39398         "212",
39399         0
39400       ],
39401       [
39402         "Mozambique (Moçambique)",
39403         "mz",
39404         "258"
39405       ],
39406       [
39407         "Myanmar (Burma) (မြန်မာ)",
39408         "mm",
39409         "95"
39410       ],
39411       [
39412         "Namibia (Namibië)",
39413         "na",
39414         "264"
39415       ],
39416       [
39417         "Nauru",
39418         "nr",
39419         "674"
39420       ],
39421       [
39422         "Nepal (नेपाल)",
39423         "np",
39424         "977"
39425       ],
39426       [
39427         "Netherlands (Nederland)",
39428         "nl",
39429         "31"
39430       ],
39431       [
39432         "New Caledonia (Nouvelle-Calédonie)",
39433         "nc",
39434         "687"
39435       ],
39436       [
39437         "New Zealand",
39438         "nz",
39439         "64"
39440       ],
39441       [
39442         "Nicaragua",
39443         "ni",
39444         "505"
39445       ],
39446       [
39447         "Niger (Nijar)",
39448         "ne",
39449         "227"
39450       ],
39451       [
39452         "Nigeria",
39453         "ng",
39454         "234"
39455       ],
39456       [
39457         "Niue",
39458         "nu",
39459         "683"
39460       ],
39461       [
39462         "Norfolk Island",
39463         "nf",
39464         "672"
39465       ],
39466       [
39467         "North Korea (조선 민주주의 인민 공화국)",
39468         "kp",
39469         "850"
39470       ],
39471       [
39472         "Northern Mariana Islands",
39473         "mp",
39474         "1670"
39475       ],
39476       [
39477         "Norway (Norge)",
39478         "no",
39479         "47",
39480         0
39481       ],
39482       [
39483         "Oman (‫عُمان‬‎)",
39484         "om",
39485         "968"
39486       ],
39487       [
39488         "Pakistan (‫پاکستان‬‎)",
39489         "pk",
39490         "92"
39491       ],
39492       [
39493         "Palau",
39494         "pw",
39495         "680"
39496       ],
39497       [
39498         "Palestine (‫فلسطين‬‎)",
39499         "ps",
39500         "970"
39501       ],
39502       [
39503         "Panama (Panamá)",
39504         "pa",
39505         "507"
39506       ],
39507       [
39508         "Papua New Guinea",
39509         "pg",
39510         "675"
39511       ],
39512       [
39513         "Paraguay",
39514         "py",
39515         "595"
39516       ],
39517       [
39518         "Peru (Perú)",
39519         "pe",
39520         "51"
39521       ],
39522       [
39523         "Philippines",
39524         "ph",
39525         "63"
39526       ],
39527       [
39528         "Poland (Polska)",
39529         "pl",
39530         "48"
39531       ],
39532       [
39533         "Portugal",
39534         "pt",
39535         "351"
39536       ],
39537       [
39538         "Puerto Rico",
39539         "pr",
39540         "1",
39541         3,
39542         ["787", "939"]
39543       ],
39544       [
39545         "Qatar (‫قطر‬‎)",
39546         "qa",
39547         "974"
39548       ],
39549       [
39550         "Réunion (La Réunion)",
39551         "re",
39552         "262",
39553         0
39554       ],
39555       [
39556         "Romania (România)",
39557         "ro",
39558         "40"
39559       ],
39560       [
39561         "Russia (Россия)",
39562         "ru",
39563         "7",
39564         0
39565       ],
39566       [
39567         "Rwanda",
39568         "rw",
39569         "250"
39570       ],
39571       [
39572         "Saint Barthélemy",
39573         "bl",
39574         "590",
39575         1
39576       ],
39577       [
39578         "Saint Helena",
39579         "sh",
39580         "290"
39581       ],
39582       [
39583         "Saint Kitts and Nevis",
39584         "kn",
39585         "1869"
39586       ],
39587       [
39588         "Saint Lucia",
39589         "lc",
39590         "1758"
39591       ],
39592       [
39593         "Saint Martin (Saint-Martin (partie française))",
39594         "mf",
39595         "590",
39596         2
39597       ],
39598       [
39599         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39600         "pm",
39601         "508"
39602       ],
39603       [
39604         "Saint Vincent and the Grenadines",
39605         "vc",
39606         "1784"
39607       ],
39608       [
39609         "Samoa",
39610         "ws",
39611         "685"
39612       ],
39613       [
39614         "San Marino",
39615         "sm",
39616         "378"
39617       ],
39618       [
39619         "São Tomé and Príncipe (São Tomé e Príncipe)",
39620         "st",
39621         "239"
39622       ],
39623       [
39624         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39625         "sa",
39626         "966"
39627       ],
39628       [
39629         "Senegal (Sénégal)",
39630         "sn",
39631         "221"
39632       ],
39633       [
39634         "Serbia (Србија)",
39635         "rs",
39636         "381"
39637       ],
39638       [
39639         "Seychelles",
39640         "sc",
39641         "248"
39642       ],
39643       [
39644         "Sierra Leone",
39645         "sl",
39646         "232"
39647       ],
39648       [
39649         "Singapore",
39650         "sg",
39651         "65"
39652       ],
39653       [
39654         "Sint Maarten",
39655         "sx",
39656         "1721"
39657       ],
39658       [
39659         "Slovakia (Slovensko)",
39660         "sk",
39661         "421"
39662       ],
39663       [
39664         "Slovenia (Slovenija)",
39665         "si",
39666         "386"
39667       ],
39668       [
39669         "Solomon Islands",
39670         "sb",
39671         "677"
39672       ],
39673       [
39674         "Somalia (Soomaaliya)",
39675         "so",
39676         "252"
39677       ],
39678       [
39679         "South Africa",
39680         "za",
39681         "27"
39682       ],
39683       [
39684         "South Korea (대한민국)",
39685         "kr",
39686         "82"
39687       ],
39688       [
39689         "South Sudan (‫جنوب السودان‬‎)",
39690         "ss",
39691         "211"
39692       ],
39693       [
39694         "Spain (España)",
39695         "es",
39696         "34"
39697       ],
39698       [
39699         "Sri Lanka (ශ්‍රී ලංකාව)",
39700         "lk",
39701         "94"
39702       ],
39703       [
39704         "Sudan (‫السودان‬‎)",
39705         "sd",
39706         "249"
39707       ],
39708       [
39709         "Suriname",
39710         "sr",
39711         "597"
39712       ],
39713       [
39714         "Svalbard and Jan Mayen",
39715         "sj",
39716         "47",
39717         1
39718       ],
39719       [
39720         "Swaziland",
39721         "sz",
39722         "268"
39723       ],
39724       [
39725         "Sweden (Sverige)",
39726         "se",
39727         "46"
39728       ],
39729       [
39730         "Switzerland (Schweiz)",
39731         "ch",
39732         "41"
39733       ],
39734       [
39735         "Syria (‫سوريا‬‎)",
39736         "sy",
39737         "963"
39738       ],
39739       [
39740         "Taiwan (台灣)",
39741         "tw",
39742         "886"
39743       ],
39744       [
39745         "Tajikistan",
39746         "tj",
39747         "992"
39748       ],
39749       [
39750         "Tanzania",
39751         "tz",
39752         "255"
39753       ],
39754       [
39755         "Thailand (ไทย)",
39756         "th",
39757         "66"
39758       ],
39759       [
39760         "Timor-Leste",
39761         "tl",
39762         "670"
39763       ],
39764       [
39765         "Togo",
39766         "tg",
39767         "228"
39768       ],
39769       [
39770         "Tokelau",
39771         "tk",
39772         "690"
39773       ],
39774       [
39775         "Tonga",
39776         "to",
39777         "676"
39778       ],
39779       [
39780         "Trinidad and Tobago",
39781         "tt",
39782         "1868"
39783       ],
39784       [
39785         "Tunisia (‫تونس‬‎)",
39786         "tn",
39787         "216"
39788       ],
39789       [
39790         "Turkey (Türkiye)",
39791         "tr",
39792         "90"
39793       ],
39794       [
39795         "Turkmenistan",
39796         "tm",
39797         "993"
39798       ],
39799       [
39800         "Turks and Caicos Islands",
39801         "tc",
39802         "1649"
39803       ],
39804       [
39805         "Tuvalu",
39806         "tv",
39807         "688"
39808       ],
39809       [
39810         "U.S. Virgin Islands",
39811         "vi",
39812         "1340"
39813       ],
39814       [
39815         "Uganda",
39816         "ug",
39817         "256"
39818       ],
39819       [
39820         "Ukraine (Україна)",
39821         "ua",
39822         "380"
39823       ],
39824       [
39825         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39826         "ae",
39827         "971"
39828       ],
39829       [
39830         "United Kingdom",
39831         "gb",
39832         "44",
39833         0
39834       ],
39835       [
39836         "United States",
39837         "us",
39838         "1",
39839         0
39840       ],
39841       [
39842         "Uruguay",
39843         "uy",
39844         "598"
39845       ],
39846       [
39847         "Uzbekistan (Oʻzbekiston)",
39848         "uz",
39849         "998"
39850       ],
39851       [
39852         "Vanuatu",
39853         "vu",
39854         "678"
39855       ],
39856       [
39857         "Vatican City (Città del Vaticano)",
39858         "va",
39859         "39",
39860         1
39861       ],
39862       [
39863         "Venezuela",
39864         "ve",
39865         "58"
39866       ],
39867       [
39868         "Vietnam (Việt Nam)",
39869         "vn",
39870         "84"
39871       ],
39872       [
39873         "Wallis and Futuna (Wallis-et-Futuna)",
39874         "wf",
39875         "681"
39876       ],
39877       [
39878         "Western Sahara (‫الصحراء الغربية‬‎)",
39879         "eh",
39880         "212",
39881         1
39882       ],
39883       [
39884         "Yemen (‫اليمن‬‎)",
39885         "ye",
39886         "967"
39887       ],
39888       [
39889         "Zambia",
39890         "zm",
39891         "260"
39892       ],
39893       [
39894         "Zimbabwe",
39895         "zw",
39896         "263"
39897       ],
39898       [
39899         "Åland Islands",
39900         "ax",
39901         "358",
39902         1
39903       ]
39904   ];
39905   
39906   return d;
39907 }/**
39908 *    This script refer to:
39909 *    Title: International Telephone Input
39910 *    Author: Jack O'Connor
39911 *    Code version:  v12.1.12
39912 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39913 **/
39914
39915 /**
39916  * @class Roo.bootstrap.PhoneInput
39917  * @extends Roo.bootstrap.TriggerField
39918  * An input with International dial-code selection
39919  
39920  * @cfg {String} defaultDialCode default '+852'
39921  * @cfg {Array} preferedCountries default []
39922   
39923  * @constructor
39924  * Create a new PhoneInput.
39925  * @param {Object} config Configuration options
39926  */
39927
39928 Roo.bootstrap.PhoneInput = function(config) {
39929     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39930 };
39931
39932 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39933         
39934         listWidth: undefined,
39935         
39936         selectedClass: 'active',
39937         
39938         invalidClass : "has-warning",
39939         
39940         validClass: 'has-success',
39941         
39942         allowed: '0123456789',
39943         
39944         max_length: 15,
39945         
39946         /**
39947          * @cfg {String} defaultDialCode The default dial code when initializing the input
39948          */
39949         defaultDialCode: '+852',
39950         
39951         /**
39952          * @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
39953          */
39954         preferedCountries: false,
39955         
39956         getAutoCreate : function()
39957         {
39958             var data = Roo.bootstrap.PhoneInputData();
39959             var align = this.labelAlign || this.parentLabelAlign();
39960             var id = Roo.id();
39961             
39962             this.allCountries = [];
39963             this.dialCodeMapping = [];
39964             
39965             for (var i = 0; i < data.length; i++) {
39966               var c = data[i];
39967               this.allCountries[i] = {
39968                 name: c[0],
39969                 iso2: c[1],
39970                 dialCode: c[2],
39971                 priority: c[3] || 0,
39972                 areaCodes: c[4] || null
39973               };
39974               this.dialCodeMapping[c[2]] = {
39975                   name: c[0],
39976                   iso2: c[1],
39977                   priority: c[3] || 0,
39978                   areaCodes: c[4] || null
39979               };
39980             }
39981             
39982             var cfg = {
39983                 cls: 'form-group',
39984                 cn: []
39985             };
39986             
39987             var input =  {
39988                 tag: 'input',
39989                 id : id,
39990                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
39991                 maxlength: this.max_length,
39992                 cls : 'form-control tel-input',
39993                 autocomplete: 'new-password'
39994             };
39995             
39996             var hiddenInput = {
39997                 tag: 'input',
39998                 type: 'hidden',
39999                 cls: 'hidden-tel-input'
40000             };
40001             
40002             if (this.name) {
40003                 hiddenInput.name = this.name;
40004             }
40005             
40006             if (this.disabled) {
40007                 input.disabled = true;
40008             }
40009             
40010             var flag_container = {
40011                 tag: 'div',
40012                 cls: 'flag-box',
40013                 cn: [
40014                     {
40015                         tag: 'div',
40016                         cls: 'flag'
40017                     },
40018                     {
40019                         tag: 'div',
40020                         cls: 'caret'
40021                     }
40022                 ]
40023             };
40024             
40025             var box = {
40026                 tag: 'div',
40027                 cls: this.hasFeedback ? 'has-feedback' : '',
40028                 cn: [
40029                     hiddenInput,
40030                     input,
40031                     {
40032                         tag: 'input',
40033                         cls: 'dial-code-holder',
40034                         disabled: true
40035                     }
40036                 ]
40037             };
40038             
40039             var container = {
40040                 cls: 'roo-select2-container input-group',
40041                 cn: [
40042                     flag_container,
40043                     box
40044                 ]
40045             };
40046             
40047             if (this.fieldLabel.length) {
40048                 var indicator = {
40049                     tag: 'i',
40050                     tooltip: 'This field is required'
40051                 };
40052                 
40053                 var label = {
40054                     tag: 'label',
40055                     'for':  id,
40056                     cls: 'control-label',
40057                     cn: []
40058                 };
40059                 
40060                 var label_text = {
40061                     tag: 'span',
40062                     html: this.fieldLabel
40063                 };
40064                 
40065                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40066                 label.cn = [
40067                     indicator,
40068                     label_text
40069                 ];
40070                 
40071                 if(this.indicatorpos == 'right') {
40072                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40073                     label.cn = [
40074                         label_text,
40075                         indicator
40076                     ];
40077                 }
40078                 
40079                 if(align == 'left') {
40080                     container = {
40081                         tag: 'div',
40082                         cn: [
40083                             container
40084                         ]
40085                     };
40086                     
40087                     if(this.labelWidth > 12){
40088                         label.style = "width: " + this.labelWidth + 'px';
40089                     }
40090                     if(this.labelWidth < 13 && this.labelmd == 0){
40091                         this.labelmd = this.labelWidth;
40092                     }
40093                     if(this.labellg > 0){
40094                         label.cls += ' col-lg-' + this.labellg;
40095                         input.cls += ' col-lg-' + (12 - this.labellg);
40096                     }
40097                     if(this.labelmd > 0){
40098                         label.cls += ' col-md-' + this.labelmd;
40099                         container.cls += ' col-md-' + (12 - this.labelmd);
40100                     }
40101                     if(this.labelsm > 0){
40102                         label.cls += ' col-sm-' + this.labelsm;
40103                         container.cls += ' col-sm-' + (12 - this.labelsm);
40104                     }
40105                     if(this.labelxs > 0){
40106                         label.cls += ' col-xs-' + this.labelxs;
40107                         container.cls += ' col-xs-' + (12 - this.labelxs);
40108                     }
40109                 }
40110             }
40111             
40112             cfg.cn = [
40113                 label,
40114                 container
40115             ];
40116             
40117             var settings = this;
40118             
40119             ['xs','sm','md','lg'].map(function(size){
40120                 if (settings[size]) {
40121                     cfg.cls += ' col-' + size + '-' + settings[size];
40122                 }
40123             });
40124             
40125             this.store = new Roo.data.Store({
40126                 proxy : new Roo.data.MemoryProxy({}),
40127                 reader : new Roo.data.JsonReader({
40128                     fields : [
40129                         {
40130                             'name' : 'name',
40131                             'type' : 'string'
40132                         },
40133                         {
40134                             'name' : 'iso2',
40135                             'type' : 'string'
40136                         },
40137                         {
40138                             'name' : 'dialCode',
40139                             'type' : 'string'
40140                         },
40141                         {
40142                             'name' : 'priority',
40143                             'type' : 'string'
40144                         },
40145                         {
40146                             'name' : 'areaCodes',
40147                             'type' : 'string'
40148                         }
40149                     ]
40150                 })
40151             });
40152             
40153             if(!this.preferedCountries) {
40154                 this.preferedCountries = [
40155                     'hk',
40156                     'gb',
40157                     'us'
40158                 ];
40159             }
40160             
40161             var p = this.preferedCountries.reverse();
40162             
40163             if(p) {
40164                 for (var i = 0; i < p.length; i++) {
40165                     for (var j = 0; j < this.allCountries.length; j++) {
40166                         if(this.allCountries[j].iso2 == p[i]) {
40167                             var t = this.allCountries[j];
40168                             this.allCountries.splice(j,1);
40169                             this.allCountries.unshift(t);
40170                         }
40171                     } 
40172                 }
40173             }
40174             
40175             this.store.proxy.data = {
40176                 success: true,
40177                 data: this.allCountries
40178             };
40179             
40180             return cfg;
40181         },
40182         
40183         initEvents : function()
40184         {
40185             this.createList();
40186             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40187             
40188             this.indicator = this.indicatorEl();
40189             this.flag = this.flagEl();
40190             this.dialCodeHolder = this.dialCodeHolderEl();
40191             
40192             this.trigger = this.el.select('div.flag-box',true).first();
40193             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40194             
40195             var _this = this;
40196             
40197             (function(){
40198                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40199                 _this.list.setWidth(lw);
40200             }).defer(100);
40201             
40202             this.list.on('mouseover', this.onViewOver, this);
40203             this.list.on('mousemove', this.onViewMove, this);
40204             this.inputEl().on("keyup", this.onKeyUp, this);
40205             this.inputEl().on("keypress", this.onKeyPress, this);
40206             
40207             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40208
40209             this.view = new Roo.View(this.list, this.tpl, {
40210                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40211             });
40212             
40213             this.view.on('click', this.onViewClick, this);
40214             this.setValue(this.defaultDialCode);
40215         },
40216         
40217         onTriggerClick : function(e)
40218         {
40219             Roo.log('trigger click');
40220             if(this.disabled){
40221                 return;
40222             }
40223             
40224             if(this.isExpanded()){
40225                 this.collapse();
40226                 this.hasFocus = false;
40227             }else {
40228                 this.store.load({});
40229                 this.hasFocus = true;
40230                 this.expand();
40231             }
40232         },
40233         
40234         isExpanded : function()
40235         {
40236             return this.list.isVisible();
40237         },
40238         
40239         collapse : function()
40240         {
40241             if(!this.isExpanded()){
40242                 return;
40243             }
40244             this.list.hide();
40245             Roo.get(document).un('mousedown', this.collapseIf, this);
40246             Roo.get(document).un('mousewheel', this.collapseIf, this);
40247             this.fireEvent('collapse', this);
40248             this.validate();
40249         },
40250         
40251         expand : function()
40252         {
40253             Roo.log('expand');
40254
40255             if(this.isExpanded() || !this.hasFocus){
40256                 return;
40257             }
40258             
40259             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40260             this.list.setWidth(lw);
40261             
40262             this.list.show();
40263             this.restrictHeight();
40264             
40265             Roo.get(document).on('mousedown', this.collapseIf, this);
40266             Roo.get(document).on('mousewheel', this.collapseIf, this);
40267             
40268             this.fireEvent('expand', this);
40269         },
40270         
40271         restrictHeight : function()
40272         {
40273             this.list.alignTo(this.inputEl(), this.listAlign);
40274             this.list.alignTo(this.inputEl(), this.listAlign);
40275         },
40276         
40277         onViewOver : function(e, t)
40278         {
40279             if(this.inKeyMode){
40280                 return;
40281             }
40282             var item = this.view.findItemFromChild(t);
40283             
40284             if(item){
40285                 var index = this.view.indexOf(item);
40286                 this.select(index, false);
40287             }
40288         },
40289
40290         // private
40291         onViewClick : function(view, doFocus, el, e)
40292         {
40293             var index = this.view.getSelectedIndexes()[0];
40294             
40295             var r = this.store.getAt(index);
40296             
40297             if(r){
40298                 this.onSelect(r, index);
40299             }
40300             if(doFocus !== false && !this.blockFocus){
40301                 this.inputEl().focus();
40302             }
40303         },
40304         
40305         onViewMove : function(e, t)
40306         {
40307             this.inKeyMode = false;
40308         },
40309         
40310         select : function(index, scrollIntoView)
40311         {
40312             this.selectedIndex = index;
40313             this.view.select(index);
40314             if(scrollIntoView !== false){
40315                 var el = this.view.getNode(index);
40316                 if(el){
40317                     this.list.scrollChildIntoView(el, false);
40318                 }
40319             }
40320         },
40321         
40322         createList : function()
40323         {
40324             this.list = Roo.get(document.body).createChild({
40325                 tag: 'ul',
40326                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40327                 style: 'display:none'
40328             });
40329             
40330             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40331         },
40332         
40333         collapseIf : function(e)
40334         {
40335             var in_combo  = e.within(this.el);
40336             var in_list =  e.within(this.list);
40337             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40338             
40339             if (in_combo || in_list || is_list) {
40340                 return;
40341             }
40342             this.collapse();
40343         },
40344         
40345         onSelect : function(record, index)
40346         {
40347             if(this.fireEvent('beforeselect', this, record, index) !== false){
40348                 
40349                 this.setFlagClass(record.data.iso2);
40350                 this.setDialCode(record.data.dialCode);
40351                 this.hasFocus = false;
40352                 this.collapse();
40353                 this.fireEvent('select', this, record, index);
40354             }
40355         },
40356         
40357         flagEl : function()
40358         {
40359             var flag = this.el.select('div.flag',true).first();
40360             if(!flag){
40361                 return false;
40362             }
40363             return flag;
40364         },
40365         
40366         dialCodeHolderEl : function()
40367         {
40368             var d = this.el.select('input.dial-code-holder',true).first();
40369             if(!d){
40370                 return false;
40371             }
40372             return d;
40373         },
40374         
40375         setDialCode : function(v)
40376         {
40377             this.dialCodeHolder.dom.value = '+'+v;
40378         },
40379         
40380         setFlagClass : function(n)
40381         {
40382             this.flag.dom.className = 'flag '+n;
40383         },
40384         
40385         getValue : function()
40386         {
40387             var v = this.inputEl().getValue();
40388             if(this.dialCodeHolder) {
40389                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40390             }
40391             return v;
40392         },
40393         
40394         setValue : function(v)
40395         {
40396             var d = this.getDialCode(v);
40397             
40398             //invalid dial code
40399             if(v.length == 0 || !d || d.length == 0) {
40400                 if(this.rendered){
40401                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40402                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40403                 }
40404                 return;
40405             }
40406             
40407             //valid dial code
40408             this.setFlagClass(this.dialCodeMapping[d].iso2);
40409             this.setDialCode(d);
40410             this.inputEl().dom.value = v.replace('+'+d,'');
40411             this.hiddenEl().dom.value = this.getValue();
40412             
40413             this.validate();
40414         },
40415         
40416         getDialCode : function(v)
40417         {
40418             v = v ||  '';
40419             
40420             if (v.length == 0) {
40421                 return this.dialCodeHolder.dom.value;
40422             }
40423             
40424             var dialCode = "";
40425             if (v.charAt(0) != "+") {
40426                 return false;
40427             }
40428             var numericChars = "";
40429             for (var i = 1; i < v.length; i++) {
40430               var c = v.charAt(i);
40431               if (!isNaN(c)) {
40432                 numericChars += c;
40433                 if (this.dialCodeMapping[numericChars]) {
40434                   dialCode = v.substr(1, i);
40435                 }
40436                 if (numericChars.length == 4) {
40437                   break;
40438                 }
40439               }
40440             }
40441             return dialCode;
40442         },
40443         
40444         reset : function()
40445         {
40446             this.setValue(this.defaultDialCode);
40447             this.validate();
40448         },
40449         
40450         hiddenEl : function()
40451         {
40452             return this.el.select('input.hidden-tel-input',true).first();
40453         },
40454         
40455         // after setting val
40456         onKeyUp : function(e){
40457             this.setValue(this.getValue());
40458         },
40459         
40460         onKeyPress : function(e){
40461             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40462                 e.stopEvent();
40463             }
40464         }
40465         
40466 });
40467 /**
40468  * @class Roo.bootstrap.MoneyField
40469  * @extends Roo.bootstrap.ComboBox
40470  * Bootstrap MoneyField class
40471  * 
40472  * @constructor
40473  * Create a new MoneyField.
40474  * @param {Object} config Configuration options
40475  */
40476
40477 Roo.bootstrap.MoneyField = function(config) {
40478     
40479     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40480     
40481 };
40482
40483 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40484     
40485     /**
40486      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40487      */
40488     allowDecimals : true,
40489     /**
40490      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40491      */
40492     decimalSeparator : ".",
40493     /**
40494      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40495      */
40496     decimalPrecision : 0,
40497     /**
40498      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40499      */
40500     allowNegative : true,
40501     /**
40502      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40503      */
40504     allowZero: true,
40505     /**
40506      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40507      */
40508     minValue : Number.NEGATIVE_INFINITY,
40509     /**
40510      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40511      */
40512     maxValue : Number.MAX_VALUE,
40513     /**
40514      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40515      */
40516     minText : "The minimum value for this field is {0}",
40517     /**
40518      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40519      */
40520     maxText : "The maximum value for this field is {0}",
40521     /**
40522      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40523      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40524      */
40525     nanText : "{0} is not a valid number",
40526     /**
40527      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40528      */
40529     castInt : true,
40530     /**
40531      * @cfg {String} defaults currency of the MoneyField
40532      * value should be in lkey
40533      */
40534     defaultCurrency : false,
40535     /**
40536      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40537      */
40538     thousandsDelimiter : false,
40539     /**
40540      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40541      */
40542     max_length: false,
40543     
40544     inputlg : 9,
40545     inputmd : 9,
40546     inputsm : 9,
40547     inputxs : 6,
40548     
40549     store : false,
40550     
40551     getAutoCreate : function()
40552     {
40553         var align = this.labelAlign || this.parentLabelAlign();
40554         
40555         var id = Roo.id();
40556
40557         var cfg = {
40558             cls: 'form-group',
40559             cn: []
40560         };
40561
40562         var input =  {
40563             tag: 'input',
40564             id : id,
40565             cls : 'form-control roo-money-amount-input',
40566             autocomplete: 'new-password'
40567         };
40568         
40569         var hiddenInput = {
40570             tag: 'input',
40571             type: 'hidden',
40572             id: Roo.id(),
40573             cls: 'hidden-number-input'
40574         };
40575         
40576         if(this.max_length) {
40577             input.maxlength = this.max_length; 
40578         }
40579         
40580         if (this.name) {
40581             hiddenInput.name = this.name;
40582         }
40583
40584         if (this.disabled) {
40585             input.disabled = true;
40586         }
40587
40588         var clg = 12 - this.inputlg;
40589         var cmd = 12 - this.inputmd;
40590         var csm = 12 - this.inputsm;
40591         var cxs = 12 - this.inputxs;
40592         
40593         var container = {
40594             tag : 'div',
40595             cls : 'row roo-money-field',
40596             cn : [
40597                 {
40598                     tag : 'div',
40599                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40600                     cn : [
40601                         {
40602                             tag : 'div',
40603                             cls: 'roo-select2-container input-group',
40604                             cn: [
40605                                 {
40606                                     tag : 'input',
40607                                     cls : 'form-control roo-money-currency-input',
40608                                     autocomplete: 'new-password',
40609                                     readOnly : 1,
40610                                     name : this.currencyName
40611                                 },
40612                                 {
40613                                     tag :'span',
40614                                     cls : 'input-group-addon',
40615                                     cn : [
40616                                         {
40617                                             tag: 'span',
40618                                             cls: 'caret'
40619                                         }
40620                                     ]
40621                                 }
40622                             ]
40623                         }
40624                     ]
40625                 },
40626                 {
40627                     tag : 'div',
40628                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40629                     cn : [
40630                         {
40631                             tag: 'div',
40632                             cls: this.hasFeedback ? 'has-feedback' : '',
40633                             cn: [
40634                                 input
40635                             ]
40636                         }
40637                     ]
40638                 }
40639             ]
40640             
40641         };
40642         
40643         if (this.fieldLabel.length) {
40644             var indicator = {
40645                 tag: 'i',
40646                 tooltip: 'This field is required'
40647             };
40648
40649             var label = {
40650                 tag: 'label',
40651                 'for':  id,
40652                 cls: 'control-label',
40653                 cn: []
40654             };
40655
40656             var label_text = {
40657                 tag: 'span',
40658                 html: this.fieldLabel
40659             };
40660
40661             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40662             label.cn = [
40663                 indicator,
40664                 label_text
40665             ];
40666
40667             if(this.indicatorpos == 'right') {
40668                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40669                 label.cn = [
40670                     label_text,
40671                     indicator
40672                 ];
40673             }
40674
40675             if(align == 'left') {
40676                 container = {
40677                     tag: 'div',
40678                     cn: [
40679                         container
40680                     ]
40681                 };
40682
40683                 if(this.labelWidth > 12){
40684                     label.style = "width: " + this.labelWidth + 'px';
40685                 }
40686                 if(this.labelWidth < 13 && this.labelmd == 0){
40687                     this.labelmd = this.labelWidth;
40688                 }
40689                 if(this.labellg > 0){
40690                     label.cls += ' col-lg-' + this.labellg;
40691                     input.cls += ' col-lg-' + (12 - this.labellg);
40692                 }
40693                 if(this.labelmd > 0){
40694                     label.cls += ' col-md-' + this.labelmd;
40695                     container.cls += ' col-md-' + (12 - this.labelmd);
40696                 }
40697                 if(this.labelsm > 0){
40698                     label.cls += ' col-sm-' + this.labelsm;
40699                     container.cls += ' col-sm-' + (12 - this.labelsm);
40700                 }
40701                 if(this.labelxs > 0){
40702                     label.cls += ' col-xs-' + this.labelxs;
40703                     container.cls += ' col-xs-' + (12 - this.labelxs);
40704                 }
40705             }
40706         }
40707
40708         cfg.cn = [
40709             label,
40710             container,
40711             hiddenInput
40712         ];
40713         
40714         var settings = this;
40715
40716         ['xs','sm','md','lg'].map(function(size){
40717             if (settings[size]) {
40718                 cfg.cls += ' col-' + size + '-' + settings[size];
40719             }
40720         });
40721         
40722         return cfg;
40723     },
40724     
40725     initEvents : function()
40726     {
40727         this.indicator = this.indicatorEl();
40728         
40729         this.initCurrencyEvent();
40730         
40731         this.initNumberEvent();
40732     },
40733     
40734     initCurrencyEvent : function()
40735     {
40736         if (!this.store) {
40737             throw "can not find store for combo";
40738         }
40739         
40740         this.store = Roo.factory(this.store, Roo.data);
40741         this.store.parent = this;
40742         
40743         this.createList();
40744         
40745         this.triggerEl = this.el.select('.input-group-addon', true).first();
40746         
40747         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40748         
40749         var _this = this;
40750         
40751         (function(){
40752             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40753             _this.list.setWidth(lw);
40754         }).defer(100);
40755         
40756         this.list.on('mouseover', this.onViewOver, this);
40757         this.list.on('mousemove', this.onViewMove, this);
40758         this.list.on('scroll', this.onViewScroll, this);
40759         
40760         if(!this.tpl){
40761             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40762         }
40763         
40764         this.view = new Roo.View(this.list, this.tpl, {
40765             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40766         });
40767         
40768         this.view.on('click', this.onViewClick, this);
40769         
40770         this.store.on('beforeload', this.onBeforeLoad, this);
40771         this.store.on('load', this.onLoad, this);
40772         this.store.on('loadexception', this.onLoadException, this);
40773         
40774         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40775             "up" : function(e){
40776                 this.inKeyMode = true;
40777                 this.selectPrev();
40778             },
40779
40780             "down" : function(e){
40781                 if(!this.isExpanded()){
40782                     this.onTriggerClick();
40783                 }else{
40784                     this.inKeyMode = true;
40785                     this.selectNext();
40786                 }
40787             },
40788
40789             "enter" : function(e){
40790                 this.collapse();
40791                 
40792                 if(this.fireEvent("specialkey", this, e)){
40793                     this.onViewClick(false);
40794                 }
40795                 
40796                 return true;
40797             },
40798
40799             "esc" : function(e){
40800                 this.collapse();
40801             },
40802
40803             "tab" : function(e){
40804                 this.collapse();
40805                 
40806                 if(this.fireEvent("specialkey", this, e)){
40807                     this.onViewClick(false);
40808                 }
40809                 
40810                 return true;
40811             },
40812
40813             scope : this,
40814
40815             doRelay : function(foo, bar, hname){
40816                 if(hname == 'down' || this.scope.isExpanded()){
40817                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40818                 }
40819                 return true;
40820             },
40821
40822             forceKeyDown: true
40823         });
40824         
40825         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40826         
40827     },
40828     
40829     initNumberEvent : function(e)
40830     {
40831         this.inputEl().on("keydown" , this.fireKey,  this);
40832         this.inputEl().on("focus", this.onFocus,  this);
40833         this.inputEl().on("blur", this.onBlur,  this);
40834         
40835         this.inputEl().relayEvent('keyup', this);
40836         
40837         if(this.indicator){
40838             this.indicator.addClass('invisible');
40839         }
40840  
40841         this.originalValue = this.getValue();
40842         
40843         if(this.validationEvent == 'keyup'){
40844             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40845             this.inputEl().on('keyup', this.filterValidation, this);
40846         }
40847         else if(this.validationEvent !== false){
40848             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40849         }
40850         
40851         if(this.selectOnFocus){
40852             this.on("focus", this.preFocus, this);
40853             
40854         }
40855         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40856             this.inputEl().on("keypress", this.filterKeys, this);
40857         } else {
40858             this.inputEl().relayEvent('keypress', this);
40859         }
40860         
40861         var allowed = "0123456789";
40862         
40863         if(this.allowDecimals){
40864             allowed += this.decimalSeparator;
40865         }
40866         
40867         if(this.allowNegative){
40868             allowed += "-";
40869         }
40870         
40871         if(this.thousandsDelimiter) {
40872             allowed += ",";
40873         }
40874         
40875         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40876         
40877         var keyPress = function(e){
40878             
40879             var k = e.getKey();
40880             
40881             var c = e.getCharCode();
40882             
40883             if(
40884                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40885                     allowed.indexOf(String.fromCharCode(c)) === -1
40886             ){
40887                 e.stopEvent();
40888                 return;
40889             }
40890             
40891             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40892                 return;
40893             }
40894             
40895             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40896                 e.stopEvent();
40897             }
40898         };
40899         
40900         this.inputEl().on("keypress", keyPress, this);
40901         
40902     },
40903     
40904     onTriggerClick : function(e)
40905     {   
40906         if(this.disabled){
40907             return;
40908         }
40909         
40910         this.page = 0;
40911         this.loadNext = false;
40912         
40913         if(this.isExpanded()){
40914             this.collapse();
40915             return;
40916         }
40917         
40918         this.hasFocus = true;
40919         
40920         if(this.triggerAction == 'all') {
40921             this.doQuery(this.allQuery, true);
40922             return;
40923         }
40924         
40925         this.doQuery(this.getRawValue());
40926     },
40927     
40928     getCurrency : function()
40929     {   
40930         var v = this.currencyEl().getValue();
40931         
40932         return v;
40933     },
40934     
40935     restrictHeight : function()
40936     {
40937         this.list.alignTo(this.currencyEl(), this.listAlign);
40938         this.list.alignTo(this.currencyEl(), this.listAlign);
40939     },
40940     
40941     onViewClick : function(view, doFocus, el, e)
40942     {
40943         var index = this.view.getSelectedIndexes()[0];
40944         
40945         var r = this.store.getAt(index);
40946         
40947         if(r){
40948             this.onSelect(r, index);
40949         }
40950     },
40951     
40952     onSelect : function(record, index){
40953         
40954         if(this.fireEvent('beforeselect', this, record, index) !== false){
40955         
40956             this.setFromCurrencyData(index > -1 ? record.data : false);
40957             
40958             this.collapse();
40959             
40960             this.fireEvent('select', this, record, index);
40961         }
40962     },
40963     
40964     setFromCurrencyData : function(o)
40965     {
40966         var currency = '';
40967         
40968         this.lastCurrency = o;
40969         
40970         if (this.currencyField) {
40971             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40972         } else {
40973             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40974         }
40975         
40976         this.lastSelectionText = currency;
40977         
40978         //setting default currency
40979         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40980             this.setCurrency(this.defaultCurrency);
40981             return;
40982         }
40983         
40984         this.setCurrency(currency);
40985     },
40986     
40987     setFromData : function(o)
40988     {
40989         var c = {};
40990         
40991         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40992         
40993         this.setFromCurrencyData(c);
40994         
40995         var value = '';
40996         
40997         if (this.name) {
40998             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40999         } else {
41000             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41001         }
41002         
41003         this.setValue(value);
41004         
41005     },
41006     
41007     setCurrency : function(v)
41008     {   
41009         this.currencyValue = v;
41010         
41011         if(this.rendered){
41012             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41013             this.validate();
41014         }
41015     },
41016     
41017     setValue : function(v)
41018     {
41019         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41020         
41021         this.value = v;
41022         
41023         if(this.rendered){
41024             
41025             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41026             
41027             this.inputEl().dom.value = (v == '') ? '' :
41028                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41029             
41030             if(!this.allowZero && v === '0') {
41031                 this.hiddenEl().dom.value = '';
41032                 this.inputEl().dom.value = '';
41033             }
41034             
41035             this.validate();
41036         }
41037     },
41038     
41039     getRawValue : function()
41040     {
41041         var v = this.inputEl().getValue();
41042         
41043         return v;
41044     },
41045     
41046     getValue : function()
41047     {
41048         return this.fixPrecision(this.parseValue(this.getRawValue()));
41049     },
41050     
41051     parseValue : function(value)
41052     {
41053         if(this.thousandsDelimiter) {
41054             value += "";
41055             r = new RegExp(",", "g");
41056             value = value.replace(r, "");
41057         }
41058         
41059         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41060         return isNaN(value) ? '' : value;
41061         
41062     },
41063     
41064     fixPrecision : function(value)
41065     {
41066         if(this.thousandsDelimiter) {
41067             value += "";
41068             r = new RegExp(",", "g");
41069             value = value.replace(r, "");
41070         }
41071         
41072         var nan = isNaN(value);
41073         
41074         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41075             return nan ? '' : value;
41076         }
41077         return parseFloat(value).toFixed(this.decimalPrecision);
41078     },
41079     
41080     decimalPrecisionFcn : function(v)
41081     {
41082         return Math.floor(v);
41083     },
41084     
41085     validateValue : function(value)
41086     {
41087         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41088             return false;
41089         }
41090         
41091         var num = this.parseValue(value);
41092         
41093         if(isNaN(num)){
41094             this.markInvalid(String.format(this.nanText, value));
41095             return false;
41096         }
41097         
41098         if(num < this.minValue){
41099             this.markInvalid(String.format(this.minText, this.minValue));
41100             return false;
41101         }
41102         
41103         if(num > this.maxValue){
41104             this.markInvalid(String.format(this.maxText, this.maxValue));
41105             return false;
41106         }
41107         
41108         return true;
41109     },
41110     
41111     validate : function()
41112     {
41113         if(this.disabled || this.allowBlank){
41114             this.markValid();
41115             return true;
41116         }
41117         
41118         var currency = this.getCurrency();
41119         
41120         if(this.validateValue(this.getRawValue()) && currency.length){
41121             this.markValid();
41122             return true;
41123         }
41124         
41125         this.markInvalid();
41126         return false;
41127     },
41128     
41129     getName: function()
41130     {
41131         return this.name;
41132     },
41133     
41134     beforeBlur : function()
41135     {
41136         if(!this.castInt){
41137             return;
41138         }
41139         
41140         var v = this.parseValue(this.getRawValue());
41141         
41142         if(v || v == 0){
41143             this.setValue(v);
41144         }
41145     },
41146     
41147     onBlur : function()
41148     {
41149         this.beforeBlur();
41150         
41151         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41152             //this.el.removeClass(this.focusClass);
41153         }
41154         
41155         this.hasFocus = false;
41156         
41157         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41158             this.validate();
41159         }
41160         
41161         var v = this.getValue();
41162         
41163         if(String(v) !== String(this.startValue)){
41164             this.fireEvent('change', this, v, this.startValue);
41165         }
41166         
41167         this.fireEvent("blur", this);
41168     },
41169     
41170     inputEl : function()
41171     {
41172         return this.el.select('.roo-money-amount-input', true).first();
41173     },
41174     
41175     currencyEl : function()
41176     {
41177         return this.el.select('.roo-money-currency-input', true).first();
41178     },
41179     
41180     hiddenEl : function()
41181     {
41182         return this.el.select('input.hidden-number-input',true).first();
41183     }
41184     
41185 });